"""Project health report derived from the repository graph.

Invoked by the launcher as ``python -c "from zytectx.audit import main; main()"
--root <project>`` - with ``-c``, ``sys.argv[0]`` is ``-c`` and the real
arguments start at index 1, so ``main`` parses ``sys.argv[1:]`` itself.

Prints a human report and writes ``<root>/.zyte/AUDIT_CONTEXT.md``; the
launcher's session hooks inject that file while it is fresh (< 7 days).
"""

from __future__ import annotations

import argparse
import sys
from datetime import datetime, timezone
from pathlib import Path

from . import prime, scan
from . import graph as _graph
from . import store as _store
from . import weights as _weights

TOP_COUNT = 5


def _collect(root: Path) -> dict:
    scan(root)
    loaded = _graph.load(root)
    if loaded is None:
        return {"has_graph": False}

    records = sorted(loaded.files.values(), key=lambda record: record.get("size", 0), reverse=True)
    largest = [(record["path"], record.get("size", 0), record.get("lines", 0)) for record in records[:TOP_COUNT]]

    orphans = [
        rel
        for rel in loaded.files
        if not loaded.imports.get(rel) and not loaded.imported_by.get(rel)
    ]
    hubs = sorted(
        ((rel, len(sources)) for rel, sources in loaded.imported_by.items() if rel in loaded.files),
        key=lambda item: item[1],
        reverse=True,
    )[:TOP_COUNT]

    try:
        session = _weights.scores(root)
    except Exception:
        session = {}
    heat: dict[str, float] = {}
    for (rel, _symbol), weight in session.items():
        heat[rel] = heat.get(rel, 0.0) + weight
    hot = sorted(heat.items(), key=lambda item: item[1], reverse=True)[:TOP_COUNT]

    return {
        "has_graph": True,
        "files": loaded.file_count,
        "symbols": loaded.symbol_count,
        "edges": loaded.edge_count,
        "stale": loaded.stale(),
        "truncated": bool(loaded.data.get("truncated")),
        "largest": largest,
        "orphans": orphans,
        "hubs": hubs,
        "hot": hot,
        "notes": prime(root).get("items") or [],
    }


def _render(root: Path, report: dict) -> str:
    lines = [f"# Zyte audit - {root.name}", ""]
    if not report.get("has_graph"):
        lines.append("No graph could be built for this directory.")
        return "\n".join(lines) + "\n"
    lines.append(
        f"{report['files']} files, {report['symbols']} symbols, {report['edges']} edges"
        + (" (graph stale)" if report["stale"] else "")
        + (" (repository truncated at the index cap)" if report["truncated"] else "")
    )
    if report["largest"]:
        lines += ["", "## Largest files"]
        lines += [f"- {path} - {size:,} bytes, {count} lines" for path, size, count in report["largest"]]
    if report["hubs"]:
        lines += ["", "## Most depended-on"]
        lines += [f"- {path} - imported by {count} files" for path, count in report["hubs"]]
    if report["orphans"]:
        lines += ["", f"## Unconnected files ({len(report['orphans'])})"]
        lines += [f"- {path}" for path in report["orphans"][:TOP_COUNT]]
        if len(report["orphans"]) > TOP_COUNT:
            lines.append(f"- ... and {len(report['orphans']) - TOP_COUNT} more")
    if report["hot"]:
        lines += ["", "## Recent session hot spots"]
        lines += [f"- {path}" for path, _weight in report["hot"]]
    if report["notes"]:
        lines += ["", "## Open notes"]
        for note in report["notes"]:
            lines.append(f"- ({note.get('type', 'fact')}) {str(note.get('content', '')).strip()}")
    lines += ["", f"Generated {datetime.now(timezone.utc).isoformat(timespec='seconds')}"]
    return "\n".join(lines) + "\n"


def main(argv: list[str] | None = None) -> int:
    parser = argparse.ArgumentParser(prog="zytectx.audit", description="Graph-derived project health report.")
    parser.add_argument("--root", default=".", help="Project directory to audit")
    args = parser.parse_args(sys.argv[1:] if argv is None else argv)

    root = _store.normalize_root(args.root)
    report = _collect(root)
    text = _render(root, report)
    print(text)
    try:
        store_dir = _store.store_dir(root)
        store_dir.mkdir(parents=True, exist_ok=True)
        (store_dir / "AUDIT_CONTEXT.md").write_text(text, encoding="utf-8")
    except OSError:
        pass
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
