#!/usr/bin/env python3
"""Independent offline verifier for the Task26 inode-test-fix successor."""
from __future__ import annotations

import argparse
import hashlib
import json
import stat
import subprocess
import sys
import zipfile
from pathlib import Path
from typing import cast


CONTROLS = {
    "candidate-manifest.json",
    "candidate-seal.json",
    "hash-inventory.json",
    "verifier-input.json",
    "independent-verification.json",
}
TEST_PATH = "tests/test_adaptive_nutrition.py"
EXPECTED_TEST_SHA256 = "0831230dbebb27303a55d9ac2cb349adb217cd639d1db2981eab0974db527354"
EXPECTED_OLD_TEST_SHA256 = "9d323da1fb01e3b05df6557795d87b6e26b8d0e7c8ca83f03b3287a237929369"
EXPECTED_WHEEL_SHA256 = "f75856d6d986b64d3d2f083aec2f865c7aea19f5950b84ff06e519f2f6505af6"


def canonical(value: object) -> bytes:
    return json.dumps(value, sort_keys=True, separators=(",", ":"), ensure_ascii=True).encode()


def sha256(raw: bytes) -> str:
    return hashlib.sha256(raw).hexdigest()


def sha256_file(path: Path) -> str:
    return sha256(path.read_bytes())


def sealed_file(path: Path) -> None:
    info = path.lstat()
    assert not path.is_symlink() and stat.S_ISREG(info.st_mode) and info.st_nlink == 1
    assert stat.S_IMODE(info.st_mode) == 0o400, f"unsealed file: {path}"


