import hashlib
import json
import subprocess
import sys
import tarfile
from pathlib import Path


ROOT = Path(__file__).resolve().parents[1]
PACKAGER = ROOT / "scripts" / "build_release.py"
VERIFIER = ROOT / "scripts" / "verify_release.py"


def _fixture(root: Path) -> None:
    (root / "src").mkdir(parents=True)
    (root / "frontend").mkdir()
    (root / "scripts").mkdir()
    (root / "src" / "main.py").write_text("print('ready')\n")
    (root / "frontend" / "index.html").write_text("<h1>Stackchain</h1>\n")
    (root / "requirements.txt").write_text("fastapi==1.0\n")
    (root / "README.md").write_text("# Stackchain\n")
    (root / "scripts" / "rotate_unfiled_drafts.py").write_text("print('rotate')\n")


def _commit_fixture(source: Path) -> str:
    subprocess.run(["git", "init", "-q", str(source)], check=True)
    subprocess.run(["git", "-C", str(source), "config", "user.name", "Release Test"], check=True)
    subprocess.run(
        ["git", "-C", str(source), "config", "user.email", "release-test@example.invalid"],
        check=True,
    )
    subprocess.run(["git", "-C", str(source), "add", "."], check=True)
    subprocess.run(["git", "-C", str(source), "commit", "-qm", "fixture"], check=True)
    return subprocess.run(
        ["git", "-C", str(source), "rev-parse", "HEAD"],
        text=True,
        capture_output=True,
        check=True,
    ).stdout.strip()


def _build(source: Path, output: Path, commit: str) -> subprocess.CompletedProcess[str]:
    return subprocess.run(
        [
            sys.executable,
            str(PACKAGER),
            "--root",
            str(source),
            "--output-dir",
            str(output),
            "--commit",
            commit,
            "--source-date-epoch",
            "1720000000",
        ],
        text=True,
        capture_output=True,
        check=False,
    )


def test_release_bundle_is_reproducible_from_declared_commit(tmp_path):
    source = tmp_path / "source"
    _fixture(source)
    commit = _commit_fixture(source)

    first = _build(source, tmp_path / "first", commit)
    assert first.returncode == 0, first.stderr

    (source / "src" / "__pycache__").mkdir()
    (source / "src" / "__pycache__" / "main.cpython-311.pyc").write_bytes(b"bytecode")
    (source / "src" / "untracked-secret.txt").write_text("must not ship")
    (source / "src" / "main.py").write_text("print('dirty worktree')\n")
    second = _build(source, tmp_path / "second", commit)

    assert second.returncode == 0, second.stderr
    first_archive = next((tmp_path / "first").glob("*.tar.gz"))
    second_archive = next((tmp_path / "second").glob("*.tar.gz"))
    assert first_archive.read_bytes() == second_archive.read_bytes()

    manifest = json.loads(next((tmp_path / "first").glob("*.manifest.json")).read_text())
    checksum = next((tmp_path / "first").glob("*.sha256")).read_text().split()[0]
    assert manifest["commit"] == commit
    assert manifest["artifact"]["sha256"] == checksum
    assert checksum == hashlib.sha256(first_archive.read_bytes()).hexdigest()
    assert sorted(manifest["files"]) == [
        "README.md",
        "frontend/index.html",
        "requirements.txt",
        "scripts/rotate_unfiled_drafts.py",
        "src/main.py",
    ]
    with tarfile.open(first_archive, "r:gz") as archive:
        names = archive.getnames()
        embedded = json.load(archive.extractfile("release-manifest.json"))
    assert names == [
        "README.md",
        "frontend/index.html",
        "release-manifest.json",
        "requirements.txt",
        "scripts/rotate_unfiled_drafts.py",
        "src/main.py",
    ]
    assert embedded["commit"] == commit
    assert embedded["files"] == manifest["files"]


def test_release_bundle_rejects_unknown_commit(tmp_path):
    source = tmp_path / "source"
    _fixture(source)
    _commit_fixture(source)

    built = _build(source, tmp_path / "dist", "a" * 40)

    assert built.returncode != 0
    assert "declared commit is unavailable" in built.stderr


def test_release_bundle_verifier_enforces_integrity(tmp_path):
    source = tmp_path / "source"
    output = tmp_path / "dist"
    _fixture(source)
    commit = _commit_fixture(source)
    built = _build(source, output, commit)
    assert built.returncode == 0, built.stderr

    valid = subprocess.run(
        [
            sys.executable,
            str(VERIFIER),
            "--input-dir",
            str(output),
            "--commit",
            commit,
            "--repository",
            str(source),
        ],
        text=True,
        capture_output=True,
        check=False,
    )
    assert valid.returncode == 0, valid.stderr

    archive = next(output.glob("*.tar.gz"))
    archive.write_bytes(archive.read_bytes() + b"tampered")
    tampered = subprocess.run(
        [
            sys.executable,
            str(VERIFIER),
            "--input-dir",
            str(output),
            "--commit",
            commit,
            "--repository",
            str(source),
        ],
        text=True,
        capture_output=True,
        check=False,
    )
    assert tampered.returncode != 0
