#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""Susun 14 carousel TSC untuk 18-31 Oktober 2026 dari artikel situs sendiri.

Alasan pemilihan topik: kueri nyata Search Console (90 hari) + halaman yang benar-benar
dibuka di GA4 (lihat drafts/tsc_permintaan_20261014.md). Isi tiap slide diambil dari
artikel TSC sendiri lewat WP REST, lalu dipadatkan otomatis.

Hasil: modul topik `tsc_topik18.py` ... `tsc_topik31.py` (berisi CAROUSEL dict).
Jalankan: python3 tsc_gen_topik.py [kode ...]
"""
import html as H
import json
import pprint
import re
import subprocess
import sys

WS = "/home/mantapman/.hermes/profiles/atun/workspace"
DOM = "https://www.theskincodeclinic.com"

# tanggal, kode, id artikel, judul cover, aksen, kicker, corak, foto
TOPIK = [
    (18, "p18", 10173, "Dokter kulit\nbuka sekarang", "jam praktik hari ini", "Warga Sidoarjo dan Krian",
     "gelap", "tsc_foto_batch/fb_10617538.jpg"),
    (19, "p19", 10171, "Biaya dokter\nkulit", "apa yang menentukan", "Sebelum datang",
     "terang", "tsc_foto_batch/fb_11464447.jpg"),
    (20, "p20", 10162, "Facial\ndi Sidoarjo", "jenis dan cara memilih", "Perawatan wajah",
     "biru", "tsc_foto_batch/fb_13632902.jpg"),
    (21, "p21", 2014, "Memilih klinik\nkecantikan", "yang aman dan tepat", "Panduan warga",
     "gelap", "tsc_foto_batch/fb_19999458.jpg"),
    (22, "p22", 8379, "Hapus\ntahi lalat", "aman dan estetis", "Tindakan klinik",
     "terang", "pria_8_1543737.jpg"),
    (23, "p23", 1691, "Kutil kulit", "kenali dan tangani", "Kulit dan penularan",
     "biru", "tsc_foto_batch/fb_12390582.jpg"),
    (24, "p24", 9251, "Bekas jerawat\ndi Krian", "perawatan yang tepat", "Perawatan klinik",
     "gelap", "pria_4_5712770.jpg"),
    (25, "p25", 1713, "4 grade\njerawat", "kenali sebelum diobati", "Sebelum salah obat",
     "terang", "tsc_foto_batch/fb_10209450.jpg"),
    (26, "p26", 9247, "Kulit\nberminyak", "rutinitas yang benar", "Perawatan harian",
     "biru", "tsc_foto_batch/fb_13205972.jpg"),
    (27, "p27", 1758, "Gatal dan\niritasi", "kapan harus ke dokter", "Keluhan umum",
     "gelap", "pria_2_5601967.jpg"),
    (28, "p28", 1801, "Merawat luka", "agar tidak berbekas", "Pertolongan awal",
     "terang", "tsc_foto_batch/fb_1001901.jpg"),
    (29, "p29", 1936, "Kulit anak", "kapan periksa ke dokter", "Dermatologi anak",
     "biru", "tsc_foto_batch/fb_19571098.jpg"),
    (30, "p30", 1608, "Flek hitam\ndi wajah", "penyebab dan solusinya", "Wajah kusam",
     "gelap", "tsc_foto_batch/fb_18130135.jpg"),
    (31, "p31", 1593, "Kulit\nbercahaya", "tanpa hasil instan", "Perawatan menyeluruh",
     "terang", "tsc_ads_sample/pex_10_10933469.jpg"),
]

FOTO_DIR = {"pria_": "tsc_foto_baru/"}


def ambil_artikel(pid):
    out = subprocess.run(["curl", "-4", "-sS", "--max-time", "55",
                          f"{DOM}/wp-json/wp/v2/posts/{pid}"], capture_output=True, text=True, timeout=90)
    d = json.loads(out.stdout)
    return d["title"]["rendered"], d["content"]["rendered"]


def bersih(t):
    t = re.sub(r"<[^>]+>", " ", t)
    t = H.unescape(t)
    t = t.replace("\u00a0", " ").replace("\u2019", "'").replace("\u201c", '"').replace("\u201d", '"')
    return re.sub(r"\s+", " ", t).strip()


def bagian(html):
    """Daftar (tag, teks) berurutan: h2/h3/p/li. Paragraf yang berakhir ':' dianggap subjudul."""
    out = []
    for m in re.finditer(r"<(h2|h3|p|li)[^>]*>(.*?)</\1>", html, re.S | re.I):
        tag = m.group(1).lower()
        mentah = m.group(2)
        teks = bersih(mentah)
        if not teks:
            continue
        if tag == "p":
            # paragraf yang hanya berisi teks tebal, atau diakhiri titik dua -> subjudul
            hanya_tebal = re.fullmatch(r"\s*<(strong|b)[^>]*>.*?</\1>\s*", mentah, re.S | re.I)
            if (hanya_tebal or (len(teks) <= 90 and teks.endswith(":"))) and len(teks) <= 110:
                out.append(("h3", teks.rstrip(":")))
                continue
        out.append((tag, teks))
    return out


AKHIR_OK = (".", "!", "?")


def kalimat_valid(k, min_=32, maks=96):
    """Poin hanya boleh berupa kalimat utuh (diakhiri titik/tanya/seru), tidak menggantung."""
    k = k.strip()
    if not (min_ <= len(k) <= maks):
        return None
    if not k.endswith(AKHIR_OK):
        return None
    return k


def poin_dari(teks_list, maks_poin=5):
    """Ambil poin-poin berupa kalimat utuh; kalimat kepanjangan dipecah, yang menggantung dibuang."""
    out = []
    for t in teks_list:
        t = t.strip()
        calon = [t] if len(t) <= 96 else []
        if len(t) > 96:
            calon = re.split(r"(?<=[.!?])\s+", t)
        for k in calon:
            k = kalimat_valid(k)
            if k:
                out.append(k)
            if len(out) >= maks_poin:
                break
        if len(out) >= maks_poin:
            break
    return out[:maks_poin]


def frasa_utuh(teks, maks=150, min_=45):
    """Ambil beberapa kalimat utuh pertama yang muat, tanpa memotong di tengah kalimat."""
    kal = [k.strip() for k in re.split(r"(?<=[.!?])\s+", teks) if k.strip()]
    out = ""
    for k in kal:
        if not k.endswith(AKHIR_OK):
            continue
        if len(out) + len(k) + 1 <= maks:
            out = (out + " " + k).strip()
        else:
            break
    return out if len(out) >= min_ else None


def potong(teks, maks):
    """Pemotongan hanya untuk subjudul: potong di batas kata, tanpa menambah titik palsu."""
    teks = teks.strip()
    if len(teks) <= maks:
        return teks
    awal = teks[:maks]
    if " " in awal:
        awal = awal[: awal.rfind(" ")]
    return awal.rstrip(" ,;:.")


def pecah_kalimat(p, maks=4):
    """Pecah paragraf jadi kalimat pendek yang bisa dipakai sebagai poin."""
    kal = re.split(r"(?<=[.!?])\s+", p)
    out = []
    for k in kal:
        k = k.strip()
        if 30 <= len(k) <= 190:
            out.append(k)
        if len(out) >= maks:
            break
    return out


def grup_isi(items):
    """Kelompokkan jadi (subjudul, [teks mentah]) dari pasangan h2/h3 + li/paragraf."""
    grup, kini = [], None
    for tag, teks in items:
        if tag in ("h2", "h3"):
            if kini and kini[1]:
                grup.append(kini)
            kini = [teks, []]
        elif kini is not None and tag in ("li", "p"):
            kini[1].append(teks)
    if kini and kini[1]:
        grup.append(kini)
    return grup


def judul_rapi(t):
    t = t.split("?")[0].split(":")[0].strip()
    t = re.sub(r"^\d+\s+", "", t)
    return t


def jalur_foto(f):
    return f if "/" in f else f"tsc_foto_baru/{f}"


def bangun(pid, kode, judul_cover, aksen, kicker, foto):
    judul_art, html = ambil_artikel(pid)
    items = bagian(html)
    par = [t for tag, t in items if tag == "p" and len(t) > 60]
    grup = []
    for judul_g, teks_mentah in grup_isi(items):
        poin = poin_dari(teks_mentah, 5)
        if len(poin) >= 2:
            grup.append([judul_g, poin])
    if len(grup) < 2 or len(par) < 2:
        print(f"   ✗ {kode}: bagian tidak cukup (grup {len(grup)}, paragraf {len(par)})")
        return None

    # lengkapi kalau artikelnya cuma punya 2 bagian berstruktur
    if len(grup) < 4:
        sisa = poin_dari(par[-5:], 6)
        tambahan = []
        for i in range(0, len(sisa), 3):
            potongan = sisa[i:i + 3]
            if len(potongan) >= 1:
                tambahan.append(["Hal lain yang perlu diketahui", potongan])
        grup = grup + tambahan
        if len(grup) < 3:
            print(f"   ✗ {kode}: bagian tetap kurang setelah dilengkapi ({len(grup)})")
            return None

    lead2 = None
    for p in par[:3]:
        lead2 = frasa_utuh(p, 150)
        if lead2:
            break
    catatan = None
    for p in reversed(par[-3:]):
        catatan = frasa_utuh(p, 150)
        if catatan:
            break
    if not lead2:
        print(f"   ✗ {kode}: tidak ada kalimat utuh untuk lead")
        return None

    def blok(g, maks_poin=5):
        return [("sub", potong(judul_rapi(g[0]), 52)), ("poin", g[1][:maks_poin])]

    if len(grup) > 3:
        grup_sisa = grup[3]
    else:  # lengkapi slide 3 dari paragraf sisa supaya isinya tetap padat
        sisa = poin_dari(par[-3:], 3)
        grup_sisa = ["Perlu diketahui", sisa] if len(sisa) >= 1 else None
    if not grup_sisa:
        print(f"   ✗ {kode}: grup keempat kosong")
        return None

    slide2_blok = [("lead", lead2)] + blok(grup[0]) + blok(grup[1])
    if catatan:
        slide2_blok.append(("catatan", catatan))
    slide2 = dict(judul=judul_rapi(grup[0][0]), kicker=judul_rapi(grup[0][0])[:42].upper(), blok=slide2_blok)

    lead3 = None
    for p in par[1:4]:
        lead3 = frasa_utuh(p, 150)
        if lead3 and lead3 != lead2:
            break
    slide3_blok = ([("lead", lead3)] if lead3 else []) + blok(grup[2]) + blok(grup_sisa)
    slide3 = dict(judul=judul_rapi(grup[2][0]), kicker=judul_rapi(grup[2][0])[:42].upper(),
                  blok=slide3_blok, cta=True)

    ringkas = []
    for p in par[1:6]:
        for k in re.split(r"(?<=[.!?])\s+", p):
            k = kalimat_valid(k, 40, 92)
            if k and k not in ringkas:
                ringkas.append(k)
            if len(ringkas) >= 3:
                break
        if len(ringkas) >= 3:
            break

    wa = "0851-1744-4417"
    hashtag = "#theskincodeclinic #dokterkulit #klinikkulit #sidoarjo #krian #perawatankulit"
    ig = (f"{judul_cover.replace(chr(10), ' ')}. {ringkas[0] if ringkas else ''}\n\n"
          f"{lead2}\n\n"
          + "\n".join(f"• {p}" for p in (grup[0][1][:4] + grup[2][1][:3]))
          + f"\n\nAda keluhan serupa? Cerita di komentar.\n\n📲 Konsultasi & booking via WhatsApp: {wa}\n\n{hashtag}")
    threads = (f"{judul_cover.replace(chr(10), ' ')}. {ringkas[0] if ringkas else ''}\n\n{lead2}\n\n"
               + "\n".join(f"• {p}" for p in grup[0][1][:3])
               + f"\n\nWA {wa}\n#theskincodeclinic #dokterkulit #klinikkulit")
    li = (f"{judul_art}\n\n{lead2}\n\n"
          + "\n\n".join(f"{judul_rapi(g[0])}:\n" + "\n".join(f"• {p}" for p in g[1][:5]) for g in grup[:4])
          + f"\n\nKonsultasi & booking via WhatsApp: {wa}\n\n"
            "#klinikkulit #dermatologi #perawatankulit #sidoarjo #kesehatankulit")

    return dict(kode=kode, judul=judul_cover, kicker=kicker, aksen=aksen, foto=jalur_foto(foto),
                ringkas=ringkas, slide=[slide2, slide3],
                caption_ig=ig, caption_threads=threads, caption_linkedin=li)


if __name__ == "__main__":
    pilih = sys.argv[1:]
    jadi = 0
    for tgl, kode, pid, judul, aksen, kicker, corak, foto in TOPIK:
        if pilih and kode not in pilih:
            continue
        print(f"— {tgl} Okt | {kode} | artikel {pid}")
        data = bangun(pid, kode, judul, aksen, kicker, foto)
        if not data:
            continue
        slug = f"tsc-topic-{tgl}"
        data.update(corak=corak, foto_pusat=[800, 850], foto_radius=[430, 540])
        isi = "#!/usr/bin/env python3\n# -*- coding: utf-8 -*-\n" \
              f"\"\"\"Topik carousel TSC {tgl} Oktober 2026 — otomatis dari artikel {pid}.\"\"\"\n\n" \
              "CAROUSEL = " + pprint.pformat({slug: data}, width=120, sort_dicts=False) + "\n"
        nama = f"tsc_topik{tgl}.py"
        with open(f"{WS}/{nama}", "w", encoding="utf-8") as f:
            f.write(isi)
        print(f"   ✓ {nama} | cover '{judul.replace(chr(10), ' ')}' | poin slide2 {len(data['slide'][0]['blok'])} blok")
        jadi += 1
    print(f"\nselesai: {jadi} modul topik")