def verify(root: Path) -> dict[str, object]:
    root = root.resolve()
    assert root.is_dir() and not root.is_symlink() and stat.S_IMODE(root.stat().st_mode) == 0o500
    for directory in (path for path in root.rglob("*") if path.is_dir()):
        assert not directory.is_symlink() and stat.S_IMODE(directory.stat().st_mode) == 0o500

    manifest_path = root / "candidate-manifest.json"
    inventory_path = root / "hash-inventory.json"
    seal_path = root / "candidate-seal.json"
    input_path = root / "verifier-input.json"
    for path in (manifest_path, inventory_path, seal_path, input_path):
        sealed_file(path)
    manifest = cast(dict[str, object], json.loads(manifest_path.read_text()))
    inventory = cast(dict[str, object], json.loads(inventory_path.read_text()))
    seal = cast(dict[str, object], json.loads(seal_path.read_text()))
    verifier_input = cast(dict[str, object], json.loads(input_path.read_text()))

    full = str(manifest["full_candidate_digest"])
    manifest_core = dict(manifest)
    del manifest_core["full_candidate_digest"]
    assert full == sha256(canonical(manifest_core))
    core = cast(dict[str, object], manifest["core_material"])
    assert manifest["core_candidate_digest"] == sha256(canonical(core))
    assert seal == {
        "schema": "task26-inode-test-fix-successor-seal-v1",
        "status": "PASS_SEALED_UNDEPLOYED",
        "full_candidate_digest": full,
        "core_candidate_digest": manifest["core_candidate_digest"],
        "manifest_sha256": sha256_file(manifest_path),
        "inventory_sha256": sha256_file(inventory_path),
        "wheel_sha256": EXPECTED_WHEEL_SHA256,
        "predecessor_full_digest": manifest["predecessor"]["full_digest"],
        "verifier_sha256": manifest["verifier_sha256"],
    }
    assert verifier_input == {
        "schema": "task26-inode-test-fix-independent-verifier-input-v1",
        "full_candidate_digest": full,
        "manifest_sha256": sha256_file(manifest_path),
        "seal_sha256": sha256_file(seal_path),
    }

    entries = cast(list[dict[str, object]], inventory["entries"])
    indexed = {str(row["path"]) for row in entries}
    actual = {path.relative_to(root).as_posix() for path in root.rglob("*") if path.is_file()}
    controls = CONTROLS & actual
    assert actual == indexed | controls, sorted(actual ^ (indexed | controls))
    assert len(indexed) == len(entries) == inventory["entry_count"]
    assert inventory["entries_sha256"] == sha256(canonical(entries))
    for row in entries:
        path = root / str(row["path"])
        sealed_file(path)
        assert path.stat().st_size == row["bytes"] and sha256_file(path) == row["sha256"]
    assert manifest["inventory_sha256"] == sha256_file(inventory_path)

    predecessor_info = cast(dict[str, object], manifest["predecessor"])
    predecessor = root / str(predecessor_info["preserved_path"])
    predecessor_files = [path for path in predecessor.rglob("*") if path.is_file()]
    assert len(predecessor_files) == predecessor_info["file_count"]
    assert sha256_file(predecessor / "candidate-manifest.json") == predecessor_info["manifest_sha256"]
    prior_manifest = json.loads((predecessor / "candidate-manifest.json").read_text())
    assert prior_manifest["full_candidate_digest"] == predecessor_info["full_digest"]
    subprocess.run(
        [sys.executable, "-B", str(predecessor / "verify_candidate.py"), str(predecessor)],
        check=True,
        stdout=subprocess.DEVNULL,
    )

    delta = cast(dict[str, object], json.loads((root / "bindings/test-delta.json").read_text()))
    assert delta["predecessor_full_digest"] == predecessor_info["full_digest"]
    assert delta["file_count"] == 1
    assert delta["files"] == [{
        "path": TEST_PATH,
        "change": "modified",
        "before_sha256": EXPECTED_OLD_TEST_SHA256,
        "after_sha256": EXPECTED_TEST_SHA256,
    }]
    assert sha256_file(root / "test-delta/before" / TEST_PATH) == EXPECTED_OLD_TEST_SHA256
    assert sha256_file(root / "test-delta/after" / TEST_PATH) == EXPECTED_TEST_SHA256
    assert sha256_file(root / "snapshot/tests/test_adaptive_nutrition.py") == EXPECTED_TEST_SHA256

    quality = cast(dict[str, object], json.loads((root / "artifacts/quality-summary.json").read_text()))
    assert quality["status"] == "PASS"
    assert quality["focused_overlay_umask_077"] == {"passed": 1, "failed": 0}
    assert quality["focused_overlay_umask_022"] == {"passed": 1, "failed": 0}
    assert quality["full_package_umask_077"] == {"passed": 679, "failed": 0}
    assert quality["fixture_mode_assertions"] is True
    assert quality["named_replacement_bytes_assertion"] is True
    assert quality["held_fd_original_bytes_assertion"] is True
    assert quality["reproducible_wheel_builds"] == 2
    assert quality["wheel_builds_byte_identical"] is True
    assert quality["wheel_unchanged_from_predecessor"] is True
    assert quality["wheel_source_parity"] is True
    assert quality["unindexed_count"] == 0

    wheel_info = cast(dict[str, object], manifest["profile_wheel"])
    wheel = root / str(wheel_info["path"])
    predecessor_wheel = predecessor / "artifacts/physique_checkin_cli-0.1.0-py3-none-any.whl"
    assert sha256_file(wheel) == sha256_file(predecessor_wheel) == EXPECTED_WHEEL_SHA256
    assert wheel.read_bytes() == predecessor_wheel.read_bytes()
    source_root = predecessor / "snapshot/package-runtime"
    with zipfile.ZipFile(wheel) as archive:
        infos = archive.infolist()
        assert len(infos) == wheel_info["member_count"] == 50
        assert {item.date_time for item in infos} == {(2000, 1, 1, 0, 0, 0)}
        package_members = {item.filename for item in infos if item.filename.startswith("checkin_cli/")}
        source_members = {
            path.relative_to(source_root).as_posix()
            for path in source_root.rglob("*")
            if path.is_file()
        }
        assert package_members == source_members and len(package_members) == wheel_info["package_member_count"] == 46
        for member in package_members:
            assert archive.read(member) == (source_root / member).read_bytes()
        assert not any(member.startswith("tests/") for member in archive.namelist())

    receipt_path = root / "independent-verification.json"
    if receipt_path.exists():
        sealed_file(receipt_path)
        receipt = json.loads(receipt_path.read_text())
        assert receipt["status"] == "PASS" and receipt["full_candidate_digest"] == full
        assert receipt["seal_sha256"] == sha256_file(seal_path)

    return {
        "schema": "task26-inode-test-fix-successor-verification-v1",
        "status": "PASS",
        "state": "PASS_SEALED_UNDEPLOYED",
        "full_candidate_digest": full,
        "core_candidate_digest": manifest["core_candidate_digest"],
        "wheel_sha256": EXPECTED_WHEEL_SHA256,
        "inventory_count": len(entries),
        "unindexed_count": 0,
        "predecessor_file_count": len(predecessor_files),
        "test_delta_count": 1,
        "wheel_member_count": 50,
        "source_parity_count": 46,
    }


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("root", type=Path)
    args = parser.parse_args()
    try:
        result = verify(args.root)
    except Exception as exc:
        print(json.dumps({"status": "FAIL", "error": str(exc)}, sort_keys=True))
        return 1
    print(json.dumps(result, sort_keys=True))
    return 0


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