#!/usr/bin/env python3
"""Run deterministic repository integrity checks without external services."""

from __future__ import annotations

import ast
import hashlib
import json
from collections import Counter
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
COLLECTIONS = ("jobs", "papers", "industry")
REQUIRED_FILES = (
    "AGENTS.md",
    "README.md",
    "docs/06-project-status.md",
    "docs/07-exploration-history.md",
    "docs/08-next-work.md",
    "data/index.json",
    "data/summary.json",
    "research/memory/findings.md",
    "research/memory/evidence-ledger.md",
    "research/completion-verification/findings.md",
    "research/completion-verification/evidence-ledger.md",
    "research/evaluation/k1412-agent-evaluation-v1.md",
    "data/evaluation/k1412-agent-eval-task-contracts-v1.json",
    "tools/evaluation/score_agent_runs.py",
    "evaluation-site/index.html",
    "evaluation-site/evaluation-v1.html",
    "evaluation-site/assets/styles.css",
    "evaluation-site/assets/app.js",
    "evaluation-site/assets/research.css",
    "evaluation-site/assets/research.js",
    "evaluation-site/assets/evidence-data.js",
    "evaluation-site/build_research_data.py",
    "evaluation-site/Dockerfile",
    "evaluation-site/nginx.conf",
    "web/app.py",
    "web/agent-knowledge-atlas.service",
    "web/manage.sh",
)


def load_json(path: str):
    return json.loads((ROOT / path).read_text(encoding="utf-8"))


def require(condition: bool, message: str) -> None:
    if not condition:
        raise AssertionError(message)


def check_required_files() -> None:
    missing = [path for path in REQUIRED_FILES if not (ROOT / path).is_file()]
    require(not missing, f"missing required files: {', '.join(missing)}")


def check_collection_index() -> dict[str, int]:
    index = load_json("data/index.json")
    summary = load_json("data/summary.json")
    require(isinstance(index, list), "data/index.json must be an array")

    paths = [str(item.get("path") or "") for item in index]
    require(len(paths) == len(set(paths)), "data/index.json contains duplicate paths")
    missing_paths = [path for path in paths if not (ROOT / path).is_file()]
    require(not missing_paths, f"index points to missing files: {missing_paths[:5]}")

    indexed = Counter(str(item.get("collection") or "") for item in index)
    filesystem = {
        collection: sum(
            1
            for path in (ROOT / collection / "items").glob("*.md")
            if path.name != "README.md"
        )
        for collection in COLLECTIONS
    }
    require(dict(indexed) == filesystem, f"index/file count mismatch: {dict(indexed)} != {filesystem}")
    require(summary.get("total") == len(index), "summary total does not match index")
    require(summary.get("by_collection") == filesystem, "summary collection counts are stale")
    return filesystem


def check_research_data() -> dict[str, int]:
    expanded = load_json("data/research/agent-memory-expanded.json")
    screened = load_json("data/research/agent-memory-screened.json")
    landscape = load_json("data/research/memory-landscape.json")
    corpus = load_json("data/research/memory-corpus.json")

    candidate_count = len(expanded.get("papers", []))
    require(expanded.get("unique_papers") == candidate_count, "expanded memory count mismatch")
    require(screened.get("total") == candidate_count, "screen total mismatch")
    require(screened.get("completed") == candidate_count, "memory screening is incomplete")
    require(len(screened.get("papers", [])) == candidate_count, "screen paper count mismatch")
    require(landscape.get("source_candidates") == candidate_count, "landscape source count mismatch")
    require(corpus.get("resolved") == len(corpus.get("papers", [])), "local memory corpus mismatch")

    return {
        "memory_candidates": candidate_count,
        "memory_title_central": int(landscape.get("title_central") or 0),
        "memory_local_topic": int(corpus.get("resolved") or 0),
    }


def check_python_syntax() -> int:
    paths = sorted((ROOT / "tools").rglob("*.py")) + [
        ROOT / "web" / "app.py",
        ROOT / "evaluation-site" / "build_research_data.py",
    ]
    for path in paths:
        ast.parse(path.read_text(encoding="utf-8"), filename=str(path))
    return len(paths)


def check_model_api_contract() -> None:
    source = (ROOT / "web" / "app.py").read_text(encoding="utf-8")
    require('"num_ctx"' not in source, "web app must not override model API context")
    require('"keep_alive"' not in source, "web app must not send legacy keep_alive")
    require("/api/generate" not in source, "web app must use the OpenAI-compatible model API")


def check_experiment_state() -> None:
    module = load_json("learning/agent-memory/module.json")
    require(module.get("status") == "failed", "old learning pilot must remain marked failed")
    pilot = (ROOT / "experiments/knowledge-compilation/2026-07-10-agent-memory-pilot.md").read_text(
        encoding="utf-8"
    )
    require("status: failed" in pilot, "pilot experiment status is stale")


