#!/usr/bin/env python3
"""DAILY IMAGE POSTING — cron 2026-09-18T18:00Z (= 19 Sep 2026 01:00 WIB)
Item index 48: uky_thread_18_white_modal.png (brand ukyhandoko) → Threads @ukyhandoko.

Tema gambar (hasil vision, seri kartu @ukyhandoko 2/6):
Judul: 'Saya Baru Mulai, Gak Punya Modal Besar'
Isi:  Modal terbesar yang Anda punya sekarang:
      - Waktu — belajar lebih cepat dari kompetitor
      - Rasa lapar — founder yang kelaparan lebih kreatif
      - Jaringan — satu kenalan bisa buka pintu yang gak terduga
      Modal bukan alasan. Excuses are tools of the incompetent.
Header: @ukyhandoko (kanan atas 2/6)  •  Footer: @ukyhandoko • Founder StudioSoft

Verifikasi aset: 1080x1080, bg PUTIH + teks #122B63 (18,43,99) = versi BERGANTIAN
standar 16 Sep 2026, rata kiri, tanpa garis separator, footer ada → LAYAK posting.

Aturan: Threads ≤500 char, gaya personal "saya", pancingan diskusi (diskusi_engine
industri=umum, bobot 60/25/15), 0 hashtag, 0 mention internal, replies=[].
Slot: 08:00 WIB bebas PERTAMA (01:00Z) — dipilih lewat cek API, bukan catatan lama.
"""
import json, os, re, sys, subprocess, random
from datetime import datetime, timezone, timedelta

sys.path.insert(0, "/home/mantapman/.hermes/profiles/atun/workspace")
from repliz_client import get_client, ACC, IMG_BASE_URL, IMG_DIR
from PIL import Image
from diskusi_engine import ambil_pancingan, PANCINGAN

ROOT = "/home/mantapman/.hermes/profiles/atun/workspace"
ITEM_ID = "uky_thread_18_white_modal"
FILENAME = "18_white_modal.png"
SRC = os.path.join(ROOT, "uky_threads_images", FILENAME)
UKY_T = ACC.UKY_THREADS
TITLE = "'Saya Baru Mulai, Gak Punya Modal Besar'"
TOPIC = "Modal bukan alasan: waktu, rasa lapar, dan jaringan = modal terbesar founder yang baru mulai"
EXPECTED_BASENAME = FILENAME.rsplit(".", 1)[0].lower() + ".jpg"
SLOT_WIB_HOUR = 8  # slot target 08:00 WIB = 01:00Z
NAVY = (18, 43, 99)
WHITE = (255, 255, 255)

BODY = (
    "Sering saya dengar: \"saya baru mulai, gak punya modal besar.\" "
    "Saya pun pernah di titik itu \u2014 dan sekarang saya yakin modal bukan soal uang.\n\n"
    "Modal terbesar yang Anda punya sekarang:\n"
    "\u2022 Waktu \u2014 belajar lebih cepat dari kompetitor\n"
    "\u2022 Rasa lapar \u2014 founder yang kelaparan jauh lebih kreatif\n"
    "\u2022 Jaringan \u2014 satu kenalan bisa buka pintu yang gak terduga\n\n"
    "Modal bukan alasan untuk tidak mulai."
)

CTA_RE = re.compile(r"komentar|pendapat|diskusi|setuju|share|pengalaman|menurut|sepakat|tanggapan|ceritakan", re.I)


def used_hooks():
    """Semua hook (paragraf terakhir) yang sudah pernah dipakai — biar tidak berulang."""
    out = set()
    lp = os.path.join(ROOT, "image_posting_log.json")
    for e in json.load(open(lp)):
        cap = (e.get("caption_final") or "").strip()
        if cap:
            out.add(cap.split("\n\n")[-1].strip())
    return out


def build_caption():
    """Body + pancingan diskusi (bobot 60% netral / 25% opini / 15% sanjungan)."""
    exclude = used_hooks()
    for _ in range(60):
        tone = random.choices(["netral", "opini", "sanjungan"], weights=[60, 25, 15])[0]
        hook = ambil_pancingan(industri="umum", tone=tone)
        cap = BODY + "\n\n" + hook
        if len(cap) <= 500 and CTA_RE.search(hook) and hook not in exclude:
            return cap, hook, tone
    kandidat = [s for t in ("netral", "opini", "sanjungan")
                for s in PANCINGAN["umum"][t]
                if CTA_RE.search(s) and s not in exclude]
    for s in sorted(kandidat, key=len):
        if len(BODY) + len(s) + 2 <= 500:
            return BODY + "\n\n" + s, s, "fallback"
    raise SystemExit("tidak ada pancingan yang muat / masih unik")


