#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""StudioSoft harian — konten dari website + berita AI (perintah Bos, 23 Sep 2026).

  Facebook StudioSoft
    08:00 WIB — share 1 artikel studiosoft.id yang BELUM pernah dipakai (type=text, replies=[])
    15:00 WIB — berita AI paling viral & terupdate hari itu (sinyal: Google Trends + tren X + sebaran media)
  Threads StudioSoft + LinkedIn StudioSoft
    15:00 WIB — berita AI yang sama (Threads ≤500 char, LinkedIn gaya profesional, replies=[])
  ⛔ Selasa & Jumat TIDAK dipasang (perintah Bos 23 Sep 2026 — 2 hari itu sudah ada kartu SS 19:30).

Mode:
  python3 fb_studiosoft_harian.py preview [hari]   # cetak rencana, TIDAK create apa pun
  python3 fb_studiosoft_harian.py run     [hari]   # create jadwal (idempoten: slot terisi = skip)

Catatan:
- FB tidak support `replies` -> WAJIB `replies=[]` (kalau tidak: 'undefined postId').
- Hashtag huruf kecil, tanpa mention antar akun kita.
- Sumber "X" = trends24.in (mirror tren X Indonesia) karena CLI xurl / kredensial X API tidak ada di host ini.
"""
import json
import os
import re
import subprocess
import sys
import unicodedata
from datetime import datetime, timedelta, timezone
from difflib import SequenceMatcher
from email.utils import parsedate_to_datetime

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

WIB = timezone(timedelta(hours=7))
FB_SS = "6a334952c5ff4ce3a3225941"
THREADS_SS = ACC.STUDIOSOFT_THREADS      # 6a335b9ec5ff4ce3a3225c2a
LINKEDIN_SS = ACC.STUDIOSOFT_LINKEDIN    # 6a334c49c5ff4ce3a32259c9
STATE = os.path.join(ROOT, "fb_studiosoft_harian_state.json")

JAM_ARTIKEL = 8    # WIB
JAM_AI = 15        # WIB
MAX_UMUR_BERITA = 60  # jam
# Bos 23 Sep 2026: "selasa & jumat ga usah pasang" — 2 hari itu sudah ada kartu SS 19:30,
# jadi pipeline ini tidak mengisi apa pun di sana (weekday: Senin=0 ... Minggu=6).
HARI_SKIP = {1, 4}  # Selasa, Jumat

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

# ── Sumber berita (URL asli, bukan redirect) + sumber sinyal viral ──────────
FEEDS_AI = [
    ("Detik Inet", "https://inet.detik.com/rss"),
    ("CNBC Indonesia Tech", "https://www.cnbcindonesia.com/tech/rss"),
    ("Antara Tekno", "https://www.antaranews.com/rss/tekno.xml"),
    ("TechCrunch AI", "https://techcrunch.com/category/artificial-intelligence/feed/"),
    ("The Verge AI", "https://www.theverge.com/rss/ai-artificial-intelligence/index.xml"),
    ("MIT Tech Review AI", "https://www.technologyreview.com/topic/artificial-intelligence/feed"),
    ("Ars Technica", "https://feeds.arstechnica.com/arstechnica/technology-lab"),
    ("Engadget", "https://www.engadget.com/rss.xml"),
]

SINYAL_GOOGLE_NEWS = [
    "https://news.google.com/rss/search?q=AI+when:2d&hl=id&gl=ID&ceid=ID:id",
    "https://news.google.com/rss/search?q=%22kecerdasan+buatan%22+when:3d&hl=id&gl=ID&ceid=ID:id",
    "https://news.google.com/rss/search?q=ChatGPT+OR+OpenAI+OR+Gemini+when:2d&hl=id&gl=ID&ceid=ID:id",
]

SINYAL_TREN = [
    ("Google Trends ID", "https://trends.google.com/trending/rss?geo=ID"),
    ("Google Trends US", "https://trends.google.com/trending/rss?geo=US"),
    ("Tren X Indonesia", "https://trends24.in/indonesia/"),
]

KATA_AI = [
    "ai", "artificial intelligence", "kecerdasan buatan", "chatgpt", "openai", "gpt",
    "gemini", "anthropic", "claude", "deepseek", "llm", "machine learning",
    "deep learning", "deepfake", "chatbot", "robot", "robotik", "nvidia", "copilot",
    "grok", "sora", "midjourney", "agi", "algoritma ai", "kecerdasan artifisial",
    "generatif", "autonomous", "open weight", "ai agent", "data center ai",
]

STOP = set("""yang dan di ke dari untuk pada dengan ini itu ada akan tidak bukan juga sudah telah
bisa agar oleh dalam sebagai the a an of to in for on and with is are at by from that this new
cara jadi lebih hanya masih saja para tapi atau saat setelah sebelum karena bila jika kata
kata lalu kini bakal hingga sampai antara soal bagi milik kami kita anda mereka dia his her
its it's video foto: nya tak belum pernah punya bikin buat lewat """.split())


