#!/usr/bin/env python3
import hashlib
import json
import sys
from datetime import datetime, timezone
from pathlib import Path

PROJECT = Path(__file__).resolve().parents[1]
LOG = PROJECT / "log"
MANIFEST = PROJECT / "script/project-manifest.json"
COMPLETION = LOG / "completion-report.json"
MEDIA = LOG / "postiz-media.json"
SCHEDULE = LOG / "postiz-schedule.json"
TRANSCODE = LOG / "upload-transcode.json"
FINAL_RECEIPT = LOG / "final-render.json"
FOOTAGE_RECEIPT = LOG / "footage-job.json"
RECEIPT = LOG / "storage-cleanup.json"
FINAL = PROJECT / "output/final.mp4"
FOOTAGE = PROJECT / "output/footage/footage.mp4"
UPLOAD = PROJECT / "output/final-upload.mp4"
TARGET_INTEGRATIONS = {"cmrq10vod000hj7cbt8zvuj6a", "cmrpr0j9u000bj7cboqonlx4b"}


def load(path):
    if not path.exists():
        raise RuntimeError(f"missing {path.relative_to(PROJECT)}")
    return json.loads(path.read_text())


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


def receipt_hash(row):
    for key in ("output_sha256", "sha256"):
        if row.get(key):
            return row[key]
    verification = row.get("verification") or {}
    return verification.get("output_sha256") or verification.get("sha256")


def historical_row(path, receipt):
    verification = receipt.get("verification") or {}
    probe = receipt.get("independent_probe") or receipt.get("probe") or {}
    output_hash = receipt_hash(receipt)
    output_bytes = receipt.get("output_bytes") or receipt.get("bytes") or verification.get("output_bytes")
    if not output_bytes:
        output_bytes = ((probe.get("format") or {}).get("size"))
    if not output_hash or not output_bytes:
        raise RuntimeError(f"historical receipt incomplete for {path.relative_to(PROJECT)}")
    return {"path": str(path.relative_to(PROJECT)), "bytes": int(output_bytes), "sha256": output_hash}


def validate_schedule(schedule):
    if schedule.get("verified") is not True or schedule.get("status") != "completed":
        return False
    posts = schedule.get("posts") or {}
    if set(posts) != TARGET_INTEGRATIONS:
        return False
    timestamps = set()
    for integration in TARGET_INTEGRATIONS:
        rows = posts.get(integration) or []
        if len(rows) != 1 or rows[0].get("state") != "QUEUE":
            return False
        timestamps.add(rows[0].get("publishDate"))
    return len(timestamps) == 1 and None not in timestamps


