#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""IG StudioSoft — reel harian dari video rakitan slide carousel Threads StudioSoft.

Perintah Bos 29 Sep 2026: *"akun IG StudioSoft .. kamu paham kan? seperti kamu buat reels untuk
facebook itu"* — jadi videonya ditayangkan sebagai **reel** di feed IG StudioSoft, bukan cuma story.

Pola sama dengan reel Facebook StudioSoft (29 Sep):
  - Media: MP4 1080x1920 h264+aac 17,5 dtk (5 slide x 3,5 dtk) — file yang sama dipakai story & reel.
  - Caption TIDAK dikarang: diambil dari caption yang sudah tayang untuk media yang sama
    (FB / Threads StudioSoft), lalu dirapikan (hashtag huruf kecil, ada link studiosoft.id, ada
    ajakan komentar, tanpa mention antar akun).
  - `type=video` (docs Repliz: video IG = Reel), `additionalInfo.isShareToFeed=True` supaya ikut
    tampil di grid profil, `replies=[]`.
  - Slot 11:00 WIB (09:00 sudah dipakai carousel IG, story di 07/14/20).

Pakai:
  python3 ss_ig_reel_daily.py               # dry-run: rencana + caption terpilih
  python3 ss_ig_reel_daily.py --go [--hari 4]