# ── 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;", ">")):
        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] + "."
    # buang ekor berupa judul/sub-judul pendek yang ikut terbawa (mis. "Apa Itu Branding?")
    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 kata(s):
    w = re.findall(r"[a-z0-9]+", (s or "").lower())
    return {x for x in w if len(x) > 3 and x not in STOP}


def tua_jam(tgl):
    try:
        dt = parsedate_to_datetime(tgl)
        if dt.tzinfo is None:
            dt = dt.replace(tzinfo=timezone.utc)
        return (datetime.now(timezone.utc) - dt).total_seconds() / 3600
    except Exception:
        return 999


# ── jadwal 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, domain="studiosoft.id"):
    used = set()
    pola = re.compile(r"https?://[^\s\"'<>)]*" + re.escape(domain) + r"[^\s\"'<>)]*")
    for d in docs:
        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")):
    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 slot_berikut(jam_wib, terpakai, sekarang):
    """datetime WIB berikutnya yang jamnya `jam_wib`, bukan hari SKIP, dan belum terisi di akun ini."""
    hari = sekarang.date()
    for _ in range(6):
        kandidat = datetime(hari.year, hari.month, hari.day, jam_wib, 0, tzinfo=WIB)
        kunci = kandidat.astimezone(timezone.utc).strftime("%Y-%m-%dT%H")
        if (hari.weekday() not in HARI_SKIP
                and kandidat > sekarang + timedelta(minutes=25)
                and kunci not in terpakai):
            return kandidat
        hari += timedelta(days=1)
    return None


# ── artikel studiosoft.id ──────────────────────────────────────────────────
def _parse_wp(raw):
    """r.jina.ai kadang mengembalikan JSON mentah, kadang sudah di-escape (\\")."""
    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():
    sumber = [
        ("langsung", "https://studiosoft.id/wp-json/wp/v2/posts?per_page=50"
                     "&_fields=id,date,slug,link,title,excerpt", ["--http1.1"]),
        ("jina", "https://r.jina.ai/https://studiosoft.id/wp-json/wp/v2/posts?per_page=50"
                 "&_fields=id,date,slug,link,title,excerpt", []),
    ]
    for nama, url, extra in sumber:
        for i in range(3):
            raw = curl(url, timeout=180, extra=extra) or ""
            data = _parse_wp(raw)
            if data:
                out = []
                for a in data:
                    ex = rapikan_ringkasan(bersih((a.get("excerpt") or {}).get("rendered", "")))
                    out.append({
                        "id": a.get("id"),
                        "date": a.get("date", ""),
                        "link": a.get("link", ""),
                        "slug": a.get("slug", ""),
                        "judul": bersih((a.get("title") or {}).get("rendered", "")).split("|")[0].strip(),
                        "excerpt": ex,
                    })
                print(f"  artikel via {nama}: {len(out)}")
                return out
            print(f"  {nama} percobaan {i+1}: JSON belum ada (len={len(raw)})")
    return []


TEMA = [
    ("ai", ["ai", "otomasi", "chatbot", "automasi"], "#ai #otomasibisnis #digitalmarketing"),
    ("branding", ["branding", "creative-agency", "digital-agency", "agency"], "#branding #digitalmarketing #umkm"),
    ("lokal", ["sidoarjo", "malang", "surabaya", "seo-lokal"], "#seolokal #umkm #surabaya"),
    ("industri", ["bengkel", "maklon", "firma-hukum", "klinik", "rumah-sakit"], "#digitalmarketing #kontenbisnis #umkm"),
    ("layanan", ["retainer", "paket", "kelola-sosial"], "#digitalmarketing #umkm #bisnisonline"),
]


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


