#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""fb_ss_story_harian.py — PASANG STORY harian di Facebook StudioSoft.

Dasar: Bos (2 Okt 2026) - dashboard IG StudioSoft menunjukkan "Stories 5/14" (kurang) dan "Reception" kosong;
perintahnya: "Stories n reception tolong handle Facebook StudioSoft". Pipeline story FB StudioSoft sebelumnya
(ss-fb-mingguan) hanya 7 story dan habis 2 Okt -> dibuat pipeline harian.

Rencana: 3 story/hari (07:00, 14:00, 20:00 WIB) selama 10 hari = 30 story, tiap aset dipakai sekali.
Aset: 23 video story (`ss_storyvid_*.mp4`, 17,5 dtk) + 7 gambar story (`ss_story_hc37..43.jpg`, 1080x1920).

Aturan: type=story, caption KOSONG, replies=[], media WAJIB 200 di KEDUA ingress sebelum create,
idempoten (akun + slot), FB tidak menerima replies.

Mode: preview [N] | 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))
WS = "/home/mantapman/.hermes/profiles/atun/workspace"
IMG_DIR = os.path.join(WS, "openclaw-img")
ACC_FB = "6a334952c5ff4ce3a3225941"
STATE = "fb_ss_story_state.json"
JAM_WIB = [7, 14, 20]
HOST = "mantapserver.tail3947bb.ts.net"
INGRESS = ["103.84.155.153", "103.84.155.217"]
HARI = 10

VIDEO = sorted(f for f in os.listdir(IMG_DIR) if f.startswith("ss_storyvid_") and f.endswith(".mp4"))
GAMBAR = sorted(f for f in os.listdir(IMG_DIR) if f.startswith("ss_story_hc") and f.endswith(".jpg"))
ASET = [("video", f) for f in VIDEO] + [("image", f) for f in GAMBAR]


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_story(c, jumlah_hari):
    """3 slot/hari (07/14/20 WIB), lompati slot yang sudah terisi."""
    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_hari + 2):
        if len(hasil) >= jumlah_hari * len(JAM_WIB):
            break
        for jam in JAM_WIB:
            kandidat = datetime(hari.year, hari.month, hari.day, jam, 0, tzinfo=WIB)
            if kandidat <= sekarang + timedelta(minutes=15):
                continue
            kunci = kandidat.astimezone(timezone.utc).strftime("%Y-%m-%dT%H")
            if kunci in terpakai:
                continue
            terpakai.add(kunci)
            hasil.append(kandidat)
        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"
    hari = int(sys.argv[2]) if len(sys.argv) > 2 else HARI
    state = muat_state()
    terpakai_aset = {p["aset"] for p in state["posted"]}
    aset_sisa = [a for a in ASET if a[1] not in terpakai_aset]
    jumlah = hari * len(JAM_WIB)

    if mode == "preview":
        print(f"aset: {len(VIDEO)} video + {len(GAMBAR)} gambar = {len(ASET)} | sisa belum dipakai: {len(aset_sisa)}")
        print(f"rencana: {len(JAM_WIB)}/hari x {hari} hari = {jumlah} story")
        for i, (tipe, nama) in enumerate(aset_sisa[:jumlah]):
            print(f"  {i+1:>2}. [{tipe}] {nama}")
        return

    c = get_client()
    slots = slot_story(c, hari)
    print(f"slot story tersedia: {len(slots)}")

    print("gate 200 dua ingress untuk aset yang akan dipakai...")
    akan_pakai = aset_sisa[:len(slots)]
    for tipe, nama in akan_pakai:
        ok, pesan = cek_ingress(nama)
        if not ok:
            print("  ⛔ ABORT:", pesan)
            sys.exit(1)
    print(f"  {len(akan_pakai)} aset 200 di KEDUA ingress ✅")

    dibuat = 0
    for (tipe, nama), slot in zip(akan_pakai, slots):
        payload = {
            "accountId": ACC_FB,
            "title": f"Story FB StudioSoft {nama}",
            "topic": "Story FB StudioSoft harian",
            "type": "story",
            "description": "",
            "medias": [{"url": f"{IMG_BASE_URL}/openclaw-img/{nama}", "type": tipe, "alt": nama}],
            "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 | {tipe:<5} | {nama[:42]} | id={sid}")
            state["posted"].append({"aset": nama, "tipe": tipe, "id": sid,
                                    "scheduleAt": payload["scheduleAt"]})
            simpan_state(state)
            dibuat += 1
        except ReplizError as e:
            print(f"  ❌ gagal {nama}: {e}")
    print(f"\ntotal story dibuat: {dibuat}")


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