#!/usr/bin/env python3
"""Run Ty from one exact corrected wheel in an isolated offline runtime."""
from __future__ import annotations

import argparse
import base64
import csv
import hashlib
import io
import json
import os
import shutil
import stat
import subprocess
import sys
import tempfile
import zipfile
from pathlib import Path

VERSION = "0.0.21"
SCRIPT_MEMBER = f"ty-{VERSION}.data/scripts/ty"
RECORD_MEMBER = f"ty-{VERSION}.dist-info/RECORD"
HEX = set("0123456789abcdef")
TARGET_PYTHON_VERSION = "3.12"
EXPECTED_RAW_DIAGNOSTICS_SHA256 = "d01195a6c0de359b77948625d1e97c35cffbc7dcd145daaa4eed1a5c252ce063"
EXPECTED_DIAGNOSTICS_FINGERPRINT_SHA256 = "ba03fe2e484683a8bdba0121f014caf614891c84da06a674a25314da1fce658a"
TY_COMMAND_ARGV = [
    "sealed-ty",
    "check",
    "gateway/platforms/nutrition_coaching.py",
    "--output-format",
    "gitlab",
    "--python-version",
    TARGET_PYTHON_VERSION,
]


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


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


def file_sha(path: Path) -> str:
    digest = hashlib.sha256()
    with path.open("rb") as stream:
        for chunk in iter(lambda: stream.read(1024 * 1024), b""):
            digest.update(chunk)
    return digest.hexdigest()


def private_file(path: Path, label: str) -> None:
    info = path.lstat()
    if (
        stat.S_ISLNK(info.st_mode)
        or not stat.S_ISREG(info.st_mode)
        or info.st_uid != os.geteuid()
        or info.st_nlink != 1
        or stat.S_IMODE(info.st_mode) not in {0o600, 0o400}
    ):
        raise ValueError(f"{label} is not a private regular file")


def decode_hash(value: str) -> str:
    algorithm, separator, encoded = value.partition("=")
    if algorithm != "sha256" or not separator:
        raise ValueError("Ty RECORD hash is invalid")
    raw = base64.urlsafe_b64decode(encoded + "=" * (-len(encoded) % 4))
    if len(raw) != 32:
        raise ValueError("Ty RECORD hash is invalid")
    return raw.hex()


def inspect_wheel(path: Path, expected_wheel: str, expected_executable: str) -> dict[str, object]:
    private_file(path, "corrected Ty wheel")
    if file_sha(path) != expected_wheel:
        raise ValueError("corrected Ty wheel hash differs")
    try:
        with zipfile.ZipFile(path) as archive:
            names = archive.namelist()
            if len(names) != len(set(names)) or SCRIPT_MEMBER not in names or RECORD_MEMBER not in names:
                raise ValueError("corrected Ty wheel inventory is invalid")
            member = archive.read(SCRIPT_MEMBER)
            info = archive.getinfo(SCRIPT_MEMBER)
            record = archive.read(RECORD_MEMBER)
            payloads = {name: archive.read(name) for name in names}
    except zipfile.BadZipFile as exc:
        raise ValueError("corrected Ty wheel is invalid") from exc
    if sha(member) != expected_executable or info.external_attr >> 16 & 0o111 != 0o111:
        raise ValueError("corrected Ty executable member differs")
    rows = list(csv.reader(io.StringIO(record.decode(), newline="")))
    seen: set[str] = set()
    for row in rows:
        if len(row) != 3 or row[0] in seen:
            raise ValueError("corrected Ty RECORD row is invalid")
        seen.add(row[0])
        if row[0] == RECORD_MEMBER:
            if row[1:] != ["", ""]:
                raise ValueError("corrected Ty RECORD self row is invalid")
        else:
            payload = payloads.get(row[0])
            if payload is None or decode_hash(row[1]) != sha(payload) or row[2] != str(len(payload)):
                raise ValueError("corrected Ty RECORD binding differs")
    if seen != set(names):
        raise ValueError("corrected Ty RECORD inventory differs")
    return {
        "wheel_sha256": expected_wheel,
        "wheel_record_sha256": sha(record),
        "wheel_member": SCRIPT_MEMBER,
        "wheel_member_sha256": expected_executable,
        "wheel_member_size": len(member),
    }


