from __future__ import annotations

from dataclasses import dataclass
from typing import Any

from ..schema import EvidenceImport, ModuleEdge
from .import_resolver import InternalImportResolver


@dataclass(frozen=True)
class EdgeAgg:
    weight: int
    evidence: list[EvidenceImport]


def build_file_graph(
    project: dict[str, Any],
    *,
    resolver: InternalImportResolver,
    max_evidence_per_edge: int = 20,
) -> dict[tuple[str, str], EdgeAgg]:
    """
    Build internal file->file import graph from project_map facts (FileAtlas output).
    Only imports resolved to repo files are included.
    """
    max_ev = max(0, int(max_evidence_per_edge))
    edges: dict[tuple[str, str], EdgeAgg] = {}

    edges_obj = project.get("edges", {}) if isinstance(project.get("edges", {}), dict) else {}
    imports = edges_obj.get("imports", []) if isinstance(edges_obj.get("imports", []), list) else []

    for item in imports:
        if not isinstance(item, dict):
            continue
        from_path = str(item.get("path") or "").replace("\\", "/").strip("/")
        if not from_path:
            continue
        resolved = resolver.resolve_internal(from_path=from_path, import_ref=item)
        if not resolved:
            continue
        key = (resolved.from_path, resolved.to_path)

        ev: EvidenceImport = {
            "from_path": resolved.from_path,
            "to_path": resolved.to_path,
            "import_ref": {k: v for k, v in item.items() if k != "path"},
        }

        cur = edges.get(key)
        if cur is None:
            edges[key] = EdgeAgg(weight=1, evidence=[ev] if max_ev != 0 else [])
        else:
            evidence = cur.evidence
            if max_ev != 0 and len(evidence) < max_ev:
                evidence = evidence + [ev]
            edges[key] = EdgeAgg(weight=cur.weight + 1, evidence=evidence)

    return edges


def build_module_graph(
    file_edges: dict[tuple[str, str], EdgeAgg],
    *,
    file_to_module: dict[str, str],
    max_evidence_per_edge: int = 20,
) -> list[ModuleEdge]:
    """
    Aggregate file graph edges into module graph edges.
    """
    max_ev = max(0, int(max_evidence_per_edge))
    agg: dict[tuple[str, str], EdgeAgg] = {}

    for (fp, tp), e in file_edges.items():
        fm = file_to_module.get(fp)
        tm = file_to_module.get(tp)
        if not fm or not tm:
            continue
        if fm == tm:
            continue
        key = (fm, tm)
        cur = agg.get(key)
        if cur is None:
            ev = e.evidence[:max_ev] if max_ev != 0 else []
            agg[key] = EdgeAgg(weight=int(e.weight), evidence=ev)
        else:
            new_weight = cur.weight + int(e.weight)
            ev = cur.evidence
            if max_ev != 0 and len(ev) < max_ev:
                ev = ev + e.evidence[: max(0, max_ev - len(ev))]
            agg[key] = EdgeAgg(weight=new_weight, evidence=ev)

    out: list[ModuleEdge] = []
    for (fm, tm), e in sorted(agg.items(), key=lambda kv: (-kv[1].weight, kv[0][0], kv[0][1])):
        out.append(
            {
                "from_module": fm,
                "to_module": tm,
                "kind": "imports",
                "weight": int(e.weight),
                "evidence": e.evidence,
            }
        )
    return out


def build_module_adjacency(module_edges: list[ModuleEdge]) -> tuple[dict[str, dict[str, int]], dict[str, dict[str, int]]]:
    """
    Returns (out_adj, in_adj) with weights.
    """
    out_adj: dict[str, dict[str, int]] = {}
    in_adj: dict[str, dict[str, int]] = {}
    for e in module_edges:
        fm = str(e.get("from_module") or "")
        tm = str(e.get("to_module") or "")
        w = int(e.get("weight") or 0)
        if not fm or not tm or w <= 0:
            continue
        out_adj.setdefault(fm, {})
        out_adj[fm][tm] = out_adj[fm].get(tm, 0) + w
        in_adj.setdefault(tm, {})
        in_adj[tm][fm] = in_adj[tm].get(fm, 0) + w
    return out_adj, in_adj

