from __future__ import annotations

import hashlib
import json
from pathlib import Path


ROOT = Path(__file__).resolve().parent
PROJECT_RELATIVE_ROOT = "research/m3m-digital-twin-end-to-end"
METADATA = json.loads((ROOT / "paper-metadata.json").read_text(encoding="utf-8"))
REPORT_NUMBER = str(METADATA["reportNumber"])
VERSION = str(METADATA["version"])
PDF = ROOT / "paper.pdf"
ARCHIVE = ROOT / f"{REPORT_NUMBER}-v{VERSION}.zip"
OUTPUT = ROOT / "zenodo-request.json"
VERSIONED_OUTPUT = ROOT / f"zenodo-request-v{VERSION}.json"
EXPECTED_DOI = "10.5281/zenodo.21791470"
EXPECTED_PREVIOUS_RECORD_ID = 21789964
EXPECTED_PREVIOUS_DOI = "10.5281/zenodo.21789964"
EXPECTED_CONCEPT_DOI = "10.5281/zenodo.21789963"
EXPECTED_ZENODO_DESCRIPTION = (
    "本研究把 DJI Mavic 3 Multispectral 視為受官方規格、兩秒拍攝間隔、五檔資料完整性及輻射處理限制的工程系統。"
    "研究以 DJI 第一方文件、四個具 SHA-256 驗證的實測光譜響應檔、35 組合成航線情境及可重現程式，"
    "分析有效波段、航線可行域、缺檔拒絕與相對光譜低值現勘排序。研究未連接飛機、未執行真實飛行、"
    "未處理真實 Mavic 3M 影像，亦未建立病害、農藝成因、田間準確率、安全航速、法規適法性或商業效益。"
)


def digest_file(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 descriptor(path: Path, media_type: str) -> dict[str, object]:
    return {
        "name": path.name,
        "path": f"{PROJECT_RELATIVE_ROOT}/{path.name}",
        "mediaType": media_type,
        "bytes": path.stat().st_size,
        "sha256": digest_file(path),
    }


def main() -> None:
    for path in (PDF, ARCHIVE):
        if not path.is_file():
            raise FileNotFoundError(path)
    if VERSION != "1.1" or str(METADATA.get("doi")) != EXPECTED_DOI:
        raise RuntimeError("canonical metadata is not the approved v1.1 release")
    payload = json.loads(OUTPUT.read_text(encoding="utf-8"))
    identity_ok = all((
        payload.get("reportNumber") == REPORT_NUMBER,
        payload.get("slug") == METADATA.get("slug"),
        payload.get("version") == VERSION,
        payload.get("idempotencyKey") == f"{REPORT_NUMBER}@{VERSION}",
        payload.get("reservedDoi") == EXPECTED_DOI,
        payload.get("previousVersionRecordId") == EXPECTED_PREVIOUS_RECORD_ID,
        payload.get("previousVersionDoi") == EXPECTED_PREVIOUS_DOI,
        payload.get("previousConceptDoi") == EXPECTED_CONCEPT_DOI,
        payload.get("conceptDoi") == EXPECTED_CONCEPT_DOI,
        payload.get("title") == METADATA.get("title"),
        payload.get("description") == EXPECTED_ZENODO_DESCRIPTION,
    ))
    if not identity_ok:
        raise RuntimeError("refusing to update a Zenodo request with inconsistent v1.1 identity or version linkage")
    preserved = {
        key: payload.get(key)
        for key in (
            "title", "description", "rightsNote", "reservedDoi",
            "previousVersionRecordId", "previousVersionDoi", "previousConceptDoi", "conceptDoi",
        )
    }
    payload["files"] = [
        descriptor(PDF, "application/pdf"),
        descriptor(ARCHIVE, "application/zip"),
    ]
    serialized = json.dumps(payload, ensure_ascii=False, indent=2) + "\n"
    OUTPUT.write_text(serialized, encoding="utf-8")
    VERSIONED_OUTPUT.write_text(serialized, encoding="utf-8")
    reloaded = json.loads(OUTPUT.read_text(encoding="utf-8"))
    if any(reloaded.get(key) != value for key, value in preserved.items()):
        raise RuntimeError("Zenodo descriptive metadata or version linkage changed unexpectedly")
    versioned_reloaded = json.loads(VERSIONED_OUTPUT.read_text(encoding="utf-8"))
    if versioned_reloaded != reloaded:
        raise RuntimeError("versioned Zenodo request does not match the canonical request")
    print(json.dumps({"status": "PASSED", "outputs": [OUTPUT.name, VERSIONED_OUTPUT.name], "files": payload["files"]}, ensure_ascii=False))


if __name__ == "__main__":
    main()
