from __future__ import annotations

import argparse
import hashlib
import json
from pathlib import Path
import re
from typing import Any


ROOT = Path(__file__).resolve().parents[1]
EXCLUDED_DIRECTORIES = {".git", ".demo-data", "__pycache__", ".pytest_cache", "tmp", "output"}
TEXT_SUFFIXES = {
    "", ".cmd", ".css", ".dockerignore", ".example", ".gitignore", ".html", ".js", ".json",
    ".md", ".ps1", ".py", ".toml", ".txt", ".yaml", ".yml",
}
SECRET_PATTERNS = {
    "private_key": re.compile(r"-----BEGIN [A-Z ]*PRIVATE KEY-----"),
    "aws_access_key": re.compile(r"AKIA[0-9A-Z]{16}"),
    "long_sk_token": re.compile(r"\bsk-[A-Za-z0-9_-]{20,}\b"),
    "github_token": re.compile(r"\bgh[pousr]_[A-Za-z0-9]{30,}\b"),
}
FORBIDDEN_NAME = "".join(chr(value) for value in (110, 101, 108, 121, 100, 111))


def included_files() -> list[Path]:
    rows: list[Path] = []
    for path in ROOT.rglob("*"):
        relative = path.relative_to(ROOT)
        if any(part in EXCLUDED_DIRECTORIES for part in relative.parts):
            continue
        if path.is_file() and path.name not in {".env"} and path.suffix not in {".pyc", ".sqlite3"}:
            rows.append(path)
    return sorted(rows, key=lambda item: item.relative_to(ROOT).as_posix())


def tree_digest(files: list[Path]) -> str:
    digest = hashlib.sha256()
    for path in files:
        relative = path.relative_to(ROOT).as_posix().encode("utf-8")
        digest.update(len(relative).to_bytes(4, "big"))
        digest.update(relative)
        content_digest = hashlib.sha256(path.read_bytes()).digest()
        digest.update(content_digest)
    return digest.hexdigest()


def check() -> dict[str, Any]:
    files = included_files()
    invalid_json: list[str] = []
    forbidden_hits: list[str] = []
    secret_hits: list[dict[str, str]] = []
    unsafe_paths: list[str] = []
    invalid_cmd_line_endings: list[str] = []
    for path in files:
        relative = path.relative_to(ROOT).as_posix()
        if relative.startswith("/") or ".." in Path(relative).parts:
            unsafe_paths.append(relative)
        if path.suffix == ".json":
            try:
                json.loads(path.read_text(encoding="utf-8"))
            except (UnicodeDecodeError, json.JSONDecodeError):
                invalid_json.append(relative)
        if path.suffix.lower() == ".cmd":
            remainder = path.read_bytes().replace(b"\r\n", b"")
            if b"\r" in remainder or b"\n" in remainder:
                invalid_cmd_line_endings.append(relative)
        if path.suffix.lower() not in TEXT_SUFFIXES and path.name not in {"Dockerfile", "Dockerfile.demo"}:
            continue
        try:
            text = path.read_text(encoding="utf-8")
        except UnicodeDecodeError:
            continue
        if FORBIDDEN_NAME.casefold() in text.casefold() or FORBIDDEN_NAME.casefold() in relative.casefold():
            forbidden_hits.append(relative)
        for name, pattern in SECRET_PATTERNS.items():
            if pattern.search(text):
                secret_hits.append({"path": relative, "pattern": name})
        if path.name == ".env.example":
            for line in text.splitlines():
                if line.startswith("UPSTAGE_API_KEY=") and line.partition("=")[2].strip():
                    secret_hits.append({"path": relative, "pattern": "nonempty_example_api_key"})

    required = [
        "README.md",
        "run_demo.py",
        "START_UPSTAGE_LIVE_DEMO.cmd",
        "scripts/start_live_demo.ps1",
        "scripts/live_e2e_runner.py",
        "demo/server.py",
        "demo/static/index.html",
        "demo_tests/test_demo.py",
        "docs/INTERVIEW_GUIDE_KO.md",
        "docs/SUBMISSION_GUIDE_KO.md",
        "evidence/technical_validation_summary.json",
        "evidence/demo_validation_summary.json",
    ]
    present = {path.relative_to(ROOT).as_posix() for path in files}
    missing_required = [path for path in required if path not in present]
    passed = not any((
        invalid_json,
        forbidden_hits,
        secret_hits,
        unsafe_paths,
        invalid_cmd_line_endings,
        missing_required,
    ))
    return {
        "schema": "agent-reliability-live-demo.release-check.v1",
        "passed": passed,
        "included_file_count": len(files),
        "included_logical_bytes": sum(path.stat().st_size for path in files),
        "included_tree_sha256": tree_digest(files),
        "checks": {
            "invalid_json_count": len(invalid_json),
            "forbidden_name_count": len(forbidden_hits),
            "secret_pattern_count": len(secret_hits),
            "unsafe_path_count": len(unsafe_paths),
            "invalid_cmd_line_ending_count": len(invalid_cmd_line_endings),
            "missing_required_count": len(missing_required),
        },
        "invalid_json": invalid_json,
        "forbidden_hits": forbidden_hits,
        "secret_hits": secret_hits,
        "unsafe_paths": unsafe_paths,
        "invalid_cmd_line_endings": invalid_cmd_line_endings,
        "missing_required": missing_required,
    }


def main() -> None:
    parser = argparse.ArgumentParser()
    parser.add_argument("--output", type=Path)
    arguments = parser.parse_args()
    result = check()
    rendered = json.dumps(result, ensure_ascii=False, indent=2, sort_keys=True) + "\n"
    print(rendered, end="")
    if arguments.output:
        arguments.output.parent.mkdir(parents=True, exist_ok=True)
        arguments.output.write_text(rendered, encoding="utf-8")
    if not result["passed"]:
        raise SystemExit(1)


if __name__ == "__main__":
    main()
