#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Facebook Yasma Natura — share 1 artikel website per hari (perintah Bos, 5 Okt 2026).

  Facebook Yasma Natura (akun Repliz `6a3b41e3c5ff4ce3a327544b`, username `yasmanatura`)
    1 post/hari, 11:00 WIB — share artikel yasmanatura.co.id yang BELUM pernah dipakai
    (type=text, link di caption, replies=[]).

Aturan yang dipatuhi:
- FB tidak support `replies` -> WAJIB `replies=[]` (kalau tidak: "undefined postId").
- Hashtag huruf kecil semua; tanpa mention; tanpa teks CJK (guard di repliz_client).
- Maks 1 jadwal/hari/akun -> dicek dari jadwal HIDUP di Repliz, bukan cuma state file.
- Link = field `link` dari WP REST + diverifikasi ulang artikelnya masih `publish`.

Mode:
  python3 fb_yasma_harian.py preview [N]   # cetak rencana N hari ke depan (default 1), tidak create
  python3 fb_yasma_harian.py run     [N]   # create N jadwal harian (default 1), idempoten
"""
import json
import os
import re
import subprocess
import sys
import unicodedata
from datetime import datetime, timedelta, timezone

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

WIB = timezone(timedelta(hours=7))
SITE = "https://yasmanatura.co.id"
FB_YASMA = "6a3b41e3c5ff4ce3a327544b"
STATE = os.path.join(ROOT, "fb_yasma_harian_state.json")

# Slot harian FB Yasma (WIB). Dipakai berurutan: 11:00 dulu, kalau terisi geser ke jam berikutnya.
JAM = [11, 14, 17, 20]
MAX_CAPTION = 2200

UA = ("Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 "
      "(KHTML, like Gecko) Chrome/124 Safari/537.36")

# Mode `cron`: jalankan seperti `run` tapi cuma cetak baris hasil (untuk delivery Telegram harian).
QUIET = False


def p(*a, **k):
    if not QUIET:
        print(*a, **k)


# ── util ────────────────────────────────────────────────────────────────────
def curl(url, timeout=45, extra=None):
    cmd = ["curl", "-sL", "--compressed", "--max-time", str(timeout), "-A", UA]
    if extra:
        cmd += extra
    cmd.append(url)
    p = subprocess.run(cmd, capture_output=True, text=True, errors="replace")
    return p.stdout or ""


def bersih(t):
    t = t or ""
    for a, b in (("&#038;", "&"), ("&#8217;", "'"), ("&#8216;", "'"), ("&#8211;", "-"),
                 ("&#8220;", '"'), ("&#8221;", '"'), ("&amp;", "&"), ("&#8230;", "..."),
                 ("&hellip;", "..."), ("&nbsp;", " "), ("&quot;", '"'), ("&lt;", "<"),
                 ("&gt;", ">"), ("&rsquo;", "'"), ("&lsquo;", "'"), ("&ndash;", "-")):
        t = t.replace(a, b)
    t = re.sub(r"<[^>]+>", " ", t)
    t = re.sub(r"\s+", " ", t)
    return t.strip()


def rapikan_ringkasan(ex, maks=420):
    """Buang penanda potongan WP ([...]) dan akhiri di batas kalimat yang wajar."""
    ex = re.sub(r"\[\s*(?:\.\.\.|…)\s*\]", "", ex or "").strip()
    ex = re.sub(r"(?:\.\.\.|…)\s*$", "", ex).strip()
    if not ex:
        return ""
    if len(ex) > maks:
        ex = ex[:maks].rstrip()
    if not ex.endswith((".", "!", "?")):
        titik = max(ex.rfind(". "), ex.rfind("? "), ex.rfind("! "))
        if titik > 120:
            ex = ex[:titik + 1]
        else:
            koma = ex.rfind(", ")
            ex = (ex[:koma] + ".") if koma > 120 else ex.rsplit(" ", 1)[0] + "."
    m = re.search(r"[.?!]\s+([^.?!]{0,40}[?!])$", ex)
    if m and len(m.group(1)) < 40:
        ex = ex[:m.start() + 1].strip()
    return ex


def norm(u):
    u = (u or "").split("?")[0].split("#")[0].rstrip("/").lower()
    return unicodedata.normalize("NFC", u.replace("https://www.", "https://"))


def ada_cjk(t):
    return [c for c in (t or "") if "\u2e80" <= c <= "\u9fff" or "\uac00" <= c <= "\ud7af"
            or "\u3040" <= c <= "\u30ff"]


# ── Repliz ──────────────────────────────────────────────────────────────────
def semua_jadwal(c):
    out, page = [], 1
    while True:
        r = c.get_schedules(page=page, limit=100)
        docs = r.get("docs") or r.get("data") or []
        if not docs:
            break
        out.extend(docs)
        if len(docs) < 100:
            break
        page += 1
        if page > 120:
            break
    return out


def link_terpakai(docs, akun):
    used = set()
    pola = re.compile(r"https?://[^\s\"'<>)]*yasmanatura\.co\.id[^\s\"'<>)]*")
    for d in docs:
        if d.get("accountId") != akun:
            continue
        for m in (d.get("medias") or []):
            for u in pola.findall(m.get("url") or ""):
                used.add(norm(u))
        for f in ("description", "title", "topic"):
            for u in pola.findall(d.get(f) or ""):
                used.add(norm(u))
    return used


def slot_terpakai(docs, akun, status=("pending", "draft", "processing", "success")):
    """Kunci slot 'YYYY-MM-DDTHH' (UTC) yang sudah terisi di akun ini."""
    s = set()
    for d in docs:
        if d.get("accountId") != akun:
            continue
        if (d.get("status") or "").lower() not in status:
            continue
        sa = d.get("scheduleAt") or ""
        if len(sa) >= 13:
            s.add(sa[:13])
    return s


def tanggal_terpakai(docs, akun):
    """Tanggal WIB yang sudah punya jadwal (non-error) di akun ini — penjaga 1 post/hari."""
    s = set()
    for d in docs:
        if d.get("accountId") != akun:
            continue
        if (d.get("status") or "").lower() == "error":
            continue
        sa = d.get("scheduleAt") or ""
        if len(sa) < 16:
            continue
        try:
            dt = datetime.strptime(sa[:19], "%Y-%m-%dT%H:%M:%S").replace(tzinfo=timezone.utc)
        except Exception:
            continue
        s.add(dt.astimezone(WIB).strftime("%Y-%m-%d"))
    return s


# ── sumber artikel (WP REST) ────────────────────────────────────────────────
def _parse_wp(raw):
    for teks in (raw, raw.replace('\\"', '"').replace("\\/", "/")):
        j = teks.find('[{"id')
        if j < 0:
            j = teks.find('[{"')
        if j < 0:
            continue
        k = teks.rfind("}]")
        try:
            return json.loads(teks[j:k + 2])
        except Exception:
            continue
    return None


def ambil_artikel():
    q = ("/wp-json/wp/v2/posts?per_page=60&orderby=date&order=desc"
         "&_fields=id,date,slug,link,title,excerpt,status")
    cb = "&cb=%d" % int(datetime.now().timestamp())
    sumber = [
        ("langsung -4", SITE + q + cb, ["-4"]),
        ("langsung", SITE + q + cb, []),
        ("jina", "https://r.jina.ai/" + SITE + q, []),
    ]
    for nama, url, extra in sumber:
        for i in range(3):
            raw = curl(url, timeout=90, extra=extra) or ""
            data = _parse_wp(raw)
            if data and isinstance(data, list):
                out = []
                for a in data:
                    if (a.get("status") or "publish") != "publish":
                        continue
                    out.append({
                        "id": a.get("id"),
                        "date": a.get("date", ""),
                        "link": (a.get("link") or "").strip(),
                        "slug": a.get("slug", ""),
                        "judul": bersih((a.get("title") or {}).get("rendered", "")).split("|")[0].strip(),
                        "excerpt": rapikan_ringkasan(bersih((a.get("excerpt") or {}).get("rendered", ""))),
                    })
                p("  artikel via %s: %d" % (nama, len(out)))
                return out
            p("  %s percobaan %d: JSON belum ada (len=%d)" % (nama, i + 1, len(raw)))
    return []


def slug_hidup(slug):
    """Pastikan artikel masih publish (sumber kebenaran = WP REST, bukan halaman front-end)."""
    url = "%s/wp-json/wp/v2/posts?slug=%s&status=publish&_fields=id,link&cb=%d" % (
        SITE, slug, int(datetime.now().timestamp()))
    raw = curl(url, timeout=45, extra=["-4"]) or curl(url, timeout=45)
    data = _parse_wp(raw)
    return bool(data)


# ── caption ─────────────────────────────────────────────────────────────────
TEMA = [
    (("kolagen", "glutathione", "kecantikan", "skin", "glow"), "#yasmanatura #kolagen #minumanserbuk"),
    (("madu",), "#yasmanatura #maklonmadu #bisnisumkm"),
    (("serbuk", "bubuk", "instan"), "#yasmanatura #minumanserbuk #peluangbisnis"),
    (("herbal", "jamu", "immunobooster", "imun", "kurkuma", "temulawak", "adaptogen", "stamina"),
     "#yasmanatura #minumanherbal #bisnisumkm"),
    (("maklon", "produksi", "legalitas", "bpom", "halal"), "#yasmanatura #maklonminuman #bisnisminuman"),
]


def tema_hashtag(art):
    slug = (art.get("slug") or "").lower()
    for kunci, tag in TEMA:
        if any(k in slug for k in kunci):
            return tag
    return "#yasmanatura #maklonminuman #umkm"


def caption_artikel(art, idx):
    judul, link = art["judul"], art["link"]
    ringkas = rapikan_ringkasan(art["excerpt"], 380)
    # Selaraskan nada: artikel aslinya pakai "Anda", caption-nya "kamu" (perintah Bos: lebih santai).
    ringkas = re.sub(r"\bAnda\b", "kamu", ringkas)
    tag = tema_hashtag(art)
    badan = ("%s\n\n" % ringkas) if ringkas else ""
    varian = [
        (
            "Ada yang nanya ke aku minggu lalu, dan kayaknya banyak yang ngalamin "
            "hal yang sama.\n\n"
            f"{badan}"
            f"Aku tulis lebih lengkap di sini:\n{judul}\n{link}\n\n"
            "Kamu lagi di tahap mana? Cerita di komentar, aku baca satu-satu.\n\n"
            f"{tag}"
        ),
        (
            "Aku rangkum satu topik yang sering banget muncul waktu ngobrol sama "
            "pemilik brand minuman baru.\n\n"
            f"{badan}"
            f"Versi lengkapnya di sini:\n{judul}\n{link}\n\n"
            "Kamu udah sampai tahap mana? Tulis di komentar ya.\n\n"
            f"{tag}"
        ),
        (
            "Buat kamu yang lagi nyiapin produk minuman, catatan ini mungkin kepake.\n\n"
            f"{badan}"
            f"Aku tulis lebih lengkap di sini:\n{judul}\n{link}\n\n"
            "Ada yang mau kamu tanyain? Tulis di komentar, nanti aku jawab.\n\n"
            f"{tag}"
        ),
        (
            "Udah beberapa kali ditanyain, jadi aku tulis sekalian biar rapi.\n\n"
            f"{badan}"
            f"Tulisan utuhnya di sini:\n{judul}\n{link}\n\n"
            "Kalau kamu punya rencana produk, ceritain dong di komentar. "
            "Seperti apa rencananya?\n\n"
            f"{tag}"
        ),
    ]
    return varian[idx % len(varian)]


def validasi(cap, link, maks=MAX_CAPTION):
    err = []
    if link not in cap:
        err.append("link hilang")
    if not any(k in cap.lower() for k in ["komentar", "menurut", "pendapat", "pengalaman",
                                          "cerita", "diskusi"]):
        err.append("tanpa ajakan diskusi")
    if "@" in cap:
        err.append("ada mention")
    bad = [t for t in re.findall(r"#(\w+)", cap) if t != t.lower()]
    if bad:
        err.append("hashtag kapital: %s" % bad)
    cjk = ada_cjk(cap)
    if cjk:
        err.append("ada teks CJK: %s" % "".join(cjk[:5]))
    if len(cap) > maks:
        err.append("kepanjangan %d > %d" % (len(cap), maks))
    return err


# ── state ───────────────────────────────────────────────────────────────────
def muat_state():
    if os.path.exists(STATE):
        try:
            s = json.load(open(STATE))
            s.setdefault("hari", {})
            return s
        except Exception:
            pass
    return {"hari": {}}


def simpan_state(s):
    json.dump(s, open(STATE, "w"), indent=1, ensure_ascii=False)


# ── main ────────────────────────────────────────────────────────────────────
def main():
    global QUIET
    mode = (sys.argv[1] if len(sys.argv) > 1 else "preview").lower()
    jumlah = int(sys.argv[2]) if len(sys.argv) > 2 else 1
    if mode == "cron":
        QUIET, mode = True, "run"
    sekarang = datetime.now(WIB)
    state = muat_state()
    c = get_client()

    p("=== FB Yasma Natura harian | mode %s | %s ===" %
      (mode, sekarang.strftime("%a %d %b %Y %H:%M WIB")))
    docs = semua_jadwal(c)
    terpakai_slot = slot_terpakai(docs, FB_YASMA)
    sudah_tanggal = tanggal_terpakai(docs, FB_YASMA)
    used = link_terpakai(docs, FB_YASMA)
    for tgl, isi in state["hari"].items():
        for d in (isi or {}).values():
            if isinstance(d, dict) and d.get("url"):
                used.add(norm(d["url"]))
    jml_akun = len([d for d in docs if d.get("accountId") == FB_YASMA])
    p("jadwal Repliz terbaca: %d | FB Yasma: %d jadwal | %d URL yasmanatura sudah dipakai"
      % (len(docs), jml_akun, len(used)))

    arts = ambil_artikel()
    if not arts:
        print("❌ FB Yasma: tidak bisa membaca daftar artikel yasmanatura.co.id — tidak ada jadwal dibuat.")
        sys.exit(1)
    kandidat = [a for a in arts if norm(a["link"]) not in used]
    p("artikel terbaca: %d | kandidat belum pernah dipakai: %d" % (len(arts), len(kandidat)))
    if not kandidat:
        print("⚠️ FB Yasma: semua artikel di 60 teratas sudah pernah di-share — perlu stok/topik baru.")
        sys.exit(1)

    # rencanakan tanggal + slot
    rencana = []
    hari = sekarang.date()
    dipakai_hari_ini = 0
    for _ in range(400):
        if len(rencana) >= jumlah:
            break
        tgl_str = hari.strftime("%Y-%m-%d")
        if tgl_str in sudah_tanggal or tgl_str in [r["tgl"] for r in rencana]:
            hari += timedelta(days=1)
            continue
        dipilih = None
        for jam in JAM:
            cand = datetime(hari.year, hari.month, hari.day, jam, 0, tzinfo=WIB)
            kunci = cand.astimezone(timezone.utc).strftime("%Y-%m-%dT%H")
            if cand <= sekarang + timedelta(minutes=25):
                continue
            if kunci in terpakai_slot:
                continue
            dipilih = cand
            break
        if dipilih:
            rencana.append({"tgl": tgl_str, "slot": dipilih})
        hari += timedelta(days=1)

    if not rencana:
        print("⚠️ FB Yasma: tidak ada slot kosong yang bisa dipakai — tidak ada jadwal dibuat.")
        sys.exit(1)

    kerja = []
    for i, r in enumerate(rencana):
        art = kandidat[i] if i < len(kandidat) else None
        if not art:
            print("⚠️ FB Yasma: kandidat artikel habis di hari ke-%d" % (i + 1))
            break
        cap = caption_artikel(art, r["slot"].toordinal() % 3)
        ok = slug_hidup(art["slug"])
        masalah = validasi(cap, art["link"])
        kerja.append({"art": art, "slot": r["slot"], "cap": cap})
        p("\n[%d] %s | %s WIB | %d char" % (i + 1, art["date"][:10],
                                            r["slot"].strftime("%d %b %H:%M"), len(cap)))
        p("    judul   : %s" % art["judul"][:90])
        p("    link    : %s (artikel publish: %s)" % (art["link"], "ya" if ok else "TIDAK"))
        p("    hashtag : %s" % tema_hashtag(art))
        p("    validasi: %s" % (masalah or "OK"))
        if mode != "run":
            p("    --- caption ---")
            p("    " + cap.replace("\n", "\n    "))

    if mode != "run":
        p("\n(preview — tidak ada yang dibuat)")
        return

    p("\n=== CREATE ===")
    dibuat = 0
    for k in kerja:
        art, slot, cap = k["art"], k["slot"], k["cap"]
        tgl, kunci = slot.strftime("%Y-%m-%d"), slot.astimezone(timezone.utc).strftime("%Y-%m-%dT%H")
        if (state["hari"].get(tgl) or {}).get("artikel"):
            print("  ⏭️ %s sudah tercatat di state — skip" % tgl)
            continue
        if kunci in terpakai_slot:
            print("  ⏭️ %s slot sudah terisi di Repliz — skip" % slot.strftime("%d %b %H:%M"))
            continue
        masalah = validasi(cap, art["link"])
        if masalah:
            print("  ❌ %s validasi gagal: %s" % (tgl, masalah))
            continue
        payload = {
            "accountId": FB_YASMA,
            "title": art["judul"][:95],
            "topic": "Artikel Website Yasma Natura",
            "type": "text",
            "description": cap,
            "medias": [],
            "scheduleAt": slot.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.000Z"),
            "replies": [],
        }
        try:
            res = c.post("/public/schedule", payload)
            sid = res.get("scheduleId") or res.get("_id") or res.get("id")
            print("  ✅ %s %s WIB | id=%s | %s" % (tgl, slot.strftime("%H:%M"), sid,
                                                   art["judul"][:55]))
            state["hari"].setdefault(tgl, {})["artikel"] = {
                "id": sid, "akun": FB_YASMA, "url": art["link"], "judul": art["judul"],
                "scheduleAt": payload["scheduleAt"],
            }
            simpan_state(state)
            terpakai_slot.add(kunci)
            dibuat += 1
        except ReplizError as e:
            print("  ❌ %s: %s" % (tgl, e))
    if dibuat:
        print("total dibuat: %d" % dibuat)
    else:
        print("⚠️ FB Yasma: tidak ada jadwal baru dibuat (stok/slot perlu dicek).")


if __name__ == "__main__":
    try:
        main()
    except ReplizError as e:
        print("ReplizError:", e)
        sys.exit(1)
