from __future__ import annotations

import hashlib
import json
import zipfile
from datetime import datetime
from pathlib import Path
from zoneinfo import ZoneInfo


ROOT = Path(__file__).resolve().parent
META = json.loads((ROOT / "paper-metadata.json").read_text(encoding="utf-8"))
VERSION = META["version"]
REPORT_NUMBER = META["reportNumber"]
ZIP = ROOT / f"{REPORT_NUMBER}-v{VERSION}.zip"
MANIFEST = ROOT / "release-manifest.json"

PAYLOAD_FILES = [
    "README.md",
    "paper.md",
    "paper.pdf",
    "paper-content.json",
    "paper-metadata.json",
    "CITATION.cff",
    "LICENSE.txt",
    "RIGHTS-NOTICE.txt",
    "source-registry.csv",
    "source-log.csv",
    "source-evidence.json",
    "codebook.csv",
    "cleaning-rules.md",
    "analysis-plan.md",
    "protocol.md",
    "flight-checklist.md",
    "aircraft-comparison.csv",
    "mvp-protocol.csv",
    "decision-thresholds.csv",
    "cost-model-template.csv",
    "data-dictionary.csv",
    "field-form.csv",
    "requirements.txt",
    "analysis.py",
    "build_artifacts.py",
    "validate.py",
    "verify_reproducibility.py",
    "package_release.py",
    "validate_package.py",
    "pdf-visual-qa.json",
    "validation-report.json",
    "reproducibility-check.json",
    "outputs/analysis-results.json",
    "outputs/protocol-workload.csv",
    "outputs/model-capability-matrix.csv",
    "outputs/normalized-cost-sensitivity.csv",
    "outputs/figures/figure-1-mvp-architecture.png",
    "outputs/figures/figure-2-aircraft-fit-matrix.png",
    "outputs/figures/figure-3-sampling-design.png",
    "outputs/figures/figure-4-evidence-ladder.png",
    "outputs/figures/figure-5-protocol-workload.png",
    "outputs/figures/figure-6-normalized-cost-sensitivity.png",
    "assets/fonts/NotoSansTC-Regular.ttf",
    "assets/fonts/OFL.txt",
]


def now_taipei() -> str:
    return datetime.now(ZoneInfo("Asia/Taipei")).replace(microsecond=0).isoformat()


def sha256(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 main() -> None:
    payload: list[Path] = []
    for relative in PAYLOAD_FILES:
        path = ROOT / relative
        if not path.is_file() or path.stat().st_size == 0:
            raise FileNotFoundError(f"Missing or empty allowlisted payload file: {relative}")
        payload.append(path)

    manifest = {
        "schemaVersion": 2,
        "reportNumber": REPORT_NUMBER,
        "version": VERSION,
        "doi": META.get("doi"),
        "createdAt": now_taipei(),
        "archiveTimestampPolicy": "Every ZIP member uses 1980-01-01 00:00:00; this normalized value is not a publication timestamp.",
        "licenseScope": "CC BY 4.0 applies only to author-licensable original content; DJI, government, academic and other third-party content retains original terms.",
        "studyBoundary": "Prospective protocol and public-evidence synthesis; fieldDataCollected=false and no observed performance, price, revenue or ROI is claimed.",
        "excluded": [
            "complete third-party HTML, manuals, PDFs, journal full text, images, screenshots and datasets",
            "credentials, cookies, tokens, private keys, browser state and caches",
            "landowner consent, cadastral data, names, phone numbers, precise coordinates and geotagged EXIF",
            "quotes, invoices and non-public field observations",
            "assets/schemas/cff-1.2.0-schema.json (local validator dependency; excluded to avoid unrelated schema example identifiers)",
            "the archive itself and post-package attestations",
        ],
        "files": [
            {"path": path.relative_to(ROOT).as_posix(), "bytes": path.stat().st_size, "sha256": sha256(path)}
            for path in payload
        ],
    }
    MANIFEST.write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + "\n", encoding="utf-8")

    archive_files = sorted([*payload, MANIFEST], key=lambda item: item.relative_to(ROOT).as_posix())
    with zipfile.ZipFile(ZIP, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9, strict_timestamps=True) as archive:
        for path in archive_files:
            relative = path.relative_to(ROOT).as_posix()
            info = zipfile.ZipInfo(relative, date_time=(1980, 1, 1, 0, 0, 0))
            info.create_system = 3
            info.compress_type = zipfile.ZIP_DEFLATED
            info.external_attr = 0o100644 << 16
            archive.writestr(info, path.read_bytes(), compress_type=zipfile.ZIP_DEFLATED, compresslevel=9)

    print(json.dumps({"zip": str(ZIP), "entries": len(archive_files), "bytes": ZIP.stat().st_size, "sha256": sha256(ZIP)}, ensure_ascii=False))


if __name__ == "__main__":
    main()
