from __future__ import annotations

import argparse
import hashlib
import json
import os
import pathlib
import re
import shutil
import stat
import tempfile
import time
import zipfile


ROOT = pathlib.Path(__file__).resolve().parents[1]
import sys
sys.path.insert(0, str(ROOT / "src"))

from reliability_core.archive_r10r2 import canonical_hash, sha256_file, source_tree_manifest


TEXT_SUFFIXES = {".py", ".json", ".jsonl", ".md", ".txt", ".toml", ".ini", ".csv", ".yaml", ".yml"}
SECRET_PATTERNS = (
    re.compile(r"-----BEGIN (?:RSA |EC |OPENSSH )?PRIVATE KEY-----"),
    re.compile(r"AKIA[0-9A-Z]{16}"),
    re.compile(r"(?i)bearer\s+[A-Za-z0-9._~+/=-]{20,}"),
)


def json_write(path: pathlib.Path, value: object) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    path.write_text(json.dumps(value, ensure_ascii=False, indent=2, sort_keys=True) + "\n", encoding="utf-8")


def sanitize_text(text: str, tokens: list[str], replacements: list[str]) -> str:
    result = text
    for token in tokens:
        for variant in {token, token.lower(), token.upper(), token.title()}:
            result = result.replace(variant, "[REDACTED_NAME]")
    for value in replacements:
        result = result.replace(value, "[LOCAL_PATH]")
    return result


def sanitize_value(value: object, tokens: list[str], replacements: list[str]) -> object:
    if isinstance(value, dict):
        return {str(key): sanitize_value(item, tokens, replacements) for key, item in value.items()}
    if isinstance(value, list):
        return [sanitize_value(item, tokens, replacements) for item in value]
    if isinstance(value, str):
        return sanitize_text(value, tokens, replacements)
    return value


def materialize(source: pathlib.Path, target: pathlib.Path, tokens: list[str], replacements: list[str]) -> list[dict[str, object]]:
    rows: list[dict[str, object]] = []
    for path in sorted(item for item in source.rglob("*") if item.is_file()):
        relative = path.relative_to(source)
        destination = target / relative
        destination.parent.mkdir(parents=True, exist_ok=True)
        raw = path.read_bytes()
        if path.suffix.lower() == ".json":
            value = json.loads(raw.decode("utf-8-sig"))
            destination.write_text(json.dumps(sanitize_value(value, tokens, replacements), ensure_ascii=False, indent=2, sort_keys=True) + "\n", encoding="utf-8")
        elif path.suffix.lower() in TEXT_SUFFIXES:
            destination.write_text(sanitize_text(raw.decode("utf-8", errors="replace"), tokens, replacements), encoding="utf-8")
        else:
            destination.write_bytes(raw)
        rows.append({
            "path": destination.relative_to(target).as_posix(),
            "input_sha256": hashlib.sha256(raw).hexdigest(),
            "materialized_sha256": sha256_file(destination),
        })
    return rows


def copy_source(stage: pathlib.Path) -> dict[str, object]:
    manifest = source_tree_manifest(ROOT)
    for row in manifest["entries"]:
        relative = pathlib.PurePosixPath(str(row["path"]))
        source = ROOT / pathlib.Path(*relative.parts[1:])
        destination = stage / pathlib.Path(*relative.parts)
        destination.parent.mkdir(parents=True, exist_ok=True)
        shutil.copy2(source, destination)
    return manifest


