"""
Simulation Engine — simulate-change with cost estimation.

Wraps the existing impact.analyzer.analyze() — does NOT duplicate BFS logic.
Adds cost estimation and enriched risk scoring on top.

Formula:
  risk_score = direct_count * 3 + indirect_count * 1 + hub_hits * 5
  downstream_cost_hours = total_affected * avg_hours_per_module
"""

from __future__ import annotations

from dataclasses import dataclass, field
from pathlib import Path

from ..memory.store import KodaraIndex


@dataclass
class SimulationResult:
    file: str
    module_name: str
    risk_score: float
    risk_level: str              # from ImpactResult
    direct_count: int
    indirect_count: int
    hub_hits: int
    downstream_cost_hours: float
    critical_paths: list[list[str]]
    top_affected: list[str]      # top 5 most important affected modules


def simulate(
    index: KodaraIndex,
    target_path: str,
    avg_hours: float = 2.0,
) -> SimulationResult:
    """
    Simulate the cost and impact of changing target_path.

    Delegates graph traversal to impact.analyzer.analyze() and adds
    risk scoring + cost estimation on top.

    Args:
        index:       Loaded KodaraIndex
        target_path: File path to analyse (absolute or relative — fuzzy matched)
        avg_hours:   Average dev-hours per affected module (default 2.0)

    Returns:
        SimulationResult

    Raises:
        ValueError: if target_path is not found in the index.
    """
    from kodara.engine_client import query_impact

    result = query_impact(index.modules, target_path)

    direct_count = len(result["direct"])
    indirect_count = len(result["indirect"])
    hub_hits = sum(1 for m in result["direct"] + result["indirect"] if m["is_hub"])

    risk_score = direct_count * 3 + indirect_count * 1 + hub_hits * 5
    downstream_cost_hours = result["total_affected"] * avg_hours

    # Top 5 affected: hubs first, then by module_name
    all_affected = sorted(
        result["direct"] + result["indirect"],
        key=lambda m: (not m["is_hub"], m["distance"], m["module_name"]),
    )
    top_affected = [m["module_name"] for m in all_affected[:5]]

    return SimulationResult(
        file=result["target"],
        module_name=result["target_module"],
        risk_score=risk_score,
        risk_level=result["risk_level"],
        direct_count=direct_count,
        indirect_count=indirect_count,
        hub_hits=hub_hits,
        downstream_cost_hours=downstream_cost_hours,
        critical_paths=result["critical_paths"],
        top_affected=top_affected,
    )


def simulate_narrative(result: SimulationResult) -> "str | None":
    """Call Kodara backend for risk narrative. Requires active session (Pro)."""
    try:
        from kodara.cloud.client import load_session
        session = load_session()
        if not session:
            return None

        access_token = session.get("access_token") or session.get("token")
        if not access_token:
            return None

        affected = list(result.top_affected)[:8] if result.top_affected else []
        prompt = (
            f"File: {result.file}\n"
            f"Risk score: {result.risk_score}\n"
            f"Direct dependents: {result.direct_count}\n"
            f"Indirect dependents: {result.indirect_count}\n"
            f"Affected: {', '.join(affected)}\n"
            f"In 2-3 sentences: describe the risk and what to watch out for."
        )

        import httpx
        resp = httpx.post(
            "https://getkodara.dev/api/llm",
            json={"prompt": prompt, "session_token": access_token},
            timeout=10.0,
        )
        if resp.status_code == 200:
            return resp.json().get("text")
        return None
    except Exception:
        return None
