#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""fb_ss_reel_harian.py — REEL harian di Facebook StudioSoft (1/hari, 20:00 WIB).

Dasar: permintaan Bos "Stories n reception tolong handle Facebook StudioSoft" (2 Okt 2026).
Reel adalah format dengan jangkauan terbaik di Facebook, jadi dipakai untuk menaikkan reception/reach halaman.

Aset: 16 video `ss_reel_*.mp4` (1080x1920, rakitan slide carousel StudioSoft). Caption disesuaikan
dengan topik tiap video (dibaca dari frame video, bukan dikarang).

Aturan: type=reel, medias [{url, type:video}], replies=[] (FB wajib), hashtag huruf kecil, 0 CJK,
media WAJIB 200 di KEDUA ingress sebelum create, idempoten (akun + slot).

Mode: preview | run [N]
"""
import json
import os
import subprocess
import sys
from datetime import datetime, timedelta, timezone

from repliz_client import get_client, ReplizError, IMG_BASE_URL

WIB = timezone(timedelta(hours=7))
ACC_FB = "6a334952c5ff4ce3a3225941"
STATE = "fb_ss_reel_state.json"
HOST = "mantapserver.tail3947bb.ts.net"
INGRESS = ["103.84.155.153", "103.84.155.217"]
SLOTS = [20, 17, 11]   # preferensi jam WIB

# (nama file, topik dari frame video, caption)
REELS = [
    ("ss_reel_biru_05.mp4", "Cepat = Ranking",
     "SEO tidak bisa dipesan kilat. Yang bisa dipercepat adalah pekerjaan teknis dan strukturnya: "
     "halaman yang jelas, kecepatan situs, dan struktur data yang benar. Peringkat mengikuti setelah itu, "
     "biasanya dalam hitungan bulan, bukan hari.\n\n"
     "Kalau ada yang menjanjikan halaman baru langsung peringkat satu minggu depan, itu tanda perlu dipertanyakan.\n\n"
     "Menurut Anda, apa yang paling sering dijanjikan berlebihan soal SEO? Tulis di komentar.\n\n"
     "#seo #digitalmarketing #umkm"),
    ("ss_reel_biru_07.mp4", "On-page vs Off-page",
     "Dua pekerjaan SEO yang sering dicampur. On-page mengurus hal yang ada di dalam situs: judul, isi, struktur "
     "tautan, kecepatan. Off-page mengurus hal dari luar: sitasi, tautan masuk, reputasi merek di tempat lain.\n\n"
     "Urutan yang sehat: rapikan on-page dulu, karena tautan masuk yang bagus akan terbuang kalau halamannya "
     "sendiri belum jelas menjawab apa pun.\n\n"
     "Di bisnis Anda, bagian mana yang sudah rapi: dalam situs atau luar situs? Cerita di komentar.\n\n"
     "#seo #onpage #digitalmarketing"),
    ("ss_reel_biru_09.mp4", "SEO untuk UMKM",
     "Usaha kecil tidak perlu bersaing di kata kunci besar. Yang lebih menguntungkan: kata kunci yang menyebut "
     "daerah, layanan, dan jenis pembeli, karena pesaingnya lebih sedikit dan niat pencarinya sudah jelas.\n\n"
     "Mulailah dari sepuluh pertanyaan yang paling sering ditanyakan pelanggan. Jawab satu per satu di halaman "
     "terpisah, lalu hubungkan. Itu sudah cukup jadi fondasi.\n\n"
     "Usaha Anda bergerak di bidang apa, dan kata kunci apa yang paling sering dicari pembeli? Tulis di komentar.\n\n"
     "#seo #umkm #digitalmarketing"),
    ("ss_reel_biru_11.mp4", "Google Business Profile Klinik",
     "Untuk klinik dan layanan kesehatan, profil lokasi sering jadi pintu masuk pertama. Yang perlu lengkap: "
     "kategori, jam buka, nomor WhatsApp, foto tempat, layanan yang tersedia, dan jawaban atas pertanyaan umum.\n\n"
     "Ulasan juga berpengaruh besar, dan cara paling sederhana mendapatkannya adalah meminta setelah pasien "
     "selesai berobat, saat pengalamannya masih segar.\n\n"
     "Bagi yang mengelola layanan kesehatan, informasi apa yang paling sering ditanyakan calon pasien? "
     "Tulis di komentar.\n\n"
     "#seolokal #klinik #digitalmarketing"),
    ("ss_reel_biru_13.mp4", "Strategi Digital Marketing 2026",
     "Tiga hal yang berubah dalam pemasaran digital tahun ini. Pertama, orang makin sering bertanya ke asisten AI, "
     "jadi konten yang jelas dan berbukti lebih mudah dikutip. Kedua, pencarian makin lokal. Ketiga, biaya iklan "
     "naik, sehingga halaman yang siap menerima kunjungan jadi wajib.\n\n"
     "Yang tidak berubah: siapa pun yang lebih dulu menjawab pertanyaan calon pembeli dengan jelas biasanya yang "
     "dapat percakapan.\n\n"
     "Menurut Anda, perubahan mana yang paling terasa di bisnis Anda? Tulis di komentar.\n\n"
     "#digitalmarketing #strategi #bisnis2026"),
    ("ss_reel_biru_17.mp4", "Yang Mengubah Pengunjung Jadi Pembeli",
     "Yang membuat pengunjung jadi pembeli jarang soal desain. Biasanya empat hal ini: judul yang bicara ke masalah "
     "pembaca, manfaat yang dikemas konkret, rasa mendesak yang jujur, dan ajakan bertindak yang diulang di beberapa "
     "titik halaman.\n\n"
     "Satu halaman tanpa keempatnya akan tetap sepi meski iklannya jalan.\n\n"
     "Dari empat hal itu, mana yang paling sering terlewat di halaman Anda? Tulis di komentar.\n\n"
     "#landingpage #konversi #digitalmarketing"),
    ("ss_reel_biru_19.mp4", "Jenis Konten Sosmed",
     "Tiga jenis konten yang layak dijaga komposisinya: konten edukasi yang menjawab pertanyaan, konten bukti sosial "
     "berupa hasil kerja dan testimoni, serta konten penawaran yang jelas menyebut apa yang dijual.\n\n"
     "Banyak akun hanya berisi satu jenis, biasanya penawaran. Akibatnya audiens merasa dijual terus dan berhenti "
     "mengikuti. Kalau edukasinya cukup, penawaran justru diterima lebih baik.\n\n"
     "Dari tiga jenis itu, mana yang paling jarang Anda unggah? Tulis di komentar.\n\n"
     "#kontenmarketing #sosmed #umkm"),
    ("ss_reel_hc43.mp4", "Pasien 2026 Sudah Pindah ke Google",
     "Pasien sekarang membandingkan lewat pencarian sebelum menelepon: membaca halaman layanan, melihat foto, "
     "menyimak ulasan, lalu memutuskan. Kalau informasi itu tidak ada di halaman Anda, yang mereka baca adalah "
     "halaman fasilitas lain.\n\n"
     "Karena itu kelengkapan informasi bukan pekerjaan tambahan, tapi bagian dari pelayanan itu sendiri.\n\n"
     "Bagi yang berkecimpung di layanan kesehatan, informasi apa yang paling menentukan pilihan pasien? "
     "Tulis di komentar.\n\n"
     "#rumahsakit #klinik #digitalmarketing"),
    ("ss_reel_hc48.mp4", "10 Channel Digital Marketing Rumah Sakit",
     "Rumah sakit biasanya mengurus banyak kanal sekaligus: website layanan, profil lokasi di peta, media sosial, "
     "iklan pencarian, iklan sosial, konten edukasi dokter, survei kepuasan, WhatsApp pendaftaran, artikel blog, "
     "dan ulasan pasien.\n\n"
     "Yang sering terjadi: semua dikerjakan setengah, padahal kanal ini saling menopang. Website yang rapi membuat "
     "iklan lebih murah, dan ulasan yang baik membuat profil peta lebih sering muncul.\n\n"
     "Menurut Anda, kanal mana yang paling berpengaruh untuk kepercayaan pasien? Tulis di komentar.\n\n"
     "#rumahsakit #kesehatan #digitalmarketing"),
    ("ss_reel_putih_06.mp4", "Riset Keyword Pemula",
     "Cara paling sederhana memulai riset kata kunci: tulis sepuluh pertanyaan yang pelanggan tanyakan, lalu "
     "cek bagaimana orang mengetiknya di pencarian. Dari situ akan muncul pola kata kunci yang panjang dan "
     "spesifik, dan itu justru yang paling menguntungkan karena pesaingnya sedikit.\n\n"
     "Jangan mulai dari kata kunci yang paling besar. Mulai dari yang paling mungkin Anda menangi.\n\n"
     "Bagaimana cara Anda menentukan kata kunci selama ini? Cerita di komentar.\n\n"
     "#seo #katakunci #digitalmarketing"),
    ("ss_reel_putih_08.mp4", "Backlink Berkualitas",
     "Backlink masih berpengaruh, tapi bukan jumlahnya yang menentukan. Yang menilai adalah relevansi sumbernya, "
     "kualitas isi halaman yang menaut, dan apakah tautan itu datang sendiri atau dibeli massal.\n\n"
     "Cara yang lebih sehat dan tahan lama: menulis konten yang layak dirujuk, bekerja sama dengan komunitas atau "
     "asosiasi di bidang yang sama, dan memastikan nama bisnis Anda konsisten di direktori resmi.\n\n"
     "Pernah mencoba membangun tautan sendiri? Bagikan pengalaman Anda di komentar.\n\n"
     "#seo #backlink #digitalmarketing"),
    ("ss_reel_putih_10.mp4", "SEO Lokal Surabaya",
     "Untuk bisnis yang melayani satu kota, halaman per daerah mengalahkan satu halaman umum. Orang mencari "
     "dengan menyebut kelurahan, kecamatan, atau patokan yang mereka kenal.\n\n"
     "Lengkapi juga profil lokasi di peta, karena banyak pencarian berujung pada pertanyaan yang dekat sini mana. "
     "Untuk bisnis lokal, dua pekerjaan ini sering mendatangkan hasil paling cepat.\n\n"
     "Di kota Anda, daerah mana yang paling sering dicari calon pelanggan? Tulis di komentar.\n\n"
     "#seolokal #surabaya #digitalmarketing"),
    ("ss_reel_putih_14.mp4", "Manfaat Digital Marketing untuk UMKM",
     "Manfaat yang paling terasa bagi usaha kecil biasanya bukan penjualan yang meledak, melainkan empat hal ini: "
     "bisnis ditemukan orang yang belum mengenal Anda, pelanggan bisa menghubungi tanpa perantara, biaya promosi "
     "jadi lebih terukur, dan mereknya terlihat aktif sehingga lebih dipercaya.\n\n"
     "Semuanya menumpuk pelan, bukan dalam sepekan.\n\n"
     "Bagi Anda yang menjalankan usaha kecil, manfaat mana yang paling terasa? Tulis di komentar.\n\n"
     "#umkm #digitalmarketing #bisnisonline"),
    ("ss_reel_putih_16.mp4", "Landing Page Bisnis Kecil",
     "Halaman pendaratan yang berfungsi untuk usaha kecil tidak perlu panjang. Cukup menjawab: apa yang Anda "
     "jual, untuk siapa, berapa harganya atau rentangnya, bagaimana cara memesan, dan mengapa orang lain "
     "mempercayai Anda.\n\n"
     "Satu tombol WhatsApp yang jelas biasanya lebih efektif daripada lima formulir berbeda.\n\n"
     "Halaman pendaratan Anda sekarang sudah menjawab kelima hal itu? Kalau belum, mana yang tertinggal? "
     "Tulis di komentar.\n\n"
     "#landingpage #umkm #digitalmarketing"),
    ("ss_reel_putih_18.mp4", "Konten yang Dilirik",
     "Konten yang mendapat perhatian biasanya punya satu hal yang jelas di tiga detik pertama: angka, pertanyaan, "
     "atau pernyataan yang menantang kebiasaan. Sisanya, kecepatan membalas komentar dan keberanian menyebut "
     "harga, yang membuat audiens percaya.\n\n"
     "Yang paling sering membuat konten tenggelam bukan kualitas produksi, tapi pembukaan yang tidak menjanjikan "
     "apa pun.\n\n"
     "Menurut Anda, pembukaan seperti apa yang paling membuat Anda berhenti menggulir? Tulis di komentar.\n\n"
     "#kontenmarketing #sosmed #digitalmarketing"),
    ("ss_reel_putih_20.mp4", "Budget Medsos UMKM",
     "Mengatur anggaran media sosial untuk usaha kecil lebih mudah kalau dimulai dari angka yang sederhana: "
     "tentukan biaya yang sanggup dikeluarkan sebulan, bagi tiga bagian, yaitu produksi konten, iklan, dan "
     "cadangan untuk menguji hal baru.\n\n"
     "Setelah sebulan, geser anggaran ke bagian yang paling terbukti. Jangan membagi rata ke semua kanal, karena "
     "tidak ada yang cukup untuk dinilai.\n\n"
     "Berapa kanal yang Anda kelola sekarang, dan apakah anggarannya dibagi rata? Cerita di komentar.\n\n"
     "#umkm #anggaranmarketing #sosmed"),
]


def cek_ingress(nama):
    url = f"https://{HOST}/openclaw-img/{nama}"
    for ip in INGRESS:
        code = subprocess.run(["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", "--max-time", "30",
                               "--resolve", f"{HOST}:443:{ip}", url],
                              capture_output=True, text=True).stdout.strip()
        if code != "200":
            return False, f"{nama} @{ip} → {code}"
    return True, ""


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_harian(c, jumlah):
    sekarang = datetime.now(WIB)
    terpakai = set()
    for d in semua_jadwal(c, ACC_FB):
        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(jumlah * 2):
        if len(hasil) >= jumlah:
            break
        for jam in SLOTS:
            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():
    return json.load(open(STATE)) if os.path.exists(STATE) else {"posted": []}


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


def main():
    mode = sys.argv[1] if len(sys.argv) > 1 else "preview"
    jumlah = int(sys.argv[2]) if len(sys.argv) > 2 else len(REELS)
    state = muat_state()
    terpakai = {p["aset"] for p in state["posted"]}
    sisa = [r for r in REELS if r[0] not in terpakai][:jumlah]

    if mode == "preview":
        print(f"bank reel: {len(REELS)} | sisa: {len(sisa)}")
        for i, (nama, topik, cap) in enumerate(sisa):
            print(f"  {i+1:>2}. {nama:<26} {topik:<42} caption {len(cap)} char")
        return

    c = get_client()
    slots = slot_harian(c, len(sisa))
    print(f"slot reel tersedia: {len(slots)} -> {[s.strftime('%d %b %H:%M') for s in slots]}")
    print("gate 200 dua ingress...")
    for nama, _, _ in sisa[:len(slots)]:
        ok, pesan = cek_ingress(nama)
        if not ok:
            print("  ⛔ ABORT:", pesan)
            sys.exit(1)
    print("  semua media 200 di KEDUA ingress ✅")

    dibuat = 0
    for (nama, topik, cap), slot in zip(sisa, slots):
        payload = {
            "accountId": ACC_FB,
            "title": f"Reel FB StudioSoft — {topik}"[:90],
            "topic": "Reel FB StudioSoft harian",
            "type": "reel",
            "description": cap,
            "medias": [{"url": f"{IMG_BASE_URL}/openclaw-img/{nama}", "type": "video",
                        "alt": nama, "thumbnail": "", "customThumbnail": False}],
            "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"  ✅ {slot.strftime('%d %b %H:%M')} WIB | {topik[:44]:<46} | id={sid}")
            state["posted"].append({"aset": nama, "topik": topik, "id": sid,
                                    "scheduleAt": payload["scheduleAt"]})
            simpan_state(state)
            dibuat += 1
        except ReplizError as e:
            print(f"  ❌ gagal {nama}: {e}")
    print(f"\ntotal reel dibuat: {dibuat}")


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