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

Tema gambar (hasil vision, slide 5/6 dari seri @ukyhandoko):
Judul: "Jangan Posting Setiap Hari Kalau..."
Isi:  "...konten Anda gak bermutu. Konsistensi itu penting. Tapi konsistensi konten jelek
      cuma bikin orang mute/unfollow. Lebih baik posting 3x seminggu dengan kualitas tinggi,
      daripada 7x dengan konten asal-asalan. Quality > Quantity. Sederhana."
Footer: @ukyhandoko

Aturan: Threads ≤500 char, gaya personal "saya", 3 paragraf, 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_15_blue_jangan_post"
FILENAME = "15_blue_jangan_post.png"
SRC = os.path.join(ROOT, "uky_threads_images", FILENAME)
UKY_T = ACC.UKY_THREADS
TITLE = "Quality > Quantity: Jangan Posting Kalau Gak Bermutu"
TOPIC = "Quality > Quantity — konsistensi harus bermutu"
EXPECTED_BASENAME = FILENAME.rsplit(".", 1)[0].lower() + ".jpg"
SLOT_WIB_HOUR = 8  # slot target 08:00 WIB = 01:00Z

# CTA yang sudah dipakai run-run terakhir (jangan diulang)
CTA_EXCLUDE = {
    "Berapa lama Anda bertahan sebelum melihat hasil dari digital marketing? Share pengalamannya.",
    "Kata kunci apa yang paling susah Anda rankingkan? Ceritakan di kolom komentar.",
    "Bagaimana pengalaman Anda? Share di komentar biar kita sama-sama belajar.",
}

BODY = (
    "\"Jangan posting setiap hari kalau konten Anda gak bermutu.\" Kalimat ini saya pegang banget. \U0001F4CC\n\n"
    "Konsistensi itu penting. Tapi konsistensi konten jelek cuma bikin orang mute atau unfollow. "
    "Saya lebih pilih posting 3x seminggu dengan kualitas tinggi daripada 7x dengan konten asal-asalan.\n\n"
    "Quality > Quantity. Sederhana, tapi sering dilupakan."
)

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


def build_caption():
    """Body + pancingan diskusi (bobot 60% netral / 25% opini / 15% sanjungan)."""
    for _ in range(40):
        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 CTA_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 CTA_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")


def upload():
    """PNG → JPEG di openclaw-img, 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)."""
    from datetime import date
    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():
    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 (jadwal baru belum masuk)."""
        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")
        # rerun: pakai jam jadwal yang SUDAH ada (jangan pakai slot hasil hitung baru)
        SLOT_UTC = pre[0].get("scheduleAt") or SLOT_UTC
        hari = datetime.fromisoformat(SLOT_UTC.replace("Z", "+00:00")) + timedelta(hours=7)
        slot_wib_str = hari.strftime("%Y-%m-%d 08:00 WIB")
        hari = hari.date()
        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())