def caption_artikel(art, idx):
    judul, link, ringkas, tag = art["judul"], art["link"], art["excerpt"], tema_hashtag(art)
    ringkas = rapikan_ringkasan(ringkas, 460)

    varian = [
        (
            f"Salah satu hal yang paling sering kami ulang ke klien: yang menentukan bisnis ditemukan "
            f"bukan seberapa sering posting, tapi seberapa jelas jawabannya buat calon pelanggan.\n\n"
            f"{ringkas}\n\n"
            f"Uraian lengkapnya saya tulis di artikel ini: {judul}\n{link}\n\n"
            f"Menurut Anda, apa yang paling cepat mendatangkan pelanggan untuk bisnis lokal? "
            f"Tulis pengalaman Anda di komentar, saya baca satu-satu.\n\n{tag}"
        ),
        (
            f"Pertanyaan yang paling sering muncul di grup pemilik usaha: \"Mulai dari mana dulu ya?\"\n\n"
            f"Jawabannya biasanya bukan nambah iklan, tapi merapikan yang sudah ada. {ringkas}\n\n"
            f"Panduan lengkapnya ada di sini: {judul}\n{link}\n\n"
            f"Anda tim beresin fondasi dulu atau tim ikut tren dulu? Cerita sedikit di komentar.\n\n{tag}"
        ),
        (
            f"Catatan dari proyek yang baru kami kerjakan:\n\n"
            f"{ringkas}\n\n"
            f"Topik ini saya bahas tuntas di artikel berikut: {judul}\n{link}\n\n"
            f"Kalau di bisnis Anda, bagian mana yang paling perlu dibenahi lebih dulu: kontennya, "
            f"websitenya, atau cara mengukur hasilnya? Boleh tulis di komentar.\n\n{tag}"
        ),
    ]
    # Nada sosmed = santai (Bos 5 Okt 2026): artikel pakai "Anda", caption pakai "kamu".
    return varian[idx % len(varian)].replace("Anda", "kamu").replace("anda", "kamu")


# ── berita AI + sinyal viral ───────────────────────────────────────────────
FEEDS_AI_KHUSUS = {"TechCrunch AI", "The Verge AI", "MIT Tech Review AI"}
POLA_AI = [re.compile(r"\b" + re.escape(k) + r"\b") for k in KATA_AI]
BOBOT_TOPIK = ["openai", "chatgpt", "gpt", "gemini", "anthropic", "claude", "nvidia",
               "deepfake", "regulasi", "aturan", "pemerintah", "ai agent", "robot", "deepseek"]


def ada_ai(teks):
    return any(p.search(teks) for p in POLA_AI)


def parse_rss(raw):
    items = []
    for blok in re.findall(r"<item>(.*?)</item>", raw, re.S):
        t = re.search(r"<title>(?:<!\[CDATA\[)?(.*?)(?:\]\]>)?</title>", blok, re.S)
        l = re.search(r"<link>(?:<!\[CDATA\[)?(.*?)(?:\]\]>)?</link>", blok, re.S)
        d = re.search(r"<pubDate>(.*?)</pubDate>", blok, re.S)
        s = re.search(r"<source[^>]*>(.*?)</source>", blok, re.S)
        desc = re.search(r"<description>(?:<!\[CDATA\[)?(.*?)(?:\]\]>)?</description>", blok, re.S)
        if not (t and l):
            continue
        items.append({
            "judul": bersih(t.group(1)),
            "link": (l.group(1) or "").strip(),
            "tgl": (d.group(1).strip() if d else ""),
            "sumber": bersih(s.group(1)) if s else "",
            "desc": bersih(desc.group(1)) if desc else "",
        })
    return items


def berita_ai():
    kandidat = []
    for nama, url in FEEDS_AI:
        raw = curl(url, timeout=40)
        for it in parse_rss(raw):
            judul_low = it["judul"].lower()
            cocok = ada_ai(judul_low)
            if not cocok and nama in FEEDS_AI_KHUSUS:
                cocok = ada_ai(it["desc"].lower())
            if not cocok:
                continue
            if not it["link"].startswith("http") or "news.google.com" in it["link"]:
                continue
            it["sumber"] = it["sumber"] or nama
            it["umur"] = tua_jam(it["tgl"])
            kandidat.append(it)

    # dedupe link sama
    seen, uniq = set(), []
    for it in kandidat:
        n = norm(it["link"])
        if n in seen:
            continue
        seen.add(n)
        uniq.append(it)
    return uniq


