#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""SERI StudioSoft: BISNIS PROPERTI & MANUFAKTUR — SEO + digital marketing, semua segmen.

20 segmen (10 properti + 10 manufaktur), 1 post/hari/akun, tayang di:
  * Threads StudioSoft  (6a335b9ec5ff4ce3a3225c2a)  caption <=500 char + 1 reply anak
  * Facebook StudioSoft (6a334952c5ff4ce3a3225941)  text, replies=[]
  * LinkedIn StudioSoft (6a334c49c5ff4ce3a32259c9)  text, replies=[]

Link selalu ke 2 artikel pilar studiosoft.id:
  /digital-marketing-bisnis-properti/  dan  /digital-marketing-bisnis-manufaktur/

Mode:
  preview [N]  -> cetak caption + slot (tidak create apa pun)
  run [N]      -> create jadwal (idempoten: segmen+akun yang sudah ada = skip)

Aturan: link dari website sendiri, hashtag lowercase, 0 mention, ajakan diskusi,
Threads <=500 char (assert), FB & LinkedIn replies=[].
"""
import json
import os
import re
import sys
from datetime import datetime, timedelta, timezone

from repliz_client import get_client, ReplizError

WIB = timezone(timedelta(hours=7))
SLOTS_WIB = [5, 8, 11, 14, 17, 20, 23]
STATE = "ss_seri_properti_manufaktur_state.json"

LINK_PROPERTI = "https://studiosoft.id/digital-marketing-bisnis-properti/"
LINK_MANUFAKTUR = "https://studiosoft.id/digital-marketing-bisnis-manufaktur/"

TARGETS = [
    {"key": "threads_ss", "label": "Threads StudioSoft", "account": "6a335b9ec5ff4ce3a3225c2a", "kind": "threads"},
    {"key": "fb_ss", "label": "Facebook StudioSoft", "account": "6a334952c5ff4ce3a3225941", "kind": "fb"},
    {"key": "li_ss", "label": "LinkedIn StudioSoft", "account": "6a334c49c5ff4ce3a32259c9", "kind": "li"},
]

# ── Bank 20 segmen ──────────────────────────────────────────────────────────
# hook    : pembuka (dipakai Threads & jadi kalimat kunci di FB/LinkedIn)
# isi1/isi2: dua temuan operasional (bullet di FB/LinkedIn)
# kw      : contoh kata kunci yang dibidik
# fb_par  : konteks untuk audiens Facebook
# li_par  : framing untuk audiens LinkedIn
# cta     : pertanyaan pancingan komentar
S = [
    # ── PROPERTI ────────────────────────────────────────────────────────────
    dict(nama="Agen properti & broker", industri="properti", tag="#properti #agenproperti #seolokal",
         hook="Agen properti yang menang di pencarian bukan yang paling rajin posting, tapi yang listingnya bisa ditemukan per lokasi.",
         isi1="Satu listing satu halaman, lengkap dengan nama daerah dan tipe rumahnya.",
         isi2="Konten soal harga pasaran membuat pemilik rumah menghubungi Anda sendiri.",
         kw="agen properti surabaya, jual rumah cepat, harga rumah per daerah",
         fb_par="Calon pembeli properti biasanya menghubungi beberapa agen dalam waktu yang sama. Yang lebih dulu membalas dengan informasi lengkap yang biasanya dapat percakapan lanjutan.",
         li_par="Di bisnis agen properti, kepercayaan terbentuk jauh sebelum transaksi. Jejak digital yang rapi membuat pemilik rumah bersedia menyerahkan listingnya.",
         cta="menurut Anda bagian mana yang paling sulit: dapat listing baru atau meyakinkan pembeli lama?"),
    dict(nama="Developer perumahan & cluster", industri="properti", tag="#properti #developer #seolokal",
         hook="Halaman per cluster mengalahkan satu halaman brosur untuk pencarian rumah dijual di suatu daerah.",
         isi1="Cantumkan harga mulai dari dan simulasi cicilan sederhana di halaman.",
         isi2="Progres bangunan yang diperbarui tiap bulan menjawab keraguan terbesar pembeli.",
         kw="rumah dijual nama kecamatan, cluster nama proyek, rumah baru nama kota",
         fb_par="Pembeli rumah baru selalu punya satu ketakutan yang sama: proyeknya berhenti di tengah jalan. Bukti progres yang diperbarui rutin lebih menenangkan daripada kalimat pemasaran apa pun.",
         li_par="Pemasaran proyek perumahan bekerja paling baik lewat halaman per cluster dengan data yang bisa diverifikasi, bukan satu halaman katalog yang memuat semua unit.",
         cta="apa yang paling menentukan kepercayaan Anda saat melihat proyek perumahan baru?"),
    dict(nama="Sewa rumah & kontrakan", industri="properti", tag="#properti #sewarumah #seolokal",
         hook="Pencari kontrakan memutuskan dalam beberapa hari, dan yang menentukan cuma tiga: harga, fasilitas, dan ketersediaan.",
         isi1="Harga tertulis mengalahkan halaman yang minta chat dulu untuk tahu angkanya.",
         isi2="Perbarui status kosong terisi, karena listing basi cepat kehilangan kepercayaan.",
         kw="kontrakan nama daerah, sewa rumah bulanan nama kota, sewa rumah dekat kampus",
         fb_par="Pencari sewa rumah membandingkan banyak pilihan dalam satu malam. Kalau harga dan fasilitas tidak tertulis, halaman Anda biasanya dilewati begitu saja.",
         li_par="Untuk properti sewa, kecepatan informasi adalah keunggulan bersaing. Halaman yang menampilkan harga dan ketersediaan langsung memangkas percakapan yang tidak perlu.",
         cta="bagi pemilik sewa, apa yang paling sering ditanyakan calon penyewa?"),
    dict(nama="Kos & sewa kamar", industri="properti", tag="#properti #kos #seolokal",
         hook="Kata kunci kos hampir selalu menyebut kampus, rumah sakit, atau pabrik, bukan nama kota.",
         isi1="Iklan radius sekitar kampus dan kawasan kerja lebih murah daripada iklan satu kota.",
         isi2="Foto kamar apa adanya dan aturan penghuni yang jelas menekan pertanyaan berulang.",
         kw="kos dekat nama kampus, kos putri nama daerah, kos eksklusif nama kelurahan",
         fb_par="Pencari kos bergerak cepat dan biasanya menentukan pilihan dari tiga hal: jarak, harga, dan kondisi kamar. Tiga hal itu paling baik ditampilkan langsung, bukan dijelaskan lewat chat.",
         li_par="Pemilik kos yang mengelola beberapa lokasi mendapat manfaat terbesar dari halaman per properti, karena pencarian penyewa selalu spesifik ke lokasi dan tipe kamar.",
         cta="kalau Anda pernah cari kos, informasi apa yang paling Anda butuhkan di awal?"),
    dict(nama="Apartemen & kondotel", industri="properti", tag="#properti #apartemen #propertysurabaya",
         hook="Penyewa apartemen mencari dengan nama gedung, bukan nama kota. Halaman per tower mengalahkan halaman umum.",
         isi1="Video unit satu menit menghemat banyak percakapan sebelum survei lokasi.",
         isi2="Untuk sewa harian, jangan bergantung penuh pada platform pihak ketiga.",
         kw="apartemen sewa nama tower, apartemen harian nama area, unit studio nama gedung",
         fb_par="Untuk apartemen, pembeli dan penyewa sudah tahu gedung mana yang mereka incar. Yang mereka cari adalah unit yang cocok, lengkap dengan foto dan harga.",
         li_par="Pengelola apartemen dan kondotel yang punya halaman sendiri mendapat dua hal: komisi yang tidak terpotong platform, dan data tamu yang bisa dipasarkan lagi.",
         cta="bagi Anda, apa yang lebih menentukan: harga sewa atau kondisi unitnya?"),
    dict(nama="Tanah kavling & investasi", industri="properti", tag="#properti #kavling #investasitanah",
         hook="Pembeli kavling tidak takut harga, tapi takut legalitas dan akses jalan. Dua hal itu yang harus dibuktikan lebih dulu.",
         isi1="Tampilkan status tanah, lebar jalan masuk, dan jarak ke jalan utama.",
         isi2="Video singkat akses jalan lebih meyakinkan daripada paragraf deskripsi.",
         kw="tanah dijual nama daerah, kavling siap bangun nama kota, tanah SHM nama kecamatan",
         fb_par="Pembeli tanah biasanya sudah pernah kena pengalaman buruk atau mendengar cerita buruk. Kejelasan dokumen dan akses jalan adalah dua hal pertama yang mereka periksa.",
         li_par="Untuk properti investasi, materi pemasaran yang bekerja adalah yang bisa diverifikasi. Bagi investor, risiko legal jauh lebih penting daripada potongan harga.",
         cta="apa yang paling Anda periksa dulu sebelum membeli tanah?"),
    dict(nama="Kontraktor & renovasi", industri="properti", tag="#properti #renovasi #kontraktor",
         hook="Pencari jasa renovasi sudah punya niat tinggi. Yang mereka cari bukan promosi, tapi gambaran biaya dan bukti pekerjaan.",
         isi1="Portofolio sebelum-sesudah dengan lingkup pekerjaan lebih meyakinkan daripada daftar proyek.",
         isi2="Estimasi biaya bertingkat menarik prospek yang lebih serius.",
         kw="jasa renovasi rumah nama kota, kontraktor bangun rumah, biaya renovasi per meter",
         fb_par="Di jasa renovasi, pertanyaan pertama hampir selalu soal biaya. Halaman yang menjelaskan apa saja yang mempengaruhi harga akan mendapat prospek yang lebih siap dibanding halaman yang hanya menulis hubungi kami.",
         li_par="Kontraktor yang menampilkan lingkup pekerjaan dan lama pengerjaan pada portofolionya mendapat prospek dengan ekspektasi lebih realistis, sehingga negosiasinya lebih pendek.",
         cta="menurut Anda, apa yang paling membuat orang percaya pada kontraktor?"),
    dict(nama="Jasa interior & desain ruang", industri="properti", tag="#properti #interior #desaininterior",
         hook="Interior itu bisnis visual, tapi keputusannya tetap lewat pencarian: orang mencari jenis ruang dan nama kota.",
         isi1="Satu halaman per jenis ruang membantu orang menemukan karya yang mirip kebutuhannya.",
         isi2="Cantumkan paket harga per ruang supaya calon klien tidak menebak anggarannya.",
         kw="jasa desain interior nama kota, kitchen set nama kota, desain kamar minimalis",
         fb_par="Calon klien interior biasanya sudah mengumpulkan referensi sebelum menghubungi. Yang mereka cari adalah penyedia yang gayanya cocok dan anggarannya masuk.",
         li_par="Untuk studio interior, katalog karya yang tertata per jenis ruang membuat proses penawaran lebih cepat karena calon klien langsung menunjukkan referensi yang mereka inginkan.",
         cta="kalau memilih jasa interior, apa yang lebih dulu Anda lihat: portofolio atau harga?"),
    dict(nama="Property management, ruko, gudang, kantor", industri="properti", tag="#properti #ruko #sewagudang",
         hook="Penyewa ruko dan gudang mencari berdasarkan spesifikasi: luas, daya listrik, dan akses kendaraan.",
         isi1="Satu halaman per properti jauh lebih berguna daripada brosur gabungan.",
         isi2="LinkedIn layak dipakai karena pengambil keputusannya bekerja di perusahaan.",
         kw="sewa ruko nama daerah, sewa gudang nama kota, sewa kantor nama area",
         fb_par="Penyewa komersial mencari cepat dan membandingkan banyak pilihan. Spesifikasi teknis yang lengkap membuat mereka bisa menyaring sendiri tanpa harus bertanya bolak-balik.",
         li_par="Untuk properti komersial, keputusan diambil manajemen, jadi materi yang rapi dan spesifik menaikkan peluang dibawa ke rapat internal.",
         cta="bagi penyewa komersial, spesifikasi mana yang paling menentukan?"),
    dict(nama="Villa & penginapan wisata", industri="properti", tag="#properti #villa #penginapan",
         hook="Menggantungkan semua pemesanan pada platform pihak ketiga membuat margin tergerus dan data tamu tidak pernah jadi milik Anda.",
         isi1="Website sendiri dengan kalender ketersediaan memberi margin lebih baik.",
         isi2="Konten tentang sekitar lokasi menambah alasan tamu menginap lebih lama.",
         kw="villa nama daerah, penginapan murah nama tujuan wisata, sewa villa harian",
         fb_par="Tamu penginapan hampir selalu mencari dua hal bersamaan: tempat menginap dan aktivitas di sekitarnya. Konten yang menjawab keduanya mendapat pemesanan lebih panjang.",
         li_par="Bagi pengelola penginapan, kanal pemesanan langsung menurunkan biaya komisi sekaligus membangun hubungan dengan tamu yang bisa dipasarkan ulang.",
         cta="menurut Anda, apa yang membuat tamu memesan langsung tanpa lewat platform?"),
    # ── MANUFAKTUR ──────────────────────────────────────────────────────────
    dict(nama="Makanan & minuman olahan", industri="manufaktur", tag="#manufaktur #umkm #distributor",
         hook="Pabrik makanan sering salah alamat pemasaran karena mencampur pembeli ritel, distributor, dan konsumen akhir dalam satu halaman.",
         isi1="Pisahkan halaman distributor (harga grosir) dan halaman konsumen (cerita produk).",
         isi2="Tampilkan izin edar apa adanya, karena itu pertanyaan pertama buyer.",
         kw="pabrik nama produk, supplier makanan nama kota, distributor frozen food",
         fb_par="Produk makanan yang sudah enak belum tentu mudah dijual kalau distributor tidak menemukan informasi kemasan, masa simpan, dan syarat pengiriman.",
         li_par="Untuk pabrik makanan dan minuman, pemasaran paling produktif datang dari kejelasan data produk, bukan dari konten hiburan. Distributor butuh angka, bukan cerita.",
         cta="bagi produsen makanan, kanal mana yang paling banyak mendatangkan pembeli serius?"),
    dict(nama="Maklon & kosmetik", industri="manufaktur", tag="#manufaktur #maklon #kosmetik",
         hook="Yang mencari jasa maklon bukan konsumen, tapi pemilik merek yang sedang menghitung MOQ dan waktu produksi.",
         isi1="Jelaskan MOQ, waktu produksi, dan siapa yang mengurus izin edar.",
         isi2="Transparansi kepemilikan formula menentukan keputusan mereka.",
         kw="jasa maklon kosmetik, maklon skincare terdaftar BPOM, maklon minuman",
         fb_par="Pemilik merek baru biasanya menghubungi lima sampai sepuluh pabrik maklon sekaligus. Yang menjawab paling jelas soal MOQ dan alur produksi biasanya yang dipilih.",
         li_par="Industri maklon bersaing di kejelasan proses. Pabrik yang menampilkan alur kerja dari konsultasi sampai pengiriman lebih cepat masuk daftar pendek.",
         cta="kalau mencari pabrik maklon, informasi apa yang paling Anda butuhkan lebih dulu?"),
    dict(nama="Tekstil, garmen & konveksi", industri="manufaktur", tag="#manufaktur #konveksi #garmen",
         hook="Konveksi punya dua pembeli yang sangat berbeda: reseller yang mengejar harga, dan perusahaan yang mengejar spesifikasi dan dokumen.",
         isi1="Satu halaman untuk reseller, satu halaman untuk pengadaan perusahaan.",
         isi2="Sertakan jumlah pesanan lama sebagai bukti kapasitas, bukan hanya foto produk.",
         kw="konveksi nama kota, jahit seragam kantor, supplier kain nama kota",
         fb_par="Pemesanan konveksi sering gagal bukan karena harga, tapi karena ekspektasi bahan dan lama pengerjaan yang tidak disepakati sejak awal.",
         li_par="Untuk klien korporat, kemampuan produksi dan ketepatan waktu lebih menentukan daripada selisih harga per lusin.",
         cta="bagi Anda pengguna jasa konveksi, bahan atau ketepatan waktu yang lebih penting?"),
    dict(nama="Otomotif & komponen", industri="manufaktur", tag="#manufaktur #otomotif #sparepart",
         hook="Bengkel mencari part dengan nomor, bukan dengan kata kunci umum. Halaman yang memuat kode part akan ditemukan, yang menulis sparepart lengkap tidak.",
         isi1="Cantumkan kecocokan part dengan model dan tahun kendaraan.",
         isi2="Alat pencarian part di halaman menghemat percakapan berulang.",
         kw="sparepart nama kendaraan, distributor komponen nama kota, kode part",
         fb_par="Bengkel bekerja dengan waktu cepat. Kalau stok dan kode part tidak jelas, mereka biasanya langsung pindah ke pemasok lain.",
         li_par="Distributor komponen yang menyajikan data part yang rapi lebih mudah masuk ke daftar pemasok tetap bengkel dan pemilik armada.",
         cta="bagi pemilik bengkel, apa yang paling menentukan saat memilih pemasok part?"),
    dict(nama="Logam, fabrikasi & permesinan", industri="manufaktur", tag="#manufaktur #fabrikasi #mesinindustri",
         hook="Pembeli jasa fabrikasi sudah tahu ukuran, material, dan jumlah. Yang mereka cari adalah kapasitas mesin dan toleransi yang bisa dikerjakan.",
         isi1="Tulis kapasitas mesin dan tingkat presisi di halaman, bukan hanya foto workshop.",
         isi2="Formulir penawaran sebaiknya menanyakan volume, material, dan tenggat.",
         kw="jasa fabrikasi nama kota, bubut CNC nama kota, pembuatan conveyor",
         fb_par="Pesanan fabrikasi jarang datang dari percakapan singkat. Buyer teknis membaca kemampuan pabrik lebih dulu sebelum menghubungi.",
         li_par="Di industri permesinan, spesifikasi teknis yang terbuka memangkas banyak pertanyaan awal dan mempercepat penawaran.",
         cta="bagi pelaku industri, informasi teknis apa yang paling sering ditanyakan calon klien?"),
    dict(nama="Plastik, kemasan & percetakan kemasan", industri="manufaktur", tag="#manufaktur #kemasan #packaging",
         hook="Pencarian kemasan sangat spesifik: jenis, ukuran, dan bahan. Halaman yang menjawab ketiganya mendapat buyer, yang hanya memampang foto tidak.",
         isi1="Satu halaman per jenis kemasan dengan tabel ukuran dan ketebalan.",
         isi2="Tampilkan minimum order dan waktu produksi supaya buyer bisa menyaring sendiri.",
         kw="supplier standing pouch, cetak kemasan custom, botol PET nama ukuran",
         fb_par="Pemilik merek yang mencari kemasan biasanya sedang menyiapkan peluncuran produk dan dikejar waktu. Kejelasan MOQ dan estimasi produksi menentukan pilihan.",
         li_par="Industri kemasan punya volume permintaan yang besar, dan mayoritas datang dari pencarian spesifik. Halaman per produk adalah aset yang bekerja terus-menerus.",
         cta="bagi pengguna jasa kemasan, MOQ atau waktu produksi yang lebih krusial?"),
    dict(nama="Mebel, kayu & kerajinan", industri="manufaktur", tag="#manufaktur #mebel #furniture",
         hook="Pasar mebel bercabang tiga: konsumen rumah, kontraktor proyek, dan buyer luar negeri. Satu halaman tidak bisa melayani semuanya.",
         isi1="Pisahkan materi untuk proyek (kapasitas dan jadwal) dan untuk konsumen (bentuk dan harga).",
         isi2="Halaman berbahasa Inggris membuka pertimbangan buyer ekspor.",
         kw="pabrik mebel nama kota, furniture proyek hotel, kitchen set nama kota",
         fb_par="Untuk pesanan proyek, kepastian jadwal sering lebih penting daripada harga. Kontraktor tidak ingin furnitur datang terlambat di akhir pekerjaan.",
         li_par="Pabrik mebel yang menyiapkan materi ekspor dengan spesifikasi dan dokumen lengkap punya peluang lebih besar masuk ke percakapan dengan buyer luar negeri.",
         cta="bagi Anda, mana yang lebih menentukan: desain atau ketepatan waktu pengiriman?"),
    dict(nama="Elektronik, panel listrik & otomasi", industri="manufaktur", tag="#manufaktur #panel #otomasi",
         hook="Panel listrik bukan produk yang dibeli karena murah. Buyer menimbang standar yang dipakai dan rekam jejak proyeknya.",
         isi1="Sebut standar dan komponen yang dipakai, karena itu pertanyaan teknis pertama.",
         isi2="Dokumentasi proyek sejenis lebih meyakinkan daripada potongan harga.",
         kw="panel LVMDP, jasa wiring kontrol, pabrik panel nama kota",
         fb_par="Kesalahan di pekerjaan kelistrikan mahal akibatnya. Pembeli akan memilih penyedia yang tampak paling paham risikonya, bukan yang paling murah.",
         li_par="Untuk pekerjaan kelistrikan dan otomasi industri, rekam jejak proyek adalah materi pemasaran paling kuat, terutama bila disertai jenis industrinya.",
         cta="dalam pekerjaan teknis, apa yang lebih Anda utamakan: harga atau rekam jejak?"),
    dict(nama="Kimia, cat & pelapis", industri="manufaktur", tag="#manufaktur #industri #coating",
         hook="Pembeli bahan kimia mencari data teknis: daya sebar, waktu kering, ketahanan. Tanpa angka, halaman mereka hanya jadi brosur.",
         isi1="Tampilkan data teknis singkat per produk, bukan hanya nama dagang.",
         isi2="Kelengkapan dokumen bahan berbahaya mempercepat proses pengadaan.",
         kw="supplier cat epoxy nama kota, coating anti karat, perekat industri",
         fb_par="Pengguna bahan industri biasanya teknisi yang sudah tahu spesifikasi yang dibutuhkan. Mereka menyaring cepat, dan halaman tanpa data teknis langsung ditinggalkan.",
         li_par="Untuk produk kimia dan pelapis, kepercayaan dibangun lewat data uji dan konsistensi. Itu sebabnya materi teknis lebih berpengaruh daripada promosi.",
         cta="bagi pengguna bahan industri, data teknis apa yang paling sering Anda cari?"),
    dict(nama="Farmasi, alkes & produk kesehatan", industri="manufaktur", tag="#manufaktur #alkes #kesehatan",
         hook="Pengambil keputusan di segmen ini institusi: rumah sakit, klinik, apotek. Pembeliannya mengikuti standar pengadaan.",
         isi1="Cantumkan nomor izin edar dan kategori produk dengan jelas.",
         isi2="Kesiapan dokumen menentukan sebelum harga dibahas, termasuk untuk katalog elektronik.",
         kw="distributor alat kesehatan nama kota, supplier alkes, pabrik handscoon",
         fb_par="Pengadaan alat kesehatan melewati beberapa meja persetujuan. Materi yang rapi dan lengkap membuat produk Anda lebih cepat lolos penyaringan awal.",
         li_par="Di sektor kesehatan, kelengkapan dokumen adalah pintu masuk. Setelah itu barulah harga dan layanan purnajual dibahas.",
         cta="bagi tim pengadaan, dokumen atau layanan purnajual yang lebih menentukan?"),
]


# ── Caption builders ────────────────────────────────────────────────────────

def link_segmen(s):
    return LINK_PROPERTI if s["industri"] == "properti" else LINK_MANUFAKTUR


def cta_bersih(s):
    """CTA dari bank kadang sudah dibuka 'menurut Anda ...' — jangan dobel di template."""
    t = re.sub(r"^(menurut\s+(anda|kamu)\s*,?\s*)", "", s["cta"].strip(), flags=re.I)
    return t[0].upper() + t[1:]


def caption_threads(s, idx=0):
    link = link_segmen(s)
    q = cta_bersih(s)
    varian = [
        f"{s['hook']}\n\n"
        f"• {s['isi1']}\n\n"
        f"{link}\n\n"
        f"{q} Tulis di komentar ya.\n\n{s['tag']}",
        f"{s['hook']}\n\n"
        f"Yang perlu dibenahi lebih dulu: {s['isi1'].lower()}\n\n"
        f"Panduan lengkapnya: {link}\n\n"
        f"{q} Cerita di komentar ya.\n\n{s['tag']}",
        f"{s['hook']}\n\n"
        f"Mulai dari sini: {s['isi1'].lower()}\n\n"
        f"Selengkapnya: {link}\n\n"
        f"{q} Tulis pendapatmu di komentar.\n\n{s['tag']}",
    ]
    return varian[idx % len(varian)]


def reply_threads(s):
    return (f"Kata kunci yang biasanya dibidik segmen ini: {s['kw']}.\n\n"
            f"Satu lagi yang sering kelewat: {s['isi2'].lower()}\n\n"
            f"Kalau kamu pernah mengalaminya, cerita dikit di komentar biar yang lain ikut belajar.")


def caption_fb(s, idx=0):
    link = link_segmen(s)
    q = cta_bersih(s)
    pembuka = [
        s["hook"],
        f"Satu hal yang sering kelewat di segmen ini: {s['isi1'].lower()}",
        f"{s['hook']} Saya sering menemui pola yang sama di beberapa klien.",
    ][idx % 3]
    return (f"{pembuka}\n\n"
            f"{s['fb_par']}\n\n"
            f"Yang perlu dibenahi lebih dulu:\n"
            f"• {s['isi1']}\n• {s['isi2']}\n\n"
            f"Panduan lengkap semua segmen (10 properti, 10 manufaktur) ada di artikel ini:\n{link}\n\n"
            f"{q} Tulis di kolom komentar, saya bantu jawab satu per satu.\n\n"
            f"{s['tag']} #seo #digitalmarketing")


def caption_li(s, idx=0):
    link = link_segmen(s)
    q = cta_bersih(s)
    pembuka = [
        s["li_par"],
        f"{s['li_par']} Pola ini berulang hampir di semua klien yang kami tangani.",
        f"Dari pengalaman kami: {s['li_par'][0].lower()}{s['li_par'][1:]}",
    ][idx % 3]
    return (f"{pembuka}\n\n"
            f"{s['hook']}\n\n"
            f"Dua hal yang paling sering jadi penghambat:\n"
            f"• {s['isi1']}\n• {s['isi2']}\n\n"
            f"Kerangka lengkapnya, termasuk kata kunci yang layak dibidik per segmen, saya tulis di sini:\n{link}\n\n"
            f"{q} Saya tertarik mendengar pengalaman teman-teman di kolom komentar.\n\n"
            f"{s['tag']} #seo #digitalmarketing")


def validasi(cap, link, maks):
    err = []
    if link not in cap:
        err.append("link hilang")
    ekor = cap[-200:].lower()
    if not re.search(r"komentar|diskusi|pendapat|tulis di|share|pengalaman|menurut", ekor):
        err.append("tanpa ajakan diskusi di ekor caption")
    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}")
    cjk = [ch for ch in cap if "\u3000" <= ch <= "\u9fff" or "\uac00" <= ch <= "\ud7af"]
    if cjk:
        err.append(f"CJK: {cjk[:5]}")
    return err


# ── Slot & state ────────────────────────────────────────────────────────────

def semua_jadwal(c, account_id):
    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([d for d in docs if d.get("accountId") == account_id])
        if len(docs) < 100:
            break
        page += 1
    return out


def slot_seri(c, account_id, jumlah):
    """Daftar slot (<=`jumlah`), satu slot per hari, 7 slot WIB, lompati yang sudah terisi."""
    sekarang = datetime.now(WIB)
    terpakai = set()
    for d in semua_jadwal(c, account_id):
        if d.get("status") not in ("pending", "draft", "processing"):
            continue
        sa = d.get("scheduleAt") or ""
        if len(sa) >= 13:
            terpakai.add(sa[:13])
    hasil = []
    hari = sekarang.date()
    for _ in range(60):
        if len(hasil) >= jumlah:
            break
        for jam in SLOTS_WIB:
            kandidat = datetime(hari.year, hari.month, hari.day, jam, 0, tzinfo=WIB)
            if kandidat <= sekarang + timedelta(minutes=20):
                continue
            kunci = kandidat.astimezone(timezone.utc).strftime("%Y-%m-%dT%H")
            if kunci in terpakai:
                continue
            terpakai.add(kunci)
            hasil.append(kandidat)
            break
        hari += timedelta(days=1)
    return hasil


def muat_state():
    if os.path.exists(STATE):
        return json.load(open(STATE))
    return {"posted": []}


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


def sudah(state, key, nama):
    return any(p["akun"] == key and p["segmen"] == nama for p in state["posted"])


# ── Main ────────────────────────────────────────────────────────────────────

def main():
    mode = sys.argv[1] if len(sys.argv) > 1 else "preview"
    jumlah = int(sys.argv[2]) if len(sys.argv) > 2 else 20
    state = muat_state()

    if mode == "preview":
        for t in TARGETS:
            maks = 500 if t["kind"] == "threads" else 3000
            build = {"threads": caption_threads, "fb": caption_fb, "li": caption_li}[t["kind"]]
            print(f"\n########## {t['label']} ##########")
            for i, s in enumerate(S[:jumlah]):
                cap = build(s, i)
                err = validasi(cap, link_segmen(s), maks)
                print(f"\n--- [{i+1}] {s['nama']} ({s['industri']}) | {len(cap)} char | "
                      f"{'OK' if not err else err}")
                print("    " + cap.replace("\n", "\n    "))
                if t["kind"] == "threads":
                    print(f"    > reply: {reply_threads(s)}")
        return

    c = get_client()
    dibuat = 0
    for t in TARGETS:
        maks = 500 if t["kind"] == "threads" else 3000
        build = {"threads": caption_threads, "fb": caption_fb, "li": caption_li}[t["kind"]]
        sisa = [s for s in S if not sudah(state, t["key"], s["nama"])]
        print(f"\n=== {t['label']} — {len(sisa)} segmen belum dijadwalkan ===")
        if not sisa:
            continue
        slots = slot_seri(c, t["account"], min(jumlah, len(sisa)))
        print(f"    slot tersedia: {len(slots)}")
        for s, slot in zip(sisa[:len(slots)], slots):
            idx = S.index(s)
            cap = build(s, idx)
            err = validasi(cap, link_segmen(s), maks)
            if err:
                print(f"  ⚠️ SKIP {s['nama']}: {err}")
                continue
            payload = {
                "accountId": t["account"],
                "title": f"StudioSoft — {s['nama']}"[:90],
                "topic": f"Seri {s['industri'].title()}: SEO & Digital Marketing",
                "type": "text",
                "description": cap,
                "medias": [],
                "scheduleAt": slot.astimezone(timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.000Z"),
                "replies": ([{"text": reply_threads(s), "username": ""}]
                            if t["kind"] == "threads" else []),
            }
            try:
                res = c.post("/public/schedule", payload)
                sid = res.get("scheduleId") or res.get("_id") or res.get("id")
                print(f"  ✅ {slot.strftime('%d %b %H:%M')} WIB | {s['nama']} | id={sid}")
                state["posted"].append({"akun": t["key"], "segmen": s["nama"], "id": sid,
                                        "scheduleAt": payload["scheduleAt"],
                                        "link": link_segmen(s)})
                simpan_state(state)
                dibuat += 1
            except ReplizError as e:
                print(f"  ❌ gagal {s['nama']}: {e}")
    print(f"\ntotal dibuat: {dibuat}")


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