"""``mgf-fed status`` — the read-only federation tracking matrix.

Scans each member's repo on disk for the standard scaffolding + conformance state and
renders a table (and a machine-readable ``MGF_FED_STATUS.json``). No network, no writes
to any repo — pure inspection.
"""

from __future__ import annotations

import json
import re
import tomllib
from collections import Counter
from dataclasses import asdict, dataclass
from datetime import UTC, datetime
from pathlib import Path

from mgf.fed._roster import Member, federation_root, members

_LEDGER_REL = Path("docs/inprogress/MGF_STANDARDS_CONFORMANCE.md")
_VERDICT_RE = re.compile(r"\|\s*([A-Z]{2,5}-\d+)\s*\|\s*(✅|⏳|N/A|🚫)\s*\|")
_STATUS_JSON = Path.home() / ".mgf" / "MGF_FED_STATUS.json"


@dataclass(frozen=True, slots=True)
class RepoStatus:
    repo: str
    lang: str
    wave: int
    source_version: str
    registry_version: str
    has_ci: bool
    has_import_linter: bool
    has_public_api: bool
    has_ledger: bool
    conf_ok: int
    conf_wip: int
    conf_na: int
    conf_pct: int | None  # % of applicable MUSTs evidenced; None if no ledger
    has_standards_extra: bool
    version_synced: bool  # source pyproject == registry record


def _pyproject_version(repo_dir: Path) -> str:
    p = repo_dir / "pyproject.toml"
    if not p.is_file():
        return "?"
    try:
        return str(
            tomllib.loads(p.read_text(encoding="utf-8")).get("project", {}).get("version", "?")
        )
    except (tomllib.TOMLDecodeError, OSError):
        return "?"


def _has_import_linter(repo_dir: Path) -> bool:
    if (repo_dir / ".importlinter").is_file():
        return True
    p = repo_dir / "pyproject.toml"
    return p.is_file() and "[tool.importlinter" in p.read_text(encoding="utf-8", errors="replace")


def _conformance(repo_dir: Path) -> tuple[int, int, int, int | None]:
    led = repo_dir / _LEDGER_REL
    if not led.is_file():
        return 0, 0, 0, None
    seen: dict[str, str] = {}
    for rid, st in _VERDICT_RE.findall(led.read_text(encoding="utf-8", errors="replace")):
        seen[rid] = st  # last status for each id wins
    c = Counter(seen.values())
    ok, wip, na, decl = c.get("✅", 0), c.get("⏳", 0), c.get("N/A", 0), c.get("🚫", 0)
    applicable = ok + wip + decl
    pct = round(100 * ok / applicable) if applicable else None
    return ok, wip, na, pct


def scan(member: Member) -> RepoStatus:
    d = member.repo_dir
    src_ver = _pyproject_version(d)
    ok, wip, na, pct = _conformance(d)
    return RepoStatus(
        repo=member.repo,
        lang=member.lang,
        wave=member.wave,
        source_version=src_ver,
        registry_version=member.registry_version,
        has_ci=(d / ".woodpecker.yml").is_file(),
        has_import_linter=_has_import_linter(d),
        has_public_api=(d / "PUBLIC_API.json").is_file(),
        has_ledger=(d / _LEDGER_REL).is_file(),
        conf_ok=ok,
        conf_wip=wip,
        conf_na=na,
        conf_pct=pct,
        has_standards_extra=member.has_standards_extra,
        version_synced=(src_ver == member.registry_version),
    )


def _glyph(b: bool) -> str:
    return "✓" if b else "·"


def render_table(rows: list[RepoStatus], *, shape: str = "all") -> str:
    picked = [r for r in rows if shape == "all" or r.lang == shape]
    hdr = f"{'W':>1}  {'repo':<22} {'version':<9} {'conf':>5}  CI IL API LG std  notes"
    lines = [hdr, "─" * len(hdr)]
    for r in picked:
        conf = f"{r.conf_pct}%" if r.conf_pct is not None else "—"
        note = "" if r.version_synced else f"!ver src={r.source_version} reg={r.registry_version}"
        lines.append(
            f"{r.wave if r.wave >= 0 else '?':>1}  {r.repo:<22} {r.source_version:<9} {conf:>5}  "
            f"{_glyph(r.has_ci):>2} {_glyph(r.has_import_linter):>2} {_glyph(r.has_public_api):>3} "
            f"{_glyph(r.has_ledger):>2} {_glyph(r.has_standards_extra):>3}  {note}"
        )
    # fleet summary
    py = [r for r in picked if r.lang == "python"]
    with_ci = sum(r.has_ci for r in py)
    avg = [r.conf_pct for r in py if r.conf_pct is not None]
    lines.append("─" * len(hdr))
    lines.append(
        f"   {len(picked)} members ({len(py)} python) · CI {with_ci}/{len(py)} · "
        f"avg conf {round(sum(avg) / len(avg)) if avg else 0}% · "
        f"legend: IL=import-linter API=PUBLIC_API.json LG=ledger std=[standards]"
    )
    return "\n".join(lines)


def write_status_json(rows: list[RepoStatus], path: Path = _STATUS_JSON) -> Path:
    path.parent.mkdir(parents=True, exist_ok=True)
    payload = {
        "generated": datetime.now(UTC).isoformat(timespec="seconds"),
        "root": str(federation_root()),
        "members": [asdict(r) for r in rows],
    }
    path.write_text(json.dumps(payload, indent=2, sort_keys=True) + "\n", encoding="utf-8")
    return path


def run_status(*, shape: str = "all", as_json: bool = False, root: Path | None = None) -> int:
    rows = [scan(m) for m in members(root)]
    out = write_status_json(rows)
    if as_json:
        print(out.read_text(encoding="utf-8"), end="")
    else:
        print(render_table(rows, shape=shape))
        print(f"\nwrote {out}")
    return 0
