from dataclasses import dataclass, field
from pathlib import Path

import tomli_w

from ossature.models.amd import AMDSpec
from ossature.models.smd import SMDSpec


@dataclass
class SpecGraphEntry:
    id: str
    file: str
    depends: list[str]
    architectures: list[str] = field(default_factory=list)


@dataclass
class SpecGraph:
    specs: list[SpecGraphEntry]
    levels: list[list[str]]


def _topological_levels(parsed_smds: list[SMDSpec]) -> list[list[str]]:
    spec_ids = {smd.spec_id for smd in parsed_smds}
    deps = {smd.spec_id: set(smd.depends) & spec_ids for smd in parsed_smds}

    levels: list[list[str]] = []
    remaining = set(spec_ids)
    resolved: set[str] = set()

    while remaining:
        level = sorted(sid for sid in remaining if deps[sid] <= resolved)
        if not level:
            break
        levels.append(level)
        resolved.update(level)
        remaining -= set(level)

    return levels


def build_spec_graph(
    parsed_smds: list[SMDSpec],
    parsed_amds: list[AMDSpec],
    smd_files: list[Path],
    amd_files: list[Path],
    root: Path,
) -> SpecGraph:
    smd_paths: dict[str, str] = {}
    for smd, smd_file in zip(parsed_smds, smd_files):
        smd_paths[smd.spec_id] = f"./{smd_file.relative_to(root)}"

    amd_paths: dict[str, list[str]] = {}
    for amd, amd_file in zip(parsed_amds, amd_files):
        amd_paths.setdefault(amd.spec_id, []).append(f"./{amd_file.relative_to(root)}")

    entries = []
    for smd in parsed_smds:
        entries.append(
            SpecGraphEntry(
                id=smd.spec_id,
                file=smd_paths[smd.spec_id],
                depends=list(smd.depends),
                architectures=amd_paths.get(smd.spec_id, []),
            )
        )

    levels = _topological_levels(parsed_smds)

    return SpecGraph(specs=entries, levels=levels)


def write_spec_graph(graph: SpecGraph, filename: Path) -> None:
    data = {
        "spec": [
            {
                "id": entry.id,
                "file": entry.file,
                "depends": entry.depends,
                "architectures": entry.architectures,
            }
            for entry in graph.specs
        ],
        "order": {
            "levels": graph.levels,
        },
    }

    content = tomli_w.dumps(data)

    filename.parent.mkdir(parents=True, exist_ok=True)
    with open(filename, "w") as f:
        f.write("# Auto-generated by ossature audit — do not edit\n\n")
        f.write(content)