"""
import argparse
import datetime
import json
import os
import re
import subprocess
import sys
import time
import urllib.request

WS = "/home/mantapman/.hermes/profiles/atun/workspace"
sys.path.insert(0, WS)
from repliz_client import get_client  # noqa: E402
from ss_ig_story_3x import SERI, rakit, video_path, video_url, BASE  # noqa: E402

IG_SS = "6a38ac33c5ff4ce3a3231232"
TS_SS = "6a335b9ec5ff4ce3a3225c2a"
FB_SS = "6a334952c5ff4ce3a3225941"
WIB = datetime.timezone(datetime.timedelta(hours=7))
JAM = 11              # WIB
TOPIK = "IG StudioSoft Reel"
OFFSET = 11           # geser dari seri yang dipakai story hari itu
CAP_BANK = f"{WS}/ss_ig_reel_captions.json"

CTA = "Menurut Anda bagaimana? Tulis pendapat Anda di komentar. 👇"
CTA_PENDEK = "Tulis di kolom komentar. 👇"
RE_CTA = re.compile(r"komentar|pendapat|diskusi|setuju|share|pengalaman", re.I)
RE_TANYA = re.compile(r"bagaimana menurut Anda|menurut Anda bagaimana|menurut kamu|menurut Anda", re.I)
RE_MENTION = re.compile(r"@[A-Za-z0-9_.]+")
RE_CJK = re.compile(r"[\u4e00-\u9fff\u3040-\u30ff\uac00-\ud7af]")


def rapikan(cap: str) -> str:
    """Rapikan caption warisan supaya aman untuk reel IG."""
    cap = re.sub(r"[ \t]+\n", "\n", (cap or "").strip())
    cap = re.sub(r"\n{3,}", "\n\n", cap)
    # hashtag huruf kecil semua
    cap = re.sub(r"#(\w+)", lambda m: "#" + m.group(1).lower(), cap)
    # 0 mention antar akun kita
    cap = RE_MENTION.sub("", cap)
    cap = re.sub(r"[ \t]{2,}", " ", cap).strip()
    if not RE_CTA.search(cap):
        # kalau caption sudah menutup dengan pertanyaan, cukup tambah ajakan komentar yang pendek
        cap = cap.rstrip() + "\n\n" + (CTA_PENDEK if RE_TANYA.search(cap) else CTA)
    return cap


def link_hidup(u):
    """Cek link artikel dari luar (host ini IPv6 mati → wajib -4)."""
    p = subprocess.run(["curl", "-4", "-s", "-o", "/dev/null", "-w", "%{http_code}",
                        "--max-time", "25", "-L", u], capture_output=True, text=True)
    return p.stdout.strip() == "200"


def ganti_link_mati(cap, cadangan="https://studiosoft.id/jasa-digital-marketing-klinik/"):
    """Ganti link artikel yang sudah 404 ke artikel hidup (7 artikel HC dihapus 24 Sep 2026)."""
    for u in sorted(set(re.findall(r"https?://studiosoft\.id[^\s)]*", cap)), key=len, reverse=True):
        bersih = u.rstrip(".,")
        if not link_hidup(bersih):
            cap = cap.replace(bersih, cadangan)
            print(f"    ⚠️ link mati diganti: {bersih} → {cadangan}")
    return cap


def ambil_caption(docs):
    """Peta seri -> caption terbaik (terpanjang) dari FB / Threads StudioSoft."""
    bank = {}
    for d in docs:
        if d.get("accountId") not in (FB_SS, TS_SS):
            continue
        if str(d.get("status")).lower() not in ("success", "pending", "process", "processing"):
            continue
        cap = d.get("description") or ""
        if not cap.strip():
            continue
        for m in (d.get("medias") or []):
            mm = re.match(r"(ss_hc\w+?)_\d\d_", (m.get("url") or "").split("/")[-1])
            if not mm:
                continue
            seri = mm.group(1)
            if seri not in bank or len(cap) > len(bank[seri]["cap"]):
                bank[seri] = {"cap": cap, "dari": d.get("accountId"), "id": d.get("_id")}
    return bank


def seri_reel(tgl, eligible):
    """Seri buat reel hari itu — digeser OFFSET dari seri story hari itu."""
    return eligible[(tgl.toordinal() * 3 + OFFSET) % len(eligible)]


def layak(seri, bank):
    """Seri cuma dipakai kalau caption warisannya benar-benar utuh (link + ajakan komentar)."""
    cap = rapikan(bank.get(seri, {}).get("cap", ""))
    if len(cap) < 200:
        return None
    if "studiosoft.id" not in cap or not RE_CTA.search(cap):
        return None
    if RE_CJK.search(cap) or RE_MENTION.search(cap):
        return None
    return cap


def iso_utc(tgl, jam):
    return (datetime.datetime(tgl.year, tgl.month, tgl.day, jam, tzinfo=WIB)
            .astimezone(datetime.timezone.utc).strftime("%Y-%m-%dT%H:%M:%S.000Z"))


def semua_jadwal(c):
    docs, page = [], 1
    while page <= 200:
        r = None
        for a in range(6):
            try:
                r = c.get_schedules(page=page, limit=100)
                break
            except Exception:
                print(f"    ⚠️ halaman {page} gagal ({str(e)[:50]}), ulang…")
                time.sleep(5 + 3 * a)
        if r is None:
            raise SystemExit("gagal membaca jadwal Repliz")
        b = r.get("docs") or []
        if not b:
            break
        docs += b
        if len(b) < 100:
            break
        page += 1
    return docs


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--go", action="store_true")
    ap.add_argument("--hari", type=int, default=4)
    a = ap.parse_args()

    hari_ini = datetime.datetime.now(WIB).date()
    sekarang = datetime.datetime.now(datetime.timezone.utc)

    c = get_client()
    docs = semua_jadwal(c)
    bank = ambil_caption(docs)
    eligible = [s for s in SERI if layak(s, bank)]
    dibuang = [s for s in SERI if s not in eligible]
    print(f"sumber caption: {len(bank)} seri | layak dipakai: {len(eligible)} | dibuang: {dibuang}")

    cache = {}

    def cap_final(seri):
        """Caption siap pakai: sudah dirapikan + link mati sudah ditukar."""
        if seri not in cache:
            cache[seri] = ganti_link_mati(layak(seri, bank) or "")
        return cache[seri]

    rencana = []
    for ofs in range(a.hari):
        tgl = hari_ini + datetime.timedelta(days=ofs)
        at = iso_utc(tgl, JAM)
        if datetime.datetime.fromisoformat(at.replace("Z", "+00:00")) <= sekarang:
            continue
        rencana.append((at, tgl, seri_reel(tgl, eligible)))

    print(f"horizon {a.hari} hari — {len(rencana)} slot reel IG SS (jam {JAM:02d}:00 WIB)")

    masalah = []
    for at, tgl, seri in rencana:
        cap = cap_final(seri)
        if not cap or len(cap) < 200:
            masalah.append(f"{seri}: caption tidak layak")
            continue
        print(f"  {tgl} {JAM:02d}:00 WIB | {seri} | {len(cap)} char | {cap.splitlines()[0][:70]}")
    for m in masalah:
        print("❗", m)
    if masalah:
        print("\nBATAL: rapikan caption dulu.")
        return

    # simpan bank caption buat rujukan/audit
    json.dump({k: {"cap": rapikan(v["cap"]), "dari": v["dari"]} for k, v in bank.items()},
              open(CAP_BANK, "w"), indent=1, ensure_ascii=False)

    if not a.go:
        print("\n(dry-run — pakai --go untuk rakit video + buat jadwal)")
        return

    print("\n=== rakit video ===")
    for seri in sorted({s for _, _, s in rencana}):
        out = rakit(seri)
        if out:
            print(f"  ✅ {os.path.basename(out)} ({os.path.getsize(out) / 1e6:.2f} MB)")

    print("\n=== cek media di ingress funnel ===")
    for u in sorted({video_url(s) for _, _, s in rencana}):
        kode = urllib.request.urlopen(u, timeout=30).getcode()
        print(f"  {kode} {u.split('/')[-1]}")
        if kode != 200:
            raise SystemExit("media tidak 200 — stop")

    # anti-dobel: skip kalau slot 11:00 di akun IG SS sudah dipakai jadwal apa pun (selain story)
    ada = {d.get("scheduleAt") for d in docs
           if d.get("accountId") == IG_SS and d.get("type") != "story"}
    dibuat = []
    print("\n=== buat jadwal ===")
    for at, tgl, seri in rencana:
        if at in ada:
            print(f"  ⏭️  {tgl} {JAM:02d}:00 WIB — sudah ada, SKIP")
            continue
        cap = cap_final(seri)
        media = [{"url": video_url(seri), "type": "video", "alt": os.path.basename(video_path(seri))}]
        sid = None
        for attempt in range(1, 4):
            try:
                res = c.create_schedule(IG_SS, cap, at, title=f"Reel IG SS {tgl}",
                                        topic=TOPIK, type_="video", medias=media, replies=[],
                                        additional_info={"isShareToFeed": True})
                sid = res.get("scheduleId") or res.get("_id") or res.get("id")
                break
            except Exception as e:
                print(f"    ⚠️ percobaan {attempt}: {str(e)[:110]}")
                time.sleep(8)
        print(f"  {'✅' if sid else '❌'} {tgl} {JAM:02d}:00 WIB | {seri} | id={sid}")
        if sid:
            dibuat.append((sid, str(tgl), seri))

    print("\n=== verifikasi (baca ulang per id) ===")
    janggal = 0
    for sid, label, seri in dibuat:
        d = c.get_schedule(sid)
        d = d.get("data") or d
        if isinstance(d, dict) and "docs" in d:
            d = d["docs"][0]
        ok = d.get("status") == "pending" and d.get("type") == "video"
        janggal += 0 if ok else 1
        print(f"  {'✅' if ok else '⚠️'} {label} | status={d.get('status')} type={d.get('type')} "
              f"at={d.get('scheduleAt')} media={(d.get('medias') or [{}])[0].get('url', '').split('/')[-1]} "
              f"isShareToFeed={(d.get('additionalInfo') or {}).get('isShareToFeed')}")
    print(f"\nselesai: {len(dibuat)} jadwal dibuat, {janggal} janggal")


if __name__ == "__main__":
    main()
