#!/usr/bin/env python3
"""Carousel IG TS Law "Mitos Perjanjian yang perlu Anda pahami" (6 slide, kiriman Bos 23 Sep 2026).

Aturan tetap:
  - Carousel = type "album", jam 09:00 WIB (02:00Z).
  - 1 post/hari/akun -> slot pertama yang bebas dipakai (IG TS Law 25 Sep 11:00 sudah terisi seri
    "Mitos atau Fakta", jadi carousel ini masuk 24 Sep 09:00 WIB).
  - Semua konten IG TS Law ikut ke FB + Threads (perintah 18 Sep).
    Threads 09:00 WIB (caption <=500 char), FB 19:30 WIB (caption = caption IG).
  - Caption: sesuai isi gambar, hashtag huruf kecil, 0 mention, replies=[].
  - Media: openclaw-img/tslaw_mitos_perjanjian_01..06.jpg (JPEG q90), urutan = urutan file Bos.
"""
import os
import re
import sys
import time
import urllib.request
from datetime import datetime, timezone, timedelta

from PIL import Image, ImageStat

WS = "/home/mantapman/.hermes/profiles/atun/workspace"
sys.path.insert(0, WS)
from repliz_client import get_client  # noqa: E402

SRC_DIR = os.path.join(WS, "tslaw_mitos_perjanjian")
IMG_DIR = os.path.join(WS, "openclaw-img")
BASE = "https://mantapserver.tail3947bb.ts.net/openclaw-img/"
PREFIX = "tslaw_mitos_perjanjian"
N = 6

IG = "6a76a54d62caae1e04f30968"
THREADS = "6a35f8b7c5ff4ce3a322b2ed"
FB = "6a3b41d1c5ff4ce3a32753e8"

WIB = timezone(timedelta(hours=7))
TGL = "2026-09-24"          # hari slot (WIB)
JAM_IG_THREADS = (9, 0)     # 02:00Z
JAM_FB = (19, 30)           # 12:30Z
TOPIC = "Mitos atau Fakta Hukum"
JUDUL = "TS Law — Carousel Mitos Perjanjian yang Perlu Anda Pahami"

CTA = ("Butuh pendampingan hukum? Konsultasikan masalah hukum Anda dengan Tito Suprianto Law Firm: "
       "https://tslaw.id\n\U0001f4f2 WhatsApp: +62 819-0922-2026")

CAP_IG = (
    "Perjanjian lisan tidak sah? Tidak selalu.\n\n"
    "Banyak yang menganggap kesepakatan yang tidak ditulis di atas kertas otomatis batal. Padahal selama para "
    "pihak sepakat, cakap hukum, objeknya jelas, dan sebabnya tidak terlarang, kesepakatan itu bisa mengikat "
    "(Pasal 1320 KUH Perdata). Yang lebih berat memang soal pembuktiannya.\n\n"
    "Begitu juga chat atau WhatsApp. Percakapan elektronik bisa membantu pembuktian, tapi konteks, keaslian, dan "
    "keterkaitannya dengan perkara tetap harus bisa Anda tunjukkan.\n\n"
    "Carousel ini membahas dua mitos yang paling sering muncul soal perjanjian.\n\n"
    "Menurut Anda, kesepakatan lisan jenis apa yang paling berisiko kalau tidak didokumentasikan? Tulis di "
    "komentar. \U0001f447"
)

CAP_THREADS = (
    "Perjanjian lisan tidak sah? Tidak selalu.\n\n"
    "Kalau para pihak sepakat, cakap hukum, objeknya jelas, dan sebabnya tidak terlarang, kesepakatan lisan bisa "
    "mengikat (Pasal 1320 KUH Perdata). Pembuktiannya yang lebih berat.\n\n"
    "Chat atau WhatsApp juga bisa jadi bukti selama konteks dan keasliannya bisa ditunjukkan.\n\n"
    "Kesepakatan lisan apa yang menurut Anda paling berisiko tidak didokumentasikan?"
)

