from __future__ import annotations

import hashlib
import json
import os
from pathlib import Path
import shutil
import zipfile

from release_check import check, included_files
ROOT = Path(__file__).resolve().parents[1]
RELEASE_ROOT = ROOT.parent
RELEASE_NAME = "AGENT_RELIABILITY_UPSTAGE_SUBMISSION_R5_20260718"
ZIP_PATH = RELEASE_ROOT / f"{RELEASE_NAME}.zip"
SIDECAR_PATH = RELEASE_ROOT / f"{RELEASE_NAME}.zip.sha256.json"
PDF_SOURCE = ROOT / "output" / "pdf" / "AGENT_RELIABILITY_PORTFOLIO_KO_R2_20260718.pdf"
PDF_PUBLIC = RELEASE_ROOT / PDF_SOURCE.name
FIRST_READ = RELEASE_ROOT / "00_READ_ME_FIRST_KO.md"
ROOT_LAUNCHER = RELEASE_ROOT / "START_HERE_UPSTAGE_LIVE_DEMO.cmd"


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


def has_strict_windows_line_endings(path: Path) -> bool:
    remainder = path.read_bytes().replace(b"\r\n", b"")
    return b"\r" not in remainder and b"\n" not in remainder


def resolve_pdf_source() -> Path:
    if PDF_SOURCE.is_file():
        return PDF_SOURCE
    if PDF_PUBLIC.is_file():
        return PDF_PUBLIC
    raise SystemExit("portfolio PDF is missing")


def main() -> None:
    release_check = check()
    if not release_check["passed"]:
        raise SystemExit("release check failed")
    pdf_source = resolve_pdf_source()
    if not FIRST_READ.is_file() or not ROOT_LAUNCHER.is_file():
        raise SystemExit("first-read guide or one-paste launcher is missing")
    if not has_strict_windows_line_endings(ROOT_LAUNCHER):
        raise SystemExit("root Windows launcher must use CRLF line endings")

    if pdf_source.resolve() != PDF_PUBLIC.resolve():
        shutil.copyfile(pdf_source, PDF_PUBLIC)
    temporary = ZIP_PATH.with_suffix(".zip.tmp")
    with zipfile.ZipFile(temporary, "w", compression=zipfile.ZIP_DEFLATED, compresslevel=9) as archive:
        archive.write(FIRST_READ, FIRST_READ.name)
        archive.write(ROOT_LAUNCHER, ROOT_LAUNCHER.name)
        archive.write(PDF_PUBLIC, PDF_PUBLIC.name)
        for path in included_files():
            relative = path.relative_to(ROOT)
            archive.write(path, (Path(ROOT.name) / relative).as_posix())
    os.replace(temporary, ZIP_PATH)

    with zipfile.ZipFile(ZIP_PATH) as archive:
        members = archive.infolist()
        unsafe_members = [item.filename for item in members if item.filename.startswith("/") or ".." in Path(item.filename).parts]
        bad_crc = archive.testzip()
    if unsafe_members or bad_crc:
        raise SystemExit(f"ZIP verification failed: unsafe={unsafe_members}, bad_crc={bad_crc}")

    sidecar = {
        "schema": "agent-reliability-live-demo.submission-package.v1",
        "package": {
            "filename": ZIP_PATH.name,
            "sha256": sha256(ZIP_PATH),
            "bytes": ZIP_PATH.stat().st_size,
            "member_count": len(members),
            "zip_crc_check": "PASS",
            "unsafe_member_count": 0,
        },
        "portfolio_pdf": {
            "filename": PDF_PUBLIC.name,
            "sha256": sha256(PDF_PUBLIC),
            "bytes": PDF_PUBLIC.stat().st_size,
        },
        "repository": {
            "included_tree_sha256": release_check["included_tree_sha256"],
            "included_file_count": release_check["included_file_count"],
            "release_check": "PASS",
        },
    }
    SIDECAR_PATH.write_text(json.dumps(sidecar, ensure_ascii=False, indent=2, sort_keys=True) + "\n", encoding="utf-8")
    report = dict(sidecar)
    report["local_launcher"] = {
        "filename": ROOT_LAUNCHER.name,
        "sha256": sha256(ROOT_LAUNCHER),
        "bytes": ROOT_LAUNCHER.stat().st_size,
        "requires_extracted_package": True,
    }
    print(json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True))


if __name__ == "__main__":
    main()
