#!/usr/bin/env python3
"""Create Neocodec's deterministic FFmpeg 9.0 corresponding-source archive."""

import hashlib
import io
import lzma
import os
import sys
import tarfile
from pathlib import Path, PurePosixPath

EPOCH = 1785797913
ARCHIVE_ROOT = "neocodec-ffmpeg-9.0-commercial-source"
DEFAULT_OUTPUT = "neocodec-ffmpeg-9.0-commercial-source.tar.xz"


def fail(message: str) -> None:
    raise SystemExit(f"package-corresponding-source: FAIL: {message}")


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


def expected_artifacts(source: Path) -> dict[str, str]:
    records: dict[str, str] = {}
    for line in (source / "SHA256SUMS").read_text(encoding="utf-8").splitlines():
        digest, separator, relative = line.partition("  ")
        if len(digest) != 64 or not separator or not relative:
            fail(f"invalid SHA256SUMS row: {line!r}")
        if PurePosixPath(relative).is_absolute() or ".." in PurePosixPath(relative).parts:
            fail(f"unsafe SHA256SUMS path: {relative}")
        if relative in records:
            fail(f"duplicate SHA256SUMS path: {relative}")
        records[relative] = digest
    return records


def normalized_info(name: str, *, directory: bool, size: int = 0) -> tarfile.TarInfo:
    info = tarfile.TarInfo(name)
    info.mtime = EPOCH
    info.uid = info.gid = 0
    info.uname = info.gname = "root"
    info.mode = 0o755 if directory else 0o644
    info.type = tarfile.DIRTYPE if directory else tarfile.REGTYPE
    info.size = 0 if directory else size
    return info


def main() -> None:
    source = Path(__file__).resolve().parent
    output = Path(sys.argv[1]).resolve() if len(sys.argv) == 2 else source.parent / DEFAULT_OUTPUT
    if len(sys.argv) > 2:
        fail("usage: package-corresponding-source.py [output.tar.xz]")

    artifacts = expected_artifacts(source)
    for relative, expected in artifacts.items():
        path = source / relative
        if not path.is_file() or path.is_symlink():
            fail(f"missing/non-regular source artifact: {relative}")
        actual = sha256(path)
        if actual != expected:
            fail(f"checksum mismatch for {relative}: expected {expected}, got {actual}")

    paths = sorted(
        path for path in source.rglob("*")
        if path.is_file() and ".git" not in path.parts and not path.name.startswith(".")
    )
    for path in paths:
        if path.is_symlink():
            fail(f"refusing symlink: {path.relative_to(source)}")

    output.parent.mkdir(parents=True, exist_ok=True)
    temporary = output.with_name(output.name + ".part")
    if temporary.exists():
        fail(f"temporary output already exists: {temporary}")
    try:
        with lzma.open(temporary, "wb", format=lzma.FORMAT_XZ, preset=9) as compressed:
            with tarfile.open(fileobj=compressed, mode="w|") as archive:
                archive.addfile(normalized_info(ARCHIVE_ROOT, directory=True))
                directories: set[str] = set()
                for path in paths:
                    relative = path.relative_to(source).as_posix()
                    parent = PurePosixPath(relative).parent
                    for depth in range(1, len(parent.parts) + 1):
                        directory = "/".join((ARCHIVE_ROOT, *parent.parts[:depth]))
                        if directory not in directories:
                            archive.addfile(normalized_info(directory, directory=True))
                            directories.add(directory)
                    data = path.read_bytes()
                    name = f"{ARCHIVE_ROOT}/{relative}"
                    info = normalized_info(name, directory=False, size=len(data))
                    if os.access(path, os.X_OK) or path.suffix == ".py":
                        info.mode = 0o755
                    archive.addfile(info, io.BytesIO(data))
        temporary.replace(output)
    finally:
        if temporary.exists():
            temporary.unlink()
    print(f"wrote {output} ({output.stat().st_size} bytes, sha256 {sha256(output)})")


if __name__ == "__main__":
    main()