TAG_IG = ["#tslaw", "#titosuprantolawfirm", "#hukumindonesia", "#edukasihukum", "#literasihukum",
          "#konsultasihukum", "#advokatindonesia", "#hakanda", "#mitosataufakta", "#hukumperdata",
          "#perjanjian", "#pembuktian"]

TAIL_TH = "Konsultasi: https://tslaw.id · WA +62 819-0922-2026"
TAG_TH = ["#tslaw", "#mitosataufakta", "#hukumperdata", "#literasihukum"]


def siapkan_media():
    """PNG kiriman Bos -> JPEG q90 di openclaw-img/, lalu cek 200 di funnel."""
    urls = []
    for i in range(1, N + 1):
        src = os.path.join(SRC_DIR, f"{i}.png")
        dst = os.path.join(IMG_DIR, f"{PREFIX}_{i:02d}.jpg")
        im = Image.open(src).convert("RGB")
        im.save(dst, format="JPEG", quality=90)
        url = f"{BASE}{PREFIX}_{i:02d}.jpg"
        req = urllib.request.Request(url, method="GET", headers={"User-Agent": "curl/8"})
        with urllib.request.urlopen(req, timeout=45) as r:
            code = r.status
            blob = r.read()
        assert code == 200, f"{url} -> {code}"
        Image.open(__import__("io").BytesIO(blob)).verify()
        urls.append(url)
        print(f"  ✅ slide {i:02d}: {os.path.basename(dst)} | {len(blob)//1024} KB | HTTP {code}")
    return urls


def cek_urutan():
    """Matriks beda-pixel: bukti slide terpasang = file kiriman Bos, tidak tertukar."""
    kecil = lambda p: Image.open(p).convert("L").resize((300, 375))
    mat = []
    for i in range(1, N + 1):
        a = kecil(os.path.join(IMG_DIR, f"{PREFIX}_{i:02d}.jpg"))
        baris = []
        for j in range(1, N + 1):
            b = kecil(os.path.join(SRC_DIR, f"{j}.png"))
            from PIL import ImageChops
            baris.append(round(ImageStat.Stat(ImageChops.difference(a, b)).mean[0], 2))
        mat.append(baris)
    ok = True
    for i, baris in enumerate(mat, 1):
        pilih = baris.index(min(baris)) + 1
        off = sorted(baris)[1]
        # ambang RELATIF: pasangan benar jauh lebih kecil dari kandidat lain
        tanda = "OK " if pilih == i and min(baris) < 1 and off > 10 * max(min(baris), 0.01) else "CEK"
        ok = ok and tanda == "OK "
        print(f"  {tanda} slide {i:02d} <- file {pilih}.png | diff {min(baris)} (terdekat lain {off})")
    return ok, mat


def all_docs(c):
    docs = []
    for p in range(1, 80):
        r = None
        for a in range(8):
            try:
                r = c.get_schedules(page=p, limit=100)
                break
            except Exception as e:
                print("   retry list:", e)
                time.sleep(5 + 3 * a)
        if r is None:
            break
        b = r.get("docs", [])
        if not b:
            break
        docs += b
        if len(b) < 100:
            break
    return docs