def sinyal_sebaran():
    """Google News: berapa banyak media mengangkat storyline yang sama (proxy 'viral')."""
    items = []
    for url in SINYAL_GOOGLE_NEWS:
        for it in parse_rss(curl(url, timeout=40)):
            j = re.sub(r"\s+-\s+[^-]{2,40}$", "", it["judul"]).strip()
            if not ada_ai(j.lower()):
                continue
            items.append({"judul": j, "sumber": (it["sumber"] or "-").lower(), "kw": kata(j)})
    return items


def sinyal_tren():
    """Frasa tren utuh (Google Trends + tren X Indonesia), bukan potongan kata."""
    frasa = set()
    for nama, url in SINYAL_TREN:
        raw = curl(url, timeout=40)
        if "trends24" in url:
            for q, t in re.findall(r'<a[^>]+href="https://twitter\.com/search\?q=([^"]+)"[^>]*>([^<]+)</a>', raw):
                t = bersih(t).strip().lower().lstrip("#")
                if len(t) > 4:
                    frasa.add(t)
        else:
            for t in re.findall(r"<title>(?:<!\[CDATA\[)?(.*?)(?:\]\]>)?</title>", raw, re.S)[1:]:
                t = bersih(t).strip().lower()
                if len(t) > 4:
                    frasa.add(t)
    return frasa


def skor_berita(kandidat, sebaran, tren):
    skor = []
    for it in kandidat:
        kw = kata(it["judul"] + " " + it["desc"])
        # coverage: media lain (Google News) yang mengangkat storyline sama
        media = set()
        for s in sebaran:
            inter = len(kw & s["kw"])
            if inter >= 3 and inter / max(1, len(kw)) >= 0.34:
                media.add(s["sumber"])
        cover = min(len(media), 6)
        # sinyal tren: frasa tren X / Google Trends muncul di judul
        judul_low = it["judul"].lower()
        tren_hit = sum(1 for f in tren if f in judul_low)
        recency = 60.0 * (1 - min(it["umur"], 48) / 48)
        nilai = recency + cover * 8 + min(tren_hit, 3) * 12
        if any(k in judul_low for k in BOBOT_TOPIK):
            nilai += 10
        if any(x in (it["sumber"] or "").lower() for x in ("detik", "kompas", "cnbc", "antara",
                                                           "techcrunch", "verge", "reuters", "mit")):
            nilai += 6
        it["skor"] = round(nilai, 1)
        it["cover"] = cover
        it["tren"] = tren_hit
        skor.append(it)
    skor.sort(key=lambda x: -x["skor"])
    return skor


def ringkasan_asli(url):
    raw = curl(url, timeout=40)
    for pola in (r'<meta[^>]+property=["\']og:description["\'][^>]+content=["\'](.*?)["\']',
                 r'<meta[^>]+content=["\'](.*?)["\'][^>]+property=["\']og:description["\']',
                 r'<meta[^>]+name=["\']description["\'][^>]+content=["\'](.*?)["\']'):
        m = re.search(pola, raw, re.S | re.I)
        if m and len(bersih(m.group(1))) > 60:
            return bersih(m.group(1))
    return ""


def caption_ai(it, idx, ringkas):
    judul, link, sumber = it["judul"], it["link"], it["sumber"] or "media"
    tag = "#ai #kecerdasanbuatan #teknologi #digitalmarketing"
    ringkas = rapikan_ringkasan(ringkas, 400)
    badan = f"{ringkas}\n\n" if ringkas else ""
    varian = [
        (
            f"Perkembangan AI hari ini yang paling ramai dibahas:\n\n\"{judul}\" ({sumber})\n\n"
            f"{badan}"
            f"Selengkapnya: {link}\n\n"
            f"Buat yang jalanin bisnis, pertanyaan saya selalu sama: hal seperti ini bikin kerja makin cepat, "
            f"atau bikin perencanaan makin susah ditebak? Tulis pendapat Anda di komentar.\n\n{tag}"
        ),
        (
            f"Kabar AI terbaru yang saya pantau pagi ini: {judul} ({sumber}).\n\n"
            f"{badan}"
            f"Baca beritanya di sini: {link}\n\n"
            f"Setiap gelombang AI baru selalu memunculkan pertanyaan yang sama: mana yang layak dipakai sekarang, "
            f"mana yang masih ramai saja. Menurut Anda yang mana? Yuk diskusi di komentar.\n\n{tag}"
        ),
        (
            f"Ini yang sedang naik soal AI: \"{judul}\" dari {sumber}.\n\n"
            f"{badan}"
            f"Link lengkapnya: {link}\n\n"
            f"Yang saya pelajari dari sepuluh tahun terakhir: teknologi baru jarang langsung mengubah cara orang "
            f"membeli, tapi hampir selalu mengubah cara orang mencari. Bagian mana yang paling Anda rasakan "
            f"di bisnis Anda? Cerita di komentar.\n\n{tag}"
        ),
    ]
    return varian[idx % len(varian)].replace("Anda", "kamu").replace("anda", "kamu")