def upload():
    """PNG → JPEG di openclaw-img (standar desain 16 Sep), return URL publik."""
    os.makedirs(IMG_DIR, exist_ok=True)
    img = Image.open(SRC)
    if img.mode in ("RGBA", "LA", "P"):
        img = img.convert("RGB")
    out = os.path.join(IMG_DIR, FILENAME.rsplit(".", 1)[0] + ".jpg")
    img.save(out, format="JPEG", quality=90)
    url = f"{IMG_BASE_URL.rstrip('/')}/openclaw-img/{os.path.basename(out)}"
    return url, out


def check_public(url):
    """Simulasi jalur INTERNET (resolve ke ingress Tailscale) — bukan tailnet-direct."""
    codes = {}
    for ip in ("103.84.155.153", "103.84.155.217"):
        r = subprocess.run(
            ["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", "--max-time", "25",
             "--resolve", f"mantapserver.tail3947bb.ts.net:443:{ip}", url],
            capture_output=True, text=True)
        codes[ip] = r.stdout.strip()
    return codes


def all_docs(c):
    r = c.get_schedules(page=1, limit=100)
    pages = (r["totalDocs"] + 99) // 100
    docs = list(r["docs"])
    for p in range(2, pages + 1):
        docs.extend(c.get_schedules(page=p, limit=100)["docs"])
    return docs


def uky_pending(docs):
    out = []
    for d in docs:
        acc = d.get("account") or {}
        if (d.get("accountId") or acc.get("id") or acc.get("_id")) != UKY_T:
            continue
        if d.get("status") != "pending":
            continue
        out.append(d)
    out.sort(key=lambda d: d.get("scheduleAt") or "")
    return out


def first_free_8am(pending):
    """Slot 08:00 WIB (01:00Z) pertama yang kosong, mulai hari ini (WIB)."""
    today_wib = (datetime.now(timezone.utc) + timedelta(hours=7)).date()
    taken = {d["scheduleAt"][:10] for d in pending if (d.get("scheduleAt") or "")[11:13] == "01"}
    hari = today_wib
    for _ in range(120):
        if hari.isoformat() not in taken:
            return hari
        hari = hari + timedelta(days=1)
    raise SystemExit("tidak ada slot 08:00 WIB bebas dalam 120 hari")


