"""
Impact Analysis - Geriye BFS ile etki hesabı

Bir fonksiyonu değiştirirsen kaç düğüm etkilenir?
Geriye doğru BFS ile etki ağacını çıkarır.
"""

from collections import deque
from dataclasses import dataclass


@dataclass
class ImpactNode:
    name: str
    file: str
    callers: list[str]
    depth: int = 0


class ImpactAnalyzer:
    """
    Geriye BFS ile etki analizi.

    Kullanım:
        analyzer = ImpactAnalyzer(registry)
        result = analyzer.analyze("login", depth=3)

        # Sonuç:
        # {
        #     "target": "login",
        #     "total_impact": 15,
        #     "layers": [
        #         ["authenticate", "verify_token"],  # depth 1
        #         ["check_permission", "log_access"],  # depth 2
        #     ],
        #     "risk": "high",
        #     "files_affected": ["auth.py", "api.py", "middleware.py"]
        # }
    """

    def __init__(self, registry):
        self.registry = registry

    def analyze(self, func_name: str, depth: int = 3) -> dict:
        """
        Fonksiyonun etki alanını hesapla.

        Args:
            func_name: Hedef fonksiyon ismi
            depth: Kaç katman geriye bakılacağı

        Returns:
            Etki analizi sonucu
        """
        visited = set()
        queue = deque([(func_name, 0)])
        layers = []
        all_callers = []
        files_affected = set()

        while queue:
            current, d = queue.popleft()

            if current in visited or d > depth:
                continue

            visited.add(current)
            contract = self.registry.get(current)

            if not contract:
                continue

            # Dosyayı ekle
            files_affected.add(contract.file)

            # Bu fonksiyonu kimler çağırıyor?
            if contract.called_by:
                next_layer = []
                for caller in contract.called_by:
                    caller_name = caller.split(":")[-1] if ":" in caller else caller
                    if caller_name not in visited:
                        next_layer.append(caller_name)
                        all_callers.append(caller_name)
                        queue.append((caller_name, d + 1))

                if next_layer:
                    layers.append(next_layer)

        total = len(all_callers)

        # Risk seviyesi hesapla
        if total >= 10:
            risk = "critical"
        elif total >= 5:
            risk = "high"
        elif total >= 2:
            risk = "medium"
        else:
            risk = "low"

        return {
            "target": func_name,
            "total_impact": total,
            "layers": layers,
            "risk": risk,
            "files_affected": list(files_affected),
            "depth_reached": depth,
        }

    def analyze_breaking_change(self, func_name: str) -> dict:
        """
        Breaking change analizi - bu fonksiyonu kimler çağırıyor?
        Signature değişirse kimler kırılır?
        """
        contract = self.registry.get(func_name)

        if not contract:
            return {
                "is_safe": True,
                "breaking": [],
                "message": "Fonksiyon registry'de yok",
            }

        breaking = contract.called_by.copy()

        return {
            "is_safe": len(breaking) == 0,
            "breaking": breaking,
            "count": len(breaking),
            "message": f"{len(breaking)} fonksiyon kırılır",
        }

    def find_circular_deps(self) -> list[list[str]]:
        """
        Döngüsel bağımlılıkları bul.
        """
        cycles = []

        for func_name in self.registry.functions:
            visited = set()
            path = []

            if self._has_cycle(func_name, visited, path):
                cycles.append(path)

        return cycles

    def _has_cycle(self, func_name: str, visited: set, path: list) -> bool:
        """Helper for cycle detection."""
        if func_name in path:
            path.append(func_name)
            return True

        if func_name in visited:
            return False

        visited.add(func_name)
        path.append(func_name)

        contract = self.registry.get(func_name)
        if contract and contract.called_by:
            for caller in contract.called_by:
                caller_name = caller.split(":")[-1] if ":" in caller else caller
                if self._has_cycle(caller_name, visited, path.copy()):
                    return True

        path.pop()
        return False


def quick_impact(func_name: str, registry) -> str:
    """
    Hızlı etki özet string'i.
    AI prompt'una direkt eklenebilir.
    """
    analyzer = ImpactAnalyzer(registry)
    result = analyzer.analyze(func_name, depth=3)

    lines = [
        f"## Impact Analysis: {func_name}",
        "",
        f"**Risk Level:** {result['risk'].upper()}",
        f"**Total Impacted:** {result['total_impact']} functions",
        f"**Files Affected:** {', '.join(result['files_affected'][:5])}",
        "",
    ]

    if result["layers"]:
        lines.append("**Call Hierarchy:**")
        for i, layer in enumerate(result["layers"], 1):
            lines.append(f"  Depth {i}: {', '.join(layer[:3])}")
            if len(layer) > 3:
                lines[-1] += f" ... +{len(layer) - 3} more"

    lines.append("")
    lines.append("WARNING - Before changing, ensure:")
    lines.append(f"- {result['files_affected']} files are updated")
    if result["risk"] in ["high", "critical"]:
        lines.append("- Multiple functions depend on this - review all callers")

    return "\n".join(lines)


if __name__ == "__main__":
    # Test
    from registry import Registry

    registry = Registry(".")
    analyzer = ImpactAnalyzer(registry)

    # Örnek çıktı
    print("=== Impact Analysis Demo ===")
    print(quick_impact("login", registry))