def environment() -> dict[str, str]:
    result = {
        key: value
        for key, value in os.environ.items()
        if not key.startswith(("PYTHON", "PIP")) and key not in {"PATH", "VIRTUAL_ENV"}
    }
    result.update(
        {
            "PATH": os.defpath,
            "PYTHONNOUSERSITE": "1",
            "PIP_NO_INDEX": "1",
            "PIP_CONFIG_FILE": os.devnull,
            "LC_ALL": "C",
            "LANG": "C",
        }
    )
    return result


def run(args: argparse.Namespace) -> dict[str, object]:
    if list(sys.version_info[:2]) != [3, 12]:
        raise ValueError("qualification runtime interpreter must be Python 3.12")
    wheel = args.ty_wheel.absolute()
    source = args.source.resolve(strict=True)
    gate = args.gate_tool.resolve(strict=True)
    expected_wheel = args.expected_wheel_sha256
    expected_executable = args.expected_executable_sha256
    if not all(len(value) == 64 and set(value) <= HEX for value in (expected_wheel, expected_executable)):
        raise ValueError("expected Ty hashes are invalid")
    wheel_receipt = inspect_wheel(wheel, expected_wheel, expected_executable)
    output = args.output_directory.absolute()
    output.mkdir(mode=0o700, parents=True)
    output.chmod(0o700)
    temporary = Path(tempfile.mkdtemp(prefix="task26-hermetic-ty-"))
    temporary.chmod(0o700)
    try:
        cwd = temporary / "cwd"
        cwd.mkdir(mode=0o700)
        analysis_source = cwd / "gateway/platforms/nutrition_coaching.py"
        analysis_source.parent.mkdir(mode=0o700, parents=True)
        analysis_source.write_bytes(source.read_bytes())
        analysis_source.chmod(0o600)
        analysis_relative = analysis_source.relative_to(cwd).as_posix()
        venv = temporary / "venv"
        env = environment()
        subprocess.run([sys.executable, "-I", "-m", "venv", str(venv)], cwd=cwd, env=env, check=True, capture_output=True)
        subprocess.run(
            [str(venv / "bin/python"), "-I", "-m", "pip", "install", "--no-index", "--no-deps", "--no-compile", str(wheel)],
            cwd=cwd,
            env=env,
            check=True,
            capture_output=True,
        )
        executable = venv / "bin/ty"
        executable_info = executable.lstat()
        if not stat.S_ISREG(executable_info.st_mode) or executable_info.st_nlink != 1 or file_sha(executable) != expected_executable:
            raise ValueError("installed Ty executable provenance differs")
        version = subprocess.run([str(executable), "--version"], cwd=cwd, env=env, text=True, capture_output=True, check=True).stdout.strip()
        if version != f"ty {VERSION}":
            raise ValueError("installed Ty version differs")
        site = next((venv / "lib").glob("python*/site-packages"))
        dist_info = site / f"ty-{VERSION}.dist-info"
        record_path = dist_info / "RECORD"
        record_bytes = record_path.read_bytes()
        record_rows = list(csv.reader(io.StringIO(record_bytes.decode(), newline="")))
        executable_relative = Path(os.path.relpath(executable, site)).as_posix()
        matches = [row for row in record_rows if len(row) == 3 and row[0] == executable_relative]
        if len(matches) != 1 or decode_hash(matches[0][1]) != expected_executable or matches[0][2] != str(executable.stat().st_size):
            raise ValueError("installed Ty RECORD executable binding differs")
        actual_ty_command = [
            str(executable),
            "check",
            analysis_relative,
            "--output-format",
            "gitlab",
            "--python-version",
            TARGET_PYTHON_VERSION,
        ]
        if actual_ty_command.count("--python-version") != 1:
            raise ValueError("Ty semantic target flag cardinality is invalid")
        completed = subprocess.run(
            actual_ty_command,
            cwd=cwd,
            env=env,
            capture_output=True,
            check=False,
            timeout=180,
        )
        if completed.returncode not in {0, 1}:
            raise ValueError("hermetic Ty execution failed")
        raw = completed.stdout
        parsed = json.loads(raw)
        if not isinstance(parsed, list) or not parsed:
            raise ValueError("hermetic Ty diagnostics are empty or invalid")
        raw_path = output / "ty-diagnostics.gitlab.json"
        raw_path.write_bytes(raw)
        raw_path.chmod(0o600)
        semantic_projection = sorted(parsed, key=lambda row: canonical(row))
        semantic_fingerprint = sha(canonical(semantic_projection))
        if (
            sha(raw) != EXPECTED_RAW_DIAGNOSTICS_SHA256
            or semantic_fingerprint
            != EXPECTED_DIAGNOSTICS_FINGERPRINT_SHA256
        ):
            raise ValueError("hermetic Ty diagnostics differ from qualified 3.12 semantics")
        qualification_interpreter = {
            "executable_role": "selected_qualification_python",
            "version": list(sys.version_info[:3]),
        }
        provenance: dict[str, object] = {
            "schema": "task26-hermetic-ty-execution-v2",
            "runner_tool_sha256": file_sha(Path(__file__)),
            "gate_tool_sha256": file_sha(gate),
            **wheel_receipt,
            "ty_version": version,
            "qualification_interpreter": qualification_interpreter,
            "target_python_version": TARGET_PYTHON_VERSION,
            "ty_command_argv": TY_COMMAND_ARGV,
            "installed_distribution_origin": str(dist_info.relative_to(venv)),
            "installed_record_sha256": sha(record_bytes),
            "installed_executable_relative_path": str(executable.relative_to(venv)),
            "installed_executable_sha256": expected_executable,
            "installed_executable_size": executable.stat().st_size,
            "execution": {"private_empty_cwd": True, "pythonpath_inherited": False, "ambient_path_used": False},
            "raw_diagnostics_sha256": sha(raw),
            "raw_diagnostics_size": len(raw),
            "raw_diagnostics_count": len(parsed),
            "diagnostics_semantic_fingerprint_sha256": semantic_fingerprint,
        }
        provenance["provenance_sha256"] = sha(canonical(provenance))
        provenance_path = output / "ty-execution-provenance.json"
        provenance_path.write_bytes(canonical(provenance) + b"\n")
        provenance_path.chmod(0o600)
        receipt_path = output / "ty-surface-receipt.json"
        gate_completed = subprocess.run(
            [sys.executable, "-I", str(gate), "--source", str(analysis_source), "--diagnostics-json", str(raw_path), "--execution-provenance", str(provenance_path), "--receipt", str(receipt_path)],
            cwd=cwd,
            env=env,
            text=True,
            capture_output=True,
            check=False,
            timeout=60,
        )
        if gate_completed.returncode != 0:
            raise ValueError("hermetic Ty capability gate failed")
        receipt = json.loads(receipt_path.read_bytes())
        if (
            receipt.get("target_python_version") != TARGET_PYTHON_VERSION
            or receipt.get("qualification_interpreter")
            != qualification_interpreter
            or receipt.get("ty_command_argv") != TY_COMMAND_ARGV
            or receipt.get("diagnostics_semantic_fingerprint_sha256")
            != provenance["diagnostics_semantic_fingerprint_sha256"]
        ):
            raise ValueError("Ty surface semantic target differs")
        return {
            "schema": "task26-hermetic-ty-attestation-v2",
            "ty_version": version,
            "qualification_interpreter": qualification_interpreter,
            "target_python_version": TARGET_PYTHON_VERSION,
            "ty_command_argv": TY_COMMAND_ARGV,
            "ty_wheel_sha256": expected_wheel,
            "installed_executable_sha256": expected_executable,
            "runner_tool_sha256": provenance["runner_tool_sha256"],
            "gate_tool_sha256": provenance["gate_tool_sha256"],
            "raw_diagnostics_sha256": sha(raw),
            "raw_diagnostics_count": len(parsed),
            "diagnostics_semantic_fingerprint_sha256": provenance[
                "diagnostics_semantic_fingerprint_sha256"
            ],
            "execution_provenance": provenance,
            "surface_receipt_sha256": receipt.get("receipt_sha256"),
            "surface_receipt": receipt,
        }
    finally:
        if temporary.exists():
            for path in temporary.rglob("*"):
                try:
                    if not path.is_symlink():
                        path.chmod(0o700 if path.is_dir() else 0o600)
                except OSError:
                    pass
            shutil.rmtree(temporary)


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--source", type=Path, required=True)
    parser.add_argument("--ty-wheel", type=Path, required=True)
    parser.add_argument("--expected-wheel-sha256", required=True)
    parser.add_argument("--expected-executable-sha256", required=True)
    parser.add_argument("--gate-tool", type=Path, required=True)
    parser.add_argument("--output-directory", type=Path, required=True)
    args = parser.parse_args()
    try:
        result = run(args)
    except (OSError, ValueError, subprocess.SubprocessError, json.JSONDecodeError) as exc:
        print(json.dumps({"status": "TASK26_HERMETIC_TY_FAIL", "reason": str(exc)}, sort_keys=True), file=sys.stderr)
        return 1
    print(json.dumps(result, sort_keys=True, separators=(",", ":")))
    return 0


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