def check_evaluation_contract() -> int:
    suite = load_json("data/evaluation/k1412-agent-eval-task-contracts-v1.json")
    require(
        suite.get("schema_version") == "agent-eval-task-contracts/v1",
        "unknown Agent evaluation task schema",
    )
    tasks = suite.get("tasks")
    require(isinstance(tasks, list) and tasks, "Agent evaluation task suite is empty")
    budget_profiles = suite.get("budget_profiles") or {}
    require(isinstance(budget_profiles, dict) and budget_profiles, "evaluation budget profiles are empty")
    task_ids = [str(task.get("task_id") or "") for task in tasks]
    require(all(task_ids), "Agent evaluation task is missing task_id")
    require(len(task_ids) == len(set(task_ids)), "Agent evaluation task ids are not unique")
    for task in tasks:
        require(task.get("title_zh"), f"{task['task_id']} is missing Chinese title")
        require(task.get("version"), f"{task['task_id']} is missing version")
        require(task.get("runner_status"), f"{task['task_id']} is missing runner_status")
        require(
            task.get("budget_profile") in budget_profiles,
            f"{task['task_id']} references an unknown budget profile",
        )
        require(task.get("observable_completion"), f"{task['task_id']} has no completion contract")
        require(task.get("forbidden_effects") is not None, f"{task['task_id']} has no forbidden effects")
    return len(tasks)


def check_evaluation_site() -> int:
    html = (ROOT / "evaluation-site/index.html").read_text(encoding="utf-8")
    archived_html = (ROOT / "evaluation-site/evaluation-v1.html").read_text(encoding="utf-8")
    javascript = (ROOT / "evaluation-site/assets/app.js").read_text(encoding="utf-8")
    research_javascript = (ROOT / "evaluation-site/assets/research.js").read_text(encoding="utf-8")
    evidence_javascript = (ROOT / "evaluation-site/assets/evidence-data.js").read_text(
        encoding="utf-8"
    )
    ledger = (ROOT / "research/completion-verification/evidence-ledger.md").read_text(
        encoding="utf-8"
    )
    dockerfile = (ROOT / "evaluation-site/Dockerfile").read_text(encoding="utf-8")
    nginx = (ROOT / "evaluation-site/nginx.conf").read_text(encoding="utf-8")
    require("Agent 到底凭什么说" in html, "completion research document is missing")
    require("自有跑分" in html and ">0<" in html, "research page overstates benchmark evidence")
    require("completion certificate" in html.lower(), "research synthesis boundary is missing")
    require(
        "不是真实跑分" in archived_html,
        "archived evaluation simulation must keep its evidence boundary visible",
    )
    require(
        "尚未运行真实模型对比" in archived_html,
        "archived evaluation report overstates current evidence",
    )
    require("const TASKS" in javascript, "evaluation task browser is missing")
    require("K1412_EVIDENCE" in research_javascript, "research evidence browser is missing")

    prefix = "window.K1412_EVIDENCE="
    require(evidence_javascript.startswith("/* Generated") and prefix in evidence_javascript, "bad evidence data")
    payload_text = evidence_javascript.split(prefix, 1)[1].strip()
    require(payload_text.endswith(";"), "bad evidence data terminator")
    payload = json.loads(payload_text[:-1])
    counts = payload.get("counts") or {}
    records = payload.get("records") or []
    require(counts.get("pool") == len(records) == 40, "completion evidence pool count mismatch")
    require(counts.get("audited") == 37, "completion audited count mismatch")
    require(counts.get("core") == 26, "completion core count mismatch")
    require(
        payload.get("source_sha256") == hashlib.sha256(ledger.encode("utf-8")).hexdigest(),
        "completion evidence browser data is stale",
    )

    require("install -d -m 0755" in dockerfile, "static asset directory must remain traversable")
    require("evaluation-v1.html" in dockerfile, "archived evaluation page is not packaged")
    require('location = /health' in nginx, "evaluation site health endpoint is missing")
    return int(counts["audited"])


def main() -> int:
    check_required_files()
    collections = check_collection_index()
    research = check_research_data()
    python_files = check_python_syntax()
    check_model_api_contract()
    check_experiment_state()
    evaluation_tasks = check_evaluation_contract()
    completion_evidence = check_evaluation_site()
    print(
        json.dumps(
            {
                "ok": True,
                "collections": collections,
                "research": research,
                "evaluation_tasks": evaluation_tasks,
                "completion_evidence": completion_evidence,
                "evaluation_site": True,
                "python_files_checked": python_files,
            },
            ensure_ascii=False,
            indent=2,
        )
    )
    return 0


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