def caption_ai_threads(it, idx, ringkas):
    """Threads: HARD LIMIT 500 char — caption dipangkas otomatis supaya selalu muat."""
    judul, link, sumber = it["judul"], it["link"], it["sumber"] or "media"
    tag = "#ai #kecerdasanbuatan #teknologi"
    head_var = [
        f'Berita AI yang paling ramai hari ini: "{judul}" ({sumber})',
        f'Kabar AI terbaru: {judul} ({sumber})',
        f'Sedang naik soal AI: "{judul}" ({sumber})',
    ]
    cta_var = [
        "Menurut Anda, ini bikin kerja makin cepat atau makin susah ditebak? Tulis di komentar.",
        "Bagian mana yang paling Anda rasakan dampaknya di bisnis Anda? Cerita di komentar.",
        "Menurut Anda ini kabar baik atau bikin was-was? Diskusi di komentar.",
    ]
    head, cta = head_var[idx % 3], cta_var[idx % 3]
    tetap = len(head) + len(link) + len(cta) + len(tag) + 8
    sisa = 500 - tetap
    ring = ""
    if ringkas and sisa >= 80:
        ring = rapikan_ringkasan(ringkas, sisa - 1)
    cap = "\n\n".join([b for b in (head, ring, link, cta, tag) if b])
    if len(cap) > 500:  # jaring pengaman berlapis
        cap = "\n\n".join([b for b in (head, link, cta, tag) if b])
    if len(cap) > 500:
        cap = "\n\n".join([b for b in (f'"{judul}" ({sumber})', link, cta_var[0][:80], tag) if b])
    return cap.replace("Anda", "kamu").replace("anda", "kamu")


def caption_ai_linkedin(it, idx, ringkas):
    """LinkedIn: gaya profesional, lebih panjang, replies=[] (LinkedIn juga tolak replies)."""
    judul, link, sumber = it["judul"], it["link"], it["sumber"] or "media"
    tag = "#ai #teknologi #digitalmarketing"
    ringkas = rapikan_ringkasan(ringkas, 420)
    badan = f"{ringkas}\n\n" if ringkas else ""
    varian = [
        (
            f"Kabar AI terbaru yang layak dicermati pekan ini: \"{judul}\" ({sumber}).\n\n"
            f"{badan}"
            f"Bagi tim yang mengurus pemasaran dan operasional, perubahan seperti ini biasanya masuk "
            f"lewat dua pintu: cara orang mencari solusi, dan cara kerja internal jadi lebih efisien.\n\n"
            f"Selengkapnya saya baca di sini: {link}\n\n"
            f"Di perusahaan Anda, adopsi AI lebih banyak dipakai untuk efisiensi tim atau untuk menjangkau "
            f"pelanggan baru? Saya tertarik mendengar pengalaman Anda di kolom komentar.\n\n{tag}"
        ),
        (
            f"Setiap kali ada rilis AI baru, pertanyaan yang muncul di ruang rapat selalu sama: apakah ini "
            f"mengubah cara kami bekerja?\n\n"
            f"Contoh terbaru: {judul} ({sumber}).\n\n"
            f"{badan}"
            f"Baca berita lengkapnya: {link}\n\n"
            f"Pandangan saya sederhana: teknologi baru jarang mengubah cara orang membeli, tapi hampir selalu "
            f"mengubah cara orang mencari. Menurut Anda, mana dampaknya yang paling terasa lebih dulu?\n\n{tag}"
        ),
        (
            f"Catatan singkat soal AI hari ini: \"{judul}\" dari {sumber}.\n\n"
            f"{badan}"
            f"Kenapa saya ikut mengikuti kabar seperti ini? Karena keputusan marketing yang baik jarang "
            f"datang dari tren, tapi sering datang dari paham lebih dulu dibanding kompetitor.\n\n"
            f"Berita lengkapnya: {link}\n\n"
            f"Anda sudah pernah memasukkan AI ke proses kerja tim? Kalau belum, apa yang paling menahan? "
            f"Tulis di komentar, saya baca satu-satu.\n\n{tag}"
        ),
    ]
    return varian[idx % len(varian)]