def scan_stage(stage: pathlib.Path, forbidden: list[str]) -> dict[str, object]:
    forbidden_hits: list[str] = []
    secret_hits: list[str] = []
    for path in sorted(item for item in stage.rglob("*") if item.is_file()):
        relative = path.relative_to(stage).as_posix()
        if any(token in relative for token in forbidden):
            forbidden_hits.append("name:" + relative)
        if path.suffix.lower() not in TEXT_SUFFIXES:
            continue
        text = path.read_text(encoding="utf-8", errors="replace")
        for token in forbidden:
            if token in text:
                forbidden_hits.append("content:" + relative)
        if any(pattern.search(text) for pattern in SECRET_PATTERNS):
            secret_hits.append(relative)
    return {"forbidden_hits": forbidden_hits, "secret_hits": secret_hits, "passed": not forbidden_hits and not secret_hits}


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--output-dir", required=True, type=pathlib.Path)
    parser.add_argument("--prepatch-dir", required=True, type=pathlib.Path)
    parser.add_argument("--baseline-dir", required=True, type=pathlib.Path)
    parser.add_argument("--candidate-supervisor-dir", required=True, type=pathlib.Path)
    parser.add_argument("--negative-dir", required=True, type=pathlib.Path)
    parser.add_argument("--rollback-dir", required=True, type=pathlib.Path)
    parser.add_argument("--forbidden-token-file", required=True, type=pathlib.Path)
    args = parser.parse_args()
    forbidden = [line.strip() for line in args.forbidden_token_file.read_text(encoding="utf-8").splitlines() if line.strip()]
    if not forbidden:
        raise SystemExit("empty forbidden token list")
    args.output_dir.mkdir(parents=True, exist_ok=True)
    replacements = [str(ROOT), str(ROOT.parent), str(args.prepatch_dir.parent)]
    timestamp = time.strftime("%Y%m%d_%H%M%S")
    name = f"ACTIVE_PROJECT_RESULT_R10R2_{timestamp}.zip"
    result = args.output_dir / name
    sidecar = pathlib.Path(str(result) + ".sha256.json")
    with tempfile.TemporaryDirectory(prefix="r10r2-seal-") as directory:
        stage = pathlib.Path(directory) / "stage"; stage.mkdir()
        source_manifest = copy_source(stage)
        json_write(stage / "checksums" / "source_tree_manifest.json", source_manifest)
        materialization = {
            "prepatch": materialize(args.prepatch_dir, stage / "prepatch_evidence" / "r10_r2", forbidden, replacements),
            "baseline": materialize(args.baseline_dir, stage / "integration_provenance" / "lineage", forbidden, replacements),
            "candidate": materialize(args.candidate_supervisor_dir, stage / "postpatch_evidence" / "r10_r2" / "candidate_supervisor", forbidden, replacements),
            "negative": materialize(args.negative_dir, stage / "postpatch_evidence" / "r10_r2" / "negative_controls", forbidden, replacements),
            "rollback": materialize(args.rollback_dir, stage / "rollback" / "drill", forbidden, replacements),
        }
        json_write(stage / "integration_provenance" / "materialization_manifest.json", materialization)
        supervisor = json.loads((args.candidate_supervisor_dir / "supervisor_record.json").read_text(encoding="utf-8"))
        json_write(stage / "reports" / "candidate_supervisor_summary.json", sanitize_value(supervisor, forbidden, replacements))
        json_write(stage / "reports" / "policy_audit.json", supervisor.get("policy_audit", {}))
        json_write(stage / "reports" / "r10_r2_acceptance_status.json", {
            "gate_count": 36,
            "status": "PENDING_EXACT_FINAL_EXTERNAL_REPLAY",
            "reason": "sealed archive cannot contain its own later replay evidence",
        })
        summary = {
            "verdict": "NEEDS_EVIDENCE",
            "progress_percent": 50.0,
            "source_tree_sha256": source_manifest["tree_sha256"],
            "discovered_test_count": supervisor.get("test_count"),
            "candidate_supervisor_passed": supervisor.get("passed"),
            "exact_final_replay_external": True,
            "root_goal_anchor": "sealed exact-final replay evidence closure",
            "current_work_anchor": "R10 R2 supervisor, policy audit and external replay",
            "capacity_cleanup_anchor": "no delete, no raw secret, source artifacts excluded",
            "next_handoff_anchor": "technical PASS bundle then support-stage plan only",
            "mandatory_inheritance_contract": {"sidecar_first_loading": True, "raw_suppression": True, "self_check_gates": True},
            "tokenmax_runtime_inheritance": {"token_size_first": True, "latest_sidecar_first_chain": True},
        }
        json_write(stage / "summary" / "summary.json", summary)
        json_write(stage / "summary" / "verdict.json", {"verdict": "NEEDS_EVIDENCE", "reason": "exact-final replay is external by contract"})
        json_write(stage / "issues" / "issues.json", {"issue_count": 1, "issues": [{"id": "EXACT_FINAL_REPLAY_PENDING", "blocking": True}]})
        json_write(stage / "warnings" / "warnings.json", {"warning_count": 0, "warnings": []})
        (stage / "handoff").mkdir(parents=True, exist_ok=True)
        (stage / "handoff" / "NEXT_SUPPORT_STAGE_WORKSPEC.md").write_text(
            "# Next support stage\n\nActivate only after external technical PASS. Prepare public materials and application support; do not submit externally without approval.\n",
            encoding="utf-8",
        )
        json_write(stage / "handoff" / "compact_handoff.json", {
            "status": "WAIT_FOR_EXTERNAL_TECHNICAL_PASS",
            "next_scope": "support-stage planning only",
            "external_submission": "NOT_AUTHORIZED",
        })
        scan = scan_stage(stage, forbidden)
        json_write(stage / "reports" / "seal_scan.json", scan)
        if not scan["passed"]:
            raise SystemExit("stage scan failed")
        checksum_rows = []
        for path in sorted(item for item in stage.rglob("*") if item.is_file() and item.relative_to(stage).as_posix() != "checksums/files.sha256"):
            checksum_rows.append(f"{sha256_file(path)}  {path.relative_to(stage).as_posix()}")
        (stage / "checksums" / "files.sha256").write_text("\n".join(checksum_rows) + "\n", encoding="utf-8")
        for path in sorted(item for item in stage.rglob("*.json") if item.is_file()):
            json.loads(path.read_text(encoding="utf-8"))
        with zipfile.ZipFile(result, "w", zipfile.ZIP_DEFLATED, compresslevel=9) as archive:
            for path in sorted(item for item in stage.rglob("*") if item.is_file()):
                archive.write(path, path.relative_to(stage).as_posix())
    sealed_hash = sha256_file(result)
    with zipfile.ZipFile(result) as archive:
        bad = archive.testzip(); entry_count = len(archive.namelist())
        packed_manifest = json.loads(archive.read("checksums/source_tree_manifest.json").decode("utf-8"))
    if bad is not None or packed_manifest["tree_sha256"] != source_manifest["tree_sha256"]:
        raise SystemExit("sealed ZIP verification failed")
    json_write(sidecar, {
        "file": result.name,
        "sha256": sealed_hash,
        "size": result.stat().st_size,
        "zip_entry_count": entry_count,
        "zip_test_bad_file": bad,
        "source_tree_sha256": source_manifest["tree_sha256"],
        "verdict": "SEALED_FOR_INDEPENDENT_REPLAY",
        "sealed_epoch": time.time(),
    })
    os.chmod(result, stat.S_IREAD)
    print(json.dumps({"result_zip": str(result), "sidecar": str(sidecar), "sha256": sealed_hash, "source_tree_sha256": source_manifest["tree_sha256"], "entry_count": entry_count}, sort_keys=True))
    return 0


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