def at_utc(hari, jam, menit):
    return (datetime.fromisoformat(f"{hari}T{jam:02d}:{menit:02d}:00+07:00")
            .astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.000Z"))


def guard(cap, label):
    salah = []
    if [t for t in re.findall(r"#(\w+)", cap) if t != t.lower()]:
        salah.append("hashtag ada huruf besar")
    if re.search(r"@\w+", cap):
        salah.append("ada mention")
    if label == "threads" and len(cap) > 500:
        salah.append(f"{len(cap)} char (>500)")
    return salah


def main():
    go = "--go" in sys.argv
    print("== 1. siapkan media ==")
    urls = siapkan_media()

    print("\n== 2. cek urutan slide (matriks beda-pixel) ==")
    ok, _ = cek_urutan()
    assert ok, "urutan slide tidak terbukti — stop"

    cap_ig = f"{CAP_IG}\n\n{CTA}\n\n{' '.join(TAG_IG)}"
    cap_fb = cap_ig
    cap_th = f"{CAP_THREADS}\n\n{TAIL_TH}\n\n{' '.join(TAG_TH)}"

    print("\n== 3. cek caption ==")
    masalah = []
    for label, cap in (("ig", cap_ig), ("fb", cap_fb), ("threads", cap_th)):
        for m in guard(cap, label):
            masalah.append(f"{label}: {m}")
        print(f"  {label:8s}: {len(cap)} char")
    if masalah:
        print("  ❗", "; ".join(masalah))
        return
    assert "\u2014" not in cap_ig and "\u201c" not in cap_ig, "em dash/curly quote"

    medias = [{"url": u, "type": "image", "alt": f"{PREFIX}_{i:02d}.jpg"} for i, u in enumerate(urls, 1)]
    target = [
        ("IG", IG, cap_ig, at_utc(TGL, *JAM_IG_THREADS)),
        ("THREADS", THREADS, cap_th, at_utc(TGL, *JAM_IG_THREADS)),
        ("FB", FB, cap_fb, at_utc(TGL, *JAM_FB)),
    ]
    for label, aid, cap, at in target:
        print(f"  {label:8s} {datetime.fromisoformat(at.replace('Z', '+00:00')).astimezone(WIB):%d %b %H:%M} WIB")

    if not go:
        print("\n(dry-run — tambahkan --go)")
        return

    print("\n== 4. cek idempoten + create ==")
    c = get_client()
    docs = all_docs(c)
    terpasang = set()
    for d in docs:
        for m in (d.get("medias") or []):
            fn = ((m or {}).get("url") or "").split("/")[-1]
            if fn.startswith(PREFIX):
                terpasang.add((d.get("accountId"), fn))

    rows = []
    for label, aid, cap, at in target:
        if (aid, f"{PREFIX}_01.jpg") in terpasang:
            print(f"  = {label}: media sudah terpasang di akun ini — SKIP")
            continue
        sid = None
        for attempt in range(1, 5):
            try:
                res = c.create_schedule(aid, cap, at, title=JUDUL, topic=TOPIC, type_="album",
                                        medias=medias, replies=[])
                sid = res.get("scheduleId") or res.get("_id") or res.get("id")
                break
            except Exception as e:
                print(f"    ⚠️ {label} attempt {attempt} gagal: {e}")
                time.sleep(12)
        if sid:
            rows.append((label, sid))
            print(f"  ✅ {label}: {sid}")
        else:
            print(f"  ❌ {label}: gagal 4x")

    print("\n== 5. verifikasi (fetch ulang per ID) ==")
    for label, sid in rows:
        r = c.get_schedule(sid)
        doc = r.get("doc") or r
        cap = doc.get("description") or ""
        med = [(m or {}).get("url", "").split("/")[-1] for m in (doc.get("medias") or [])]
        rep = doc.get("replies") or []
        dt = datetime.fromisoformat(doc["scheduleAt"].replace("Z", "+00:00")).astimezone(WIB)
        urut = med == [f"{PREFIX}_{i:02d}.jpg" for i in range(1, N + 1)]
        bersih = not re.findall(r"@\w+", cap) and not [t for t in re.findall(r"#(\w+)", cap) if t != t.lower()]
        panjang_ok = len(cap) <= 500 if label == "THREADS" else len(cap) > 300
        good = (doc.get("status") == "pending" and doc.get("type") == "album" and urut
                and len(rep) == 0 and bersih and panjang_ok)
        print(f"  {label:8s} | {sid} | {doc.get('status')} | {dt:%Y-%m-%d %H:%M} WIB | "
              f"{len(cap)} char | slide={len(med)} urut={'ya' if urut else 'TIDAK'} | replies={len(rep)} | "
              f"{'OK' if good else 'PERIKSA'}")


if __name__ == "__main__":
    main()
