"""Architectural drift detection — compare current graph vs previous snapshot."""
from __future__ import annotations

from dataclasses import dataclass, field
from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from kodara.memory.store import KodaraIndex


@dataclass
class DriftAlert:
    level: str        # "high" | "medium" | "info"
    message: str
    detail: str = ""


@dataclass
class DriftReport:
    alerts: list[DriftAlert] = field(default_factory=list)
    new_files: int = 0
    removed_files: int = 0
    new_hubs: list[str] = field(default_factory=list)
    lost_hubs: list[str] = field(default_factory=list)
    dependency_growth: dict[str, int] = field(default_factory=dict)  # file → delta

    @property
    def has_alerts(self) -> bool:
        return len(self.alerts) > 0


def detect_drift(current: "KodaraIndex", previous_snapshot: dict) -> DriftReport:
    """
    Compare current index state against a previous snapshot dict.

    Returns a DriftReport with alerts for significant architectural changes.
    Returns an empty DriftReport if no previous snapshot or on any exception.
    """
    if not previous_snapshot:
        return DriftReport()

    try:
        report = DriftReport()

        current_modules = set(current.modules.keys())
        prev_modules = set(previous_snapshot.get("modules", {}).keys())

        new_module_paths = current_modules - prev_modules
        removed_module_paths = prev_modules - current_modules

        report.new_files = len(new_module_paths)
        report.removed_files = len(removed_module_paths)

        # 1. File count growth > 20%
        prev_count = len(prev_modules)
        if prev_count > 0 and report.new_files > prev_count * 0.20:
            report.alerts.append(DriftAlert(
                level="medium",
                message=f"{report.new_files} new modules added — consider updating onboarding guide",
                detail=f"Previous: {prev_count} modules, now: {len(current_modules)}",
            ))

        # Build in-degree maps from edges
        # Current: count incoming edges per node (dependents = files that import this file)
        current_dependents: dict[str, int] = {}
        for edge in current.graph_edges:
            target = edge.get("to", "")
            if target:
                current_dependents[target] = current_dependents.get(target, 0) + 1

        # Previous: same from snapshot edges
        prev_dependents: dict[str, int] = {}
        for edge in previous_snapshot.get("graph_edges", []):
            target = edge.get("to", "")
            if target:
                prev_dependents[target] = prev_dependents.get(target, 0) + 1

        # 2. Hub growth: files that gained > 3 new dependents
        for path in current_modules & prev_modules:
            cur_deg = current_dependents.get(path, 0)
            prev_deg = prev_dependents.get(path, 0)
            delta = cur_deg - prev_deg

            if delta > 5:
                report.dependency_growth[path] = delta

            if delta > 3:
                report.alerts.append(DriftAlert(
                    level="high",
                    message=f"{Path(path).name} gained {delta} new dependents",
                    detail=f"Now imported by {cur_deg} modules (was {prev_deg})",
                ))

        # 3. New hubs: files that reached >= 5 dependents that weren't hubs before
        HUB_THRESHOLD = 5
        for path in current_modules:
            cur_deg = current_dependents.get(path, 0)
            prev_deg = prev_dependents.get(path, 0)
            if cur_deg >= HUB_THRESHOLD and prev_deg < HUB_THRESHOLD:
                report.new_hubs.append(path)
                report.alerts.append(DriftAlert(
                    level="medium",
                    message=f"{Path(path).name} became a hub ({cur_deg} dependents)",
                    detail=path,
                ))

        # 4. Lost hubs: files that lost > 5 dependents
        for path in current_modules & prev_modules:
            cur_deg = current_dependents.get(path, 0)
            prev_deg = prev_dependents.get(path, 0)
            loss = prev_deg - cur_deg
            if loss > 5:
                report.lost_hubs.append(path)
                report.alerts.append(DriftAlert(
                    level="info",
                    message=f"Dependency reduction in {Path(path).name}",
                    detail=f"Lost {loss} dependents (was {prev_deg}, now {cur_deg})",
                ))

        # 5. Large dependency delta for files not already captured by hub growth
        for path in current_modules & prev_modules:
            cur_deg = current_dependents.get(path, 0)
            prev_deg = prev_dependents.get(path, 0)
            delta = abs(cur_deg - prev_deg)
            if delta > 5 and path not in report.dependency_growth:
                report.dependency_growth[path] = cur_deg - prev_deg

        return report

    except Exception:
        return DriftReport()


def get_drift_report(root: str) -> DriftReport:
    """
    Convenience function: load current index + latest snapshot, return DriftReport.

    Returns empty DriftReport if index or snapshot is missing, or on any error.
    """
    try:
        from kodara.memory.store import MemoryStore
        from kodara.snapshots.store import SnapshotStore

        root_path = Path(root).resolve()
        store = MemoryStore(root_path)
        idx = store.load()
        if not idx:
            return DriftReport()

        kodara_dir = root_path / ".kodara"
        snap_store = SnapshotStore(kodara_dir)
        snapshots = snap_store.list()
        if not snapshots:
            return DriftReport()

        # Load the most recent snapshot (list() returns newest-first)
        latest = snap_store.load(snapshots[0].filename)
        if not latest:
            return DriftReport()

        return detect_drift(idx, latest)

    except Exception:
        return DriftReport()
