#!/usr/bin/env python3
"""Create deterministic zip archives.

Usage:
  zip_deterministic.py <zip_path> <root_dir>

The archive will include the root directory itself and all files under it.
"""
from __future__ import annotations

import os
import sys
import time
import zipfile
from pathlib import Path


def _timestamp() -> tuple[int, int, int, int, int, int]:
    epoch = os.environ.get("SOURCE_DATE_EPOCH")
    if epoch:
        try:
            value = int(epoch)
            return time.gmtime(value)[:6]
        except Exception:
            pass
    return (2000, 1, 1, 0, 0, 0)


def _iter_files(root: Path):
    for dirpath, dirnames, filenames in os.walk(root):
        dirnames[:] = sorted([d for d in dirnames if d != "__pycache__"])
        for filename in sorted(filenames):
            if filename.endswith(".pyc"):
                continue
            yield Path(dirpath) / filename


def _add_file(zf: zipfile.ZipFile, file_path: Path, arcname: str) -> None:
    info = zipfile.ZipInfo(arcname, date_time=_timestamp())
    info.compress_type = zipfile.ZIP_DEFLATED
    info.external_attr = (0o644 & 0xFFFF) << 16
    with file_path.open("rb") as handle:
        data = handle.read()
    zf.writestr(info, data, compress_type=zipfile.ZIP_DEFLATED)


def main() -> int:
    if len(sys.argv) != 3:
        print("Usage: zip_deterministic.py <zip_path> <root_dir>")
        return 2

    zip_path = Path(sys.argv[1]).resolve()
    root = Path(sys.argv[2]).resolve()
    if not root.exists() or not root.is_dir():
        print(f"Missing root dir: {root}")
        return 2

    base = root.parent
    zip_path.parent.mkdir(parents=True, exist_ok=True)
    if zip_path.exists():
        zip_path.unlink()

    with zipfile.ZipFile(zip_path, "w") as zf:
        for file_path in sorted(_iter_files(root)):
            arcname = str(file_path.relative_to(base)).replace(os.sep, "/")
            _add_file(zf, file_path, arcname)

    print(str(zip_path))
    return 0


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