"""One-shot: download ephemeral Instagram CDN media for insta_* entries
into C:\\fearx-photos\\insta\\<thread_id>\\, then rewrite URLs to the
permanent fearx-photos.sbxg.win path. sync.py remains untouched.
"""
import json
import os
import shutil
import sys
import time
import urllib.request
from pathlib import Path

if hasattr(sys.stdout, "reconfigure"):
    sys.stdout.reconfigure(encoding="utf-8")

ROOT = Path(r"C:\fearx-photos\insta")
META = Path(r"C:\fearx-photos\sync\insta_meta.json")
BACKUP = Path(r"C:\fearx-photos\sync\insta_meta.json.bak.backfill")
PERMANENT_BASE = "https://fearx-photos.sbxg.win/insta"
UA = (
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) "
    "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0 Safari/537.36"
)
SLEEP = 0.4


def fetch(url: str, dest: Path, expected_ct_prefix: str | None = None) -> tuple[bool, str]:
    if dest.exists() and dest.stat().st_size > 0:
        return True, "skip-existing"
    part = dest.with_suffix(dest.suffix + ".part")
    req = urllib.request.Request(url, headers={"User-Agent": UA})
    try:
        with urllib.request.urlopen(req, timeout=30) as resp:
            expected = resp.headers.get("Content-Length")
            with open(part, "wb") as f:
                shutil.copyfileobj(resp, f, length=64 * 1024)
        size = part.stat().st_size
        if expected and int(expected) != size:
            part.unlink(missing_ok=True)
            return False, f"size-mismatch exp={expected} got={size}"
        if size == 0:
            part.unlink(missing_ok=True)
            return False, "zero-size"
        os.replace(part, dest)
        return True, f"ok {size}B"
    except Exception as e:
        if part.exists():
            part.unlink(missing_ok=True)
        return False, f"ERR {type(e).__name__}: {str(e)[:100]}"


def main() -> int:
    if not BACKUP.exists():
        shutil.copy2(META, BACKUP)
        print(f"backup written: {BACKUP}")
    else:
        print(f"backup already exists: {BACKUP}")

    data = json.loads(META.read_text(encoding="utf-8"))
    targets = [e for e in data if str(e.get("thread_id", "")).startswith("insta_")]
    print(f"insta_* entries: {len(targets)}")

    img_ok = img_fail = vid_ok = vid_fail = 0
    fails: list[str] = []

    for idx, entry in enumerate(targets, 1):
        tid = entry["thread_id"]
        folder = ROOT / tid
        folder.mkdir(parents=True, exist_ok=True)

        new_imgs, new_vids = [], []

        for i, url in enumerate(entry.get("image_urls") or []):
            dest = folder / f"{i}.jpg"
            ok, msg = fetch(url, dest)
            if ok:
                img_ok += 1
            else:
                img_fail += 1
                fails.append(f"{tid}/{i}.jpg {msg}")
            new_imgs.append(f"{PERMANENT_BASE}/{tid}/{i}.jpg")
            time.sleep(SLEEP)

        for i, url in enumerate(entry.get("video_urls") or []):
            dest = folder / f"v{i}.mp4"
            ok, msg = fetch(url, dest)
            if ok:
                vid_ok += 1
            else:
                vid_fail += 1
                fails.append(f"{tid}/v{i}.mp4 {msg}")
            new_vids.append(f"{PERMANENT_BASE}/{tid}/v{i}.mp4")
            time.sleep(SLEEP)

        entry["image_urls"] = new_imgs
        entry["video_urls"] = new_vids

        if idx % 10 == 0 or idx == len(targets):
            print(f"  [{idx}/{len(targets)}] imgs ok={img_ok}/fail={img_fail} vids ok={vid_ok}/fail={vid_fail}")

    # Atomic save
    tmp = META.with_suffix(".json.writing")
    tmp.write_text(
        json.dumps(data, ensure_ascii=False, indent=2), encoding="utf-8"
    )
    os.replace(tmp, META)

    print("=== summary ===")
    print(f"images: ok={img_ok} fail={img_fail}")
    print(f"videos: ok={vid_ok} fail={vid_fail}")
    if fails:
        print("--- failures (first 20) ---")
        for line in fails[:20]:
            print(" ", line)
    print("meta written with permanent URLs.")
    return 0 if (img_fail == 0 and vid_fail == 0) else 1


if __name__ == "__main__":
    sys.exit(main())