def main():
    completion = load(COMPLETION)
    media = load(MEDIA)
    schedule = load(SCHEDULE)
    transcode = load(TRANSCODE)
    final_receipt = load(FINAL_RECEIPT)
    footage_receipt = load(FOOTAGE_RECEIPT)
    manifest = load(MANIFEST)
    expected_upload_hash = transcode.get("output_sha256")
    media_video = (media.get("media") or {}).get("video") or {}
    canon = completion.get("canon_sha256")
    gates = {
        "completion": completion.get("verified") is True and completion.get("checks_passed") == completion.get("checks_total"),
        "media": media.get("verified") is True and media.get("source_canon_sha256") == canon,
        "schedule": validate_schedule(schedule) and schedule.get("source_canon_sha256") == canon,
        "transcode": transcode.get("verified") is True and transcode.get("status") == "completed" and (transcode.get("server_verification") or {}).get("under_one_gb") is True,
        "upload_copy_exists": UPLOAD.exists(),
    }
    if not all(gates.values()):
        raise RuntimeError("cleanup gates failed: " + ", ".join(key for key, value in gates.items() if not value))
    upload_hash = digest(UPLOAD)
    upload_bytes = UPLOAD.stat().st_size
    if upload_bytes >= 1_000_000_000 or not expected_upload_hash or upload_hash != expected_upload_hash:
        raise RuntimeError("upload copy size/hash mismatch")
    if media_video.get("sha256") != upload_hash or int(media_video.get("bytes", -1)) != upload_bytes:
        raise RuntimeError("Postiz media receipt does not match upload copy")
    prior = json.loads(RECEIPT.read_text()) if RECEIPT.exists() else None
    if prior and prior.get("verified") is True and prior.get("status") == "completed":
        post = prior.get("post_delete") or {}
        if not FINAL.exists() and not FOOTAGE.exists() and UPLOAD.exists() and digest(UPLOAD) == upload_hash and post.get("upload_copy_sha256") == upload_hash:
            print(json.dumps({"verified": True, "status": "completed", "resumed": True, "freed_bytes": prior.get("freed_bytes")}, ensure_ascii=False))
            return 0
        raise RuntimeError("completed cleanup receipt conflicts with current files")
    deleted = [historical_row(FINAL, final_receipt), historical_row(FOOTAGE, footage_receipt)]
    if not FINAL.exists() or not FOOTAGE.exists():
        raise RuntimeError("master missing before cleanup without completed receipt")
    if FINAL.stat().st_size != deleted[0]["bytes"] or digest(FINAL) != deleted[0]["sha256"]:
        raise RuntimeError("final master does not match historical receipt")
    if FOOTAGE.stat().st_size != deleted[1]["bytes"] or digest(FOOTAGE) != deleted[1]["sha256"]:
        raise RuntimeError("footage master does not match historical receipt")
    started = datetime.now(timezone.utc).isoformat()
    running = {
        "version": 1, "verified": False, "status": "running", "project_id": PROJECT.name,
        "source_canon_sha256": canon, "gate_checks": gates, "deleted_candidates": deleted,
        "retained": {"path": str(UPLOAD.relative_to(PROJECT)), "bytes": upload_bytes, "sha256": upload_hash},
        "started_at": started,
    }
    RECEIPT.write_text(json.dumps(running, ensure_ascii=False, indent=2) + "\n")
    FINAL.unlink()
    FOOTAGE.unlink()
    upload_after = digest(UPLOAD) if UPLOAD.exists() else None
    post = {
        "final_master_exists": FINAL.exists(), "footage_master_exists": FOOTAGE.exists(),
        "upload_copy_exists": UPLOAD.exists(), "upload_copy_sha256": upload_after,
        "upload_copy_bytes": UPLOAD.stat().st_size if UPLOAD.exists() else None,
    }
    verified = not post["final_master_exists"] and not post["footage_master_exists"] and post["upload_copy_exists"] and upload_after == upload_hash
    if not verified:
        running.update({"status": "failed", "post_delete": post, "completed_at": datetime.now(timezone.utc).isoformat()})
        RECEIPT.write_text(json.dumps(running, ensure_ascii=False, indent=2) + "\n")
        raise RuntimeError("post-delete verification failed")
    running.update({"verified": True, "status": "completed", "freed_bytes": sum(row["bytes"] for row in deleted), "post_delete": post, "completed_at": datetime.now(timezone.utc).isoformat()})
    RECEIPT.write_text(json.dumps(running, ensure_ascii=False, indent=2) + "\n")
    manifest.setdefault("steps", {})["storage_cleanup"] = "completed"
    manifest["storage_cleanup"] = {"status": "completed", "verified": True, "receipt": "log/storage-cleanup.json", "freed_bytes": running["freed_bytes"]}
    manifest["updated_at"] = running["completed_at"]
    MANIFEST.write_text(json.dumps(manifest, ensure_ascii=False, indent=2) + "\n")
    print(json.dumps({"verified": True, "status": "completed", "freed_bytes": running["freed_bytes"], "retained": running["retained"]}, ensure_ascii=False))
    return 0


if __name__ == "__main__":
    try:
        raise SystemExit(main())
    except Exception as exc:
        print(f"Storage cleanup blocked: {exc}", file=sys.stderr)
        raise SystemExit(1)