# ── validasi + state ───────────────────────────────────────────────────────
def validasi(cap, link, maks=5000):
    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(f"hashtag kapital: {bad}")
    if len(cap) > maks:
        err.append(f"kepanjangan {len(cap)} > {maks}")
    return err


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)


def mirip(a, b, batas=0.62):
    return SequenceMatcher(None, a.lower(), b.lower()).ratio() >= batas


def judul_ai_lama(state):
    """Judul berita AI yang sudah dipakai (semua platform: ai, ai_threads, ai_linkedin)."""
    out = []
    for v in state["hari"].values():
        for k, d in (v or {}).items():
            if k.startswith("ai") and isinstance(d, dict) and d.get("judul"):
                out.append(d["judul"])
    return out


# ── main ───────────────────────────────────────────────────────────────────
def main():
    mode = (sys.argv[1] if len(sys.argv) > 1 else "preview").lower()
    hari_ke = int(sys.argv[2]) if len(sys.argv) > 2 else 0
    sek = datetime.now(WIB) + timedelta(days=hari_ke)
    state = muat_state()
    c = get_client()

    print(f"=== StudioSoft harian | mode {mode} | acuan {sek.strftime('%a %d %b %Y %H:%M WIB')} ===")
    docs = semua_jadwal(c)
    terpakai = {a: slot_terpakai(docs, a) for a in (FB_SS, THREADS_SS, LINKEDIN_SS)}
    jml = {a: len([d for d in docs if d.get("accountId") == a]) for a in (FB_SS, THREADS_SS, LINKEDIN_SS)}
    print(f"jadwal Repliz terbaca: {len(docs)} | FB SS {jml[FB_SS]} | Threads SS {jml[THREADS_SS]} | "
          f"LinkedIn SS {jml[LINKEDIN_SS]}")

    kerja = []  # dict(jenis, akun, label, cap, slot, judul, url, maks)

    # ── 1. artikel website (Facebook saja) ──
    arts = ambil_artikel()
    used = link_terpakai(docs)
    print(f"\n[1] ARTIKEL WEBSITE (Facebook) — {len(arts)} artikel terbaca, "
          f"{len(used)} URL studiosoft.id sudah pernah dipakai")
    kandidat = [a for a in arts if norm(a["link"]) not in used]
    kandidat.sort(key=lambda a: a["date"], reverse=True)
    pilihan = kandidat[0] if kandidat else None
    if not pilihan and arts:
        print("  ⚠️ stok artikel baru habis — putar ulang artikel terlama yang pernah dipakai")
        pilihan = sorted(arts, key=lambda a: a["date"])[0]
    if pilihan:
        slot = slot_berikut(JAM_ARTIKEL, terpakai[FB_SS], sek)
        cap = caption_artikel(pilihan, (slot.toordinal() if slot else 0) % 3)
        print(f"  pilih: {pilihan['date'][:10]} | {pilihan['judul'][:70]}")
        print(f"  kandidat fresh lain: {len(kandidat)}")
        print(f"  slot: {slot.strftime('%a %d %b %H:%M WIB') if slot else '-'} | "
              f"{len(cap)} char | validasi: {validasi(cap, pilihan['link']) or 'OK'}")
        if mode != "run":
            print("  --- caption ---")
            print("  " + cap.replace("\n", "\n  "))
        kerja.append({"jenis": "artikel", "akun": FB_SS, "label": "Facebook", "cap": cap, "slot": slot,
                      "judul": pilihan["judul"], "url": pilihan["link"], "maks": 5000})
    else:
        print("  ❌ tidak ada artikel sama sekali")

    # ── 2. berita AI viral → Facebook + Threads + LinkedIn ──
    print("\n[2] BERITA AI VIRAL (Facebook + Threads + LinkedIn)")
    kand_ai = [b for b in berita_ai() if b["umur"] <= MAX_UMUR_BERITA]
    sebaran = sinyal_sebaran()
    tren = sinyal_tren()
    print(f"  kandidat berita AI ≤{MAX_UMUR_BERITA} jam: {len(kand_ai)} | sinyal Google News: {len(sebaran)} item | "
          f"kata tren (Trends+X): {len(tren)}")
    skor = skor_berita(kand_ai, sebaran, tren)
    print("  top kandidat:")
    for it in skor[:8]:
        print(f"    {it['skor']:>5} | {it['umur']:>4.1f}j | cover {it['cover']} | tren {it['tren']} | "
              f"{it['sumber'][:18]:<18} | {it['judul'][:62]}")

    lama = judul_ai_lama(state)
    ai_pilih = None
    for it in skor:
        if any(mirip(it["judul"], j) for j in lama):
            continue
        ai_pilih = it
        break
    if ai_pilih:
        ringkas = ringkasan_asli(ai_pilih["link"])
        print(f"\n  pilih: {ai_pilih['judul'][:80]}")
        print(f"  sumber: {ai_pilih['sumber']} | {ai_pilih['tgl']} | {ai_pilih['link'][:110]}")
        print(f"  ringkasan asli: {'ada' if ringkas else 'TIDAK dapat (caption pakai judul saja)'}")
        for akun, label, jenis, maks, builder in (
            (FB_SS, "Facebook", "ai", 5000, caption_ai),
            (THREADS_SS, "Threads", "ai_threads", 500, caption_ai_threads),
            (LINKEDIN_SS, "LinkedIn", "ai_linkedin", 5000, caption_ai_linkedin),
        ):
            slot = slot_berikut(JAM_AI, terpakai[akun], sek)
            cap = builder(ai_pilih, (slot.toordinal() if slot else 0) % 3, ringkas)
            print(f"  [{label}] slot: {slot.strftime('%a %d %b %H:%M WIB') if slot else '-'} | {len(cap)} char | "
                  f"validasi: {validasi(cap, ai_pilih['link'], maks) or 'OK'}")
            if mode != "run":
                print("    " + cap.replace("\n", "\n    "))
            kerja.append({"jenis": jenis, "akun": akun, "label": label, "cap": cap, "slot": slot,
                          "judul": ai_pilih["judul"], "url": ai_pilih["link"], "maks": maks})
    else:
        print("  ❌ tidak ada berita AI baru yang lolos")

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

    # ── create ──
    print("\n=== CREATE ===")
    dibuat = 0
    for k in kerja:
        jenis, label, cap, slot, url = k["jenis"], k["label"], k["cap"], k["slot"], k["url"]
        if not cap or not slot:
            print(f"  ⚠️ {label}/{jenis}: tidak ada caption/slot — skip")
            continue
        tgl = slot.strftime("%Y-%m-%d")
        lead = (slot.date() - sek.date()).days
        if lead > (1 if jenis == "artikel" else 0):
            print(f"  ⚠️ {label}/{jenis}: slot {tgl} (lead {lead} hari) di luar batas — skip")
            continue
        if (state["hari"].get(tgl) or {}).get(jenis):
            print(f"  ⏭️ {label}/{jenis} {tgl} sudah tercatat di state — skip")
            continue
        kunci = slot.astimezone(timezone.utc).strftime("%Y-%m-%dT%H")
        if kunci in terpakai[k["akun"]]:
            print(f"  ⏭️ {label}/{jenis} {slot.strftime('%d %b %H:%M')} slot sudah terisi — skip")
            continue
        masalah = validasi(cap, url, k["maks"])
        if masalah:
            print(f"  ❌ {label}/{jenis}: validasi gagal {masalah}")
            continue
        payload = {
            "accountId": k["akun"],
            "title": k["judul"][:95],
            "topic": "Artikel Website StudioSoft" if jenis == "artikel" else "Berita Teknologi AI",
            "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(f"  ✅ {label:<9} {jenis:<12} {slot.strftime('%d %b %H:%M WIB')} | id={sid} | {k['judul'][:45]}")
            state["hari"].setdefault(tgl, {})[jenis] = {
                "id": sid, "akun": k["akun"], "url": url, "judul": k["judul"],
                "scheduleAt": payload["scheduleAt"],
            }
            simpan_state(state)
            terpakai[k["akun"]].add(kunci)
            dibuat += 1
        except ReplizError as e:
            print(f"  ❌ {label}/{jenis}: {e}")
    print(f"\ntotal dibuat: {dibuat}")


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