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

Tema gambar (hasil vision, seri kartu @ukyhandoko 1/6):
Judul: "Mindset yang Bikin Saya Bertahan di Bisnis"
Isi:  1. Gagal itu data — setiap error adalah informasi berharga
      2. Fokus pada kontrol — jangan sibuk mikirin hal di luar kendali Anda
      3. Uang adalah hasil sampingan dari solusi — bukan tujuan utama
      4. Rest is productive — burnout cuma bikin keputusan jelek
Footer: @ukyhandoko • Founder StudioSoft

Verifikasi aset: 1080x1080, bg PUTIH + teks #122B63 (18,43,99) = versi BERGANTIAN
standar 16 Sep 2026 (bg putih + teks navy), 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_17_white_mindset"
FILENAME = "17_white_mindset.png"
SRC = os.path.join(ROOT, "uky_threads_images", FILENAME)
UKY_T = ACC.UKY_THREADS
TITLE = "Mindset yang Bikin Saya Bertahan di Bisnis"
TOPIC = "Mindset bisnis: gagal itu data, fokus pada kontrol, uang hasil sampingan, rest is productive"
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 = (
    "Mindset yang bikin saya bertahan di bisnis bukan soal kerja 24 jam. "
    "Empat hal ini yang saya pegang:\n\n"
    "1. Gagal itu data, bukan vonis \u2014 setiap error adalah informasi berharga.\n"
    "2. Fokus ke yang bisa saya kontrol, bukan yang di luar kendali.\n"
    "3. Uang adalah hasil sampingan dari solusi, bukan tujuan utama.\n"
    "4. Rest is productive \u2014 burnout cuma bikin keputusan jelek."
)

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:
        # cek teks harus navy #122B63 (varian bergantian)
        navy_px = sum(1 for p in img.convert("RGB").getdata() if p == NAVY)
        print(f"   varian: bg PUTIH + teks #122B63 ({navy_px} px navy)")
        if navy_px < 5000:
            print("❌ teks bukan #122B63 (standar 16 Sep) — STOP, render ulang dulu.")
            return 1
    else:
        print("❌ 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("❌ 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"⏭️  sudah ada {len(pre)} jadwal utk media ini → SKIP create (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("⚠️ verifikasi tidak pas (harus 1 pending dgn sid cocok) → state TIDAK dimajukan")
        return 1
    print("\n✅ 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())