def main():
    # 0. validasi aset (standar desain 16 Sep 2026)
    img = Image.open(SRC).convert("RGB")
    w, h = img.size
    bg = img.getpixel((5, 5))
    print(f"ASET: {SRC} {w}x{h} bg RGB{bg}")
    if bg == NAVY:
        print("   varian: bg NAVY #122B63 + teks putih")
    elif bg == WHITE:
        navy_px = sum(1 for p in img.getdata() if p == NAVY)
        print(f"   varian: bg PUTIH + teks #122B63 ({navy_px} px navy)")
        if navy_px < 5000:
            print("GAGAL: teks bukan #122B63 (standar 16 Sep) — STOP, render ulang dulu.")
            return 1
    else:
        print("GAGAL: background bukan #122B63 / PUTIH (standar 16 Sep) — STOP, render ulang dulu.")
        return 1

    cap, hook, tone = build_caption()
    print(f"CAPTION ({len(cap)} chars) tone={tone}\n{cap}\n")
    assert len(cap) <= 500, f"caption {len(cap)} char > 500"
    assert not re.search(r"@\w+", cap), "ada mention!"
    assert CTA_RE.search(hook), "tidak ada pancingan diskusi"
    assert "#" not in cap, "ada hashtag (aturan: 0 hashtag untuk seri ini)"
    assert "..." not in cap, "ada elipsis truncate"

    url, out = upload()
    print("LOCAL JPEG:", out, os.path.getsize(out), "bytes")
    print("PUBLIC URL:", url)
    codes = check_public(url)
    print("INTERNET-PATH CODES:", codes)
    if not any(c == "200" for c in codes.values()):
        print("GAGAL: URL publik tidak 200 — STOP, jangan schedule (aturan media server).")
        return 1

    c = get_client()
    docs = all_docs(c)
    pending = uky_pending(docs)

    # PILIH SLOT: 08:00 WIB bebas pertama (jangan percaya catatan log lama)
    hari = first_free_8am(pending)
    slot_dt = datetime(hari.year, hari.month, hari.day, SLOT_WIB_HOUR - 7, 0, 0, tzinfo=timezone.utc)
    SLOT_UTC = slot_dt.strftime("%Y-%m-%dT%H:%M:%S.000Z")
    slot_wib_str = f"{hari.isoformat()} {SLOT_WIB_HOUR:02d}:00 WIB"
    print("SLOT TERPILIH:", SLOT_UTC, f"({slot_wib_str})")

    def list_uky_media():
        """Selalu ambil ULANG dari API — jangan pakai snapshot lama."""
        hits = []
        for d in all_docs(c):
            acc = d.get("account") or {}
            if (d.get("accountId") or acc.get("id") or acc.get("_id")) != UKY_T:
                continue
            meds = d.get("medias") or []
            u = (meds[0].get("url") if meds else "") or ""
            # exact basename — substring match pernah salah cocokkan seri uky_quote_*
            if os.path.basename(u.split("?")[0]).lower() == EXPECTED_BASENAME:
                hits.append(d)
        return hits

    # IDEMPOTENSI (create response pakai 'scheduleId', bukan '_id')
    pre = list_uky_media()
    if pre:
        sid = pre[0].get("id")
        SLOT_UTC = pre[0].get("scheduleAt") or SLOT_UTC
        hari = (datetime.fromisoformat(SLOT_UTC.replace("Z", "+00:00")) + timedelta(hours=7)).date()
        slot_wib_str = f"{hari.isoformat()} {SLOT_WIB_HOUR:02d}:00 WIB"
        print(f"SKIP: sudah ada {len(pre)} jadwal utk media ini (id={sid})")
    else:
        res = c.create_schedule(
            UKY_T, cap, SLOT_UTC,
            title=TITLE, topic=TOPIC,
            type_="image",
            medias=[{"url": url, "type": "image", "alt": FILENAME}],
            replies=[],
        )
        sid = res.get("scheduleId") or res.get("_id") or res.get("id")
        print("CREATE RESPONSE:", json.dumps(res)[:400])
    print("SID:", sid)

    # VERIFIKASI via re-list (jangan percaya response create)
    hits = list_uky_media()
    print(f"\nVERIFIKASI: {len(hits)} jadwal {EXPECTED_BASENAME} di Threads uky")
    ok = False
    for d in hits:
        wib = datetime.fromisoformat(d["scheduleAt"].replace("Z", "+00:00")) + timedelta(hours=7)
        print(f"  id={d.get('id')} status={d.get('status')} type={d.get('type')} at={wib.strftime('%Y-%m-%d %H:%M')} WIB")
        print(f"  media={d['medias'][0]['url']}")
        print(f"  desc({len(d.get('description') or '')}) replies={len(d.get('replies') or [])}")
        if d.get("id") == sid and d.get("status") == "pending":
            ok = True
    if not (ok and len(hits) == 1):
        print("PERINGATAN: verifikasi tidak pas (harus 1 pending dgn sid cocok) -> state TIDAK dimajukan")
        return 1
    print("\nOK TERVERIFIKASI: 1 jadwal pending, media + caption terpasang")

    # UPDATE STATE + LOG
    sp = os.path.join(ROOT, "image_posting_state.json")
    st = json.load(open(sp))
    st["index"] = (st["index"] + 1) % st["total"]
    json.dump(st, open(sp, "w"), indent=2)
    print("STATE ->", st)

    lp = os.path.join(ROOT, "image_posting_log.json")
    log = json.load(open(lp))
    log.append({
        "timestamp": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.000Z"),
        "item_id": ITEM_ID, "brand": "ukyhandoko", "source": "uky_threads_images",
        "filename": FILENAME, "topic": TOPIC,
        "slot_note": f"slot 08:00 WIB bebas pertama dari cek API = {slot_wib_str}",
        "schedule_times": {"threads_uky": f"{SLOT_UTC} (08:00 WIB {hari.isoformat()}) - image 1"},
        "repliz_ids": {"threads_uky": sid},
        "caption_chars": len(cap), "media_url": url,
        "caption_final": cap,
        "next_index": st["index"],
    })
    json.dump(log, open(lp, "w"), indent=2, ensure_ascii=False)
    print("LOG ->", lp, f"({len(log)} entri)")
    return 0


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