#!/usr/bin/env python3
"""StudioSoft Healthcare — hc35 = RE-AIR hc21 "Website RS Cepat & Mobile-Friendly: 5 Kesalahan Fatal yang Bikin Pasien Kabur".

Konteks (cron run 13 Sep 2026, 02:00 WIB):
- Stok artikel healthcare studiosoft.id masih HABIS (artikel terbaru di website = 3 Sep 2026, non-healthcare).
- hc21 belum pernah tayang: SEMUA jadwalnya (26 Agu 2026, Threads/LinkedIn/FB) status `error`
  karena URL media waktu itu BARE (tanpa prefix /openclaw-img/). File JPEG ADA di openclaw-img/.
  hc19 & hc20 sudah di-re-air lebih dulu (hc33, hc34).
- Catatan: re-air hc20 (hc34) pada 12 Sep 02:00Z GAGAL lagi dgn `The media could not be fetched from
  this URI` (Threads) & `Missing or invalid image file` (FB) padahal URL diverifikasi 200 lewat kedua
  ingress -> intermiten di sisi Repliz/pengambilan media. Mitigasi: PIL-verify JPEG + HTTP 200 via
  KEDUA ingress IP + tulis log supaya bisa diaudit/di-re-air lagi.

ATURAN:
- Carousel/album = 09:00 WIB (FIXED 11 Sep 2026). Threads + FB album; Instagram TIDAK disentuh;
  LinkedIn TIDAK diberi image/carousel (FIXED 5 Sep 2026).
- LinkedIn StudioSoft = TEXT saja, bahas isi artikel, gaya storytelling ringan + ajak diskusi.
  Artikel hc21 BELUM pernah sukses di LinkedIn (3 jadwal lama semuanya error) -> dibuat 1 post text
  di slot kosong pertama (antrean LI penuh s/d 9 Okt 2026 01:00Z).
- FB: album, replies KOSONG (FB tidak support replies). Threads: album + 2 replies.
- Hashtag HURUF KECIL semua, 0 mention, caption wajib pancingan diskusi, Threads <=500 char.
"""
import json, os, re, subprocess, sys
from datetime import datetime, timezone, timedelta

sys.path.insert(0, "/home/mantapman/.openclaw/workspace-atun")
from repliz_client import ReplizClient, ACC
from diskusi_engine import tambahkan_diskusi

DRY = os.environ.get("DRY", "") == "1"
c = ReplizClient()

SLOT_UTC = "2026-09-13T02:00:00.000Z"        # 13 Sep 2026 09:00 WIB (carousel = jam 9)
SLOT_LABEL = "13 Sep 2026 09:00 WIB"
LI_SLOT_UTC = "2026-10-09T04:00:00.000Z"     # 9 Okt 2026 11:00 WIB (slot LI kosong pertama)
LI_SLOT_LABEL = "9 Okt 2026 11:00 WIB"

TITLE = "Website RS Cepat & Mobile-Friendly: 5 Kesalahan Fatal yang Bikin Pasien Kabur"
ARTICLE_URL = "https://studiosoft.id/website-rumah-sakit-gagal-pasien/"
HASHTAG_LINE = "#studiosoft #websiters #digitalmarketing #rumahsakit #klinik"
LI_HASHTAG = "#studiosoft #websiters #digitalmarketing #rumahsakit #layanankesehatan"

MEDIA_URLS = [
    "https://mantapserver.tail3947bb.ts.net/openclaw-img/ss_hc21_websiters_01_cover.jpg",
    "https://mantapserver.tail3947bb.ts.net/openclaw-img/ss_hc21_websiters_02_kenapa.jpg",
    "https://mantapserver.tail3947bb.ts.net/openclaw-img/ss_hc21_websiters_03_isi.jpg",
    "https://mantapserver.tail3947bb.ts.net/openclaw-img/ss_hc21_websiters_04_langkah.jpg",
    "https://mantapserver.tail3947bb.ts.net/openclaw-img/ss_hc21_websiters_05_cta.jpg",
]
MEDIA_FILES = [u.split("/")[-1] for u in MEDIA_URLS]
IMG_DIR = "/home/mantapman/.openclaw/workspace-atun/openclaw-img"

THREADS_BASE = """Website RS bukan pajangan — ia wajah pertama pasien sebelum datang.

Kalau lambat atau kacau dibuka dari HP, pasien langsung cari RS lain. Padahal cukup:
✅ Buka di bawah 3 detik
✅ Rapi dan nyaman di layar HP
✅ Info dokter & jadwal cepat ketemu
✅ Pendaftaran online yang mulus

Website yang cepat dan mobile-friendly bikin pasien percaya sebelum mereka datang."""

FB_BASE = """Website RS dan klinik yang lambat atau berantakan di HP diam-diam mengusir calon pasien setiap hari.

Riset Google: 53% pengunjung pergi kalau halaman butuh lebih dari 3 detik untuk terbuka. Dan lebih dari 70% orang mencari RS dari smartphone.

5 kesalahan fatal yang paling sering kami temukan di website RS:
1. Loading lambat — pasien pergi sebelum lihat isinya
2. Info dokter & jadwal sulit ditemukan
3. Tampilan berantakan di layar HP
4. Tidak ada tombol booking / WhatsApp yang jelas
5. Tidak terhubung ke sistem pendaftaran online

Yang perlu dibenahi:
✓ Uji kecepatan & optimasi gambar
✓ Pastikan tampil rapi di HP
✓ Permudah akses info dokter & jadwal
✓ Sambungkan pendaftaran online

Bold statement: RS yang websitenya cuma "ada" tanpa membantu pasien memutuskan, sedang membuang pasien ke kompetitor.

Baca artikel lengkapnya: """ + ARTICLE_URL

LI_BASE = """Dulu saya pikir website rumah sakit itu sekadar pelengkap — yang penting RS-nya bagus.

Sampai saya melihat data: 53% pengunjung meninggalkan halaman yang butuh lebih dari 3 detik untuk terbuka. Dan lebih dari 70% orang mencari informasi RS dari smartphone.

Artinya, banyak calon pasien sudah "pergi" bukan karena layanan RS-nya kurang, tapi karena websitenya lambat, membingungkan, dan tidak menunjukkan langkah berikutnya.

Di artikel terbaru di website kami, ada 5 kesalahan fatal yang paling sering ditemukan pada website RS dan klinik: loading lambat, info dokter tersembunyi, tidak mobile-friendly, CTA lemah, dan tidak terintegrasi dengan pendaftaran online.

Pelajaran yang saya ambil: website yang baik bukan yang paling cantik, tapi yang paling mudah membantu pasien mengambil keputusan — cepat, jelas, dan bisa booking dalam beberapa klik.

Bacaan lengkapnya: """ + ARTICLE_URL

CTA_RE = re.compile(r"komentar|pendapat|diskusi|setuju|share|pengalaman|menurut", re.I)


def build(base, platform, industri, tones, limit=None, max_try=60):
    for tone in tones:
        for _ in range(max_try):
            txt = tambahkan_diskusi(base, platform=platform, industri=industri,
                                    tone=tone, force=True)
            if CTA_RE.search(txt) and (limit is None or len(txt) <= limit):
                return txt
    return None


THREADS = build(THREADS_BASE, "threads", "rumah_sakit", ["netral"], limit=500)
FB = build(FB_BASE, "facebook", "rumah_sakit", ["opini", "netral"])
LI = build(LI_BASE, "linkedin", "rumah_sakit", ["netral", "opini"])

REPLY1 = ("Baca selengkapnya: %s\n\nKonsultasi website RS/klinik yang cepat & mobile-friendly:\n"
          "www.studiosoft.id" % ARTICLE_URL)
REPLY2 = HASHTAG_LINE


def check_caption(name, txt, limit=None, require_cta=True):
    ok = True
    if txt is None:
        print("  ❌ %s: gagal disusun" % name)
        return False
    if limit and len(txt) > limit:
        print("  ❌ %s: %d char > %d" % (name, len(txt), limit))
        ok = False
    m = re.findall(r"@\w+", txt)
    if m:
        print("  ❌ %s: ada mention %s" % (name, m))
        ok = False
    caps = [t for t in re.findall(r"#(\w+)", txt) if t != t.lower()]
    if caps:
        print("  ❌ %s: hashtag KAPITAL %s" % (name, caps))
        ok = False
    if require_cta and not CTA_RE.search(txt):
        print("  ❌ %s: tidak ada pancingan diskusi" % name)
        ok = False
    print("  %s %s: %d char" % ("✅" if ok else "⚠️", name, len(txt)))
    return ok


def pil_ok():
    from PIL import Image
    ok = True
    for f in MEDIA_FILES:
        p = os.path.join(IMG_DIR, f)
        try:
            Image.open(p).verify()
            im = Image.open(p)
            print("  ✅ %s %s %s" % (f, im.size, im.mode))
        except Exception as e:
            print("  ❌ %s: %s" % (f, e))
            ok = False
    return ok


def verify_urls_internet():
    ips = subprocess.run(["dig", "+short", "mantapserver.tail3947bb.ts.net", "A", "@1.1.1.1"],
                         capture_output=True, text=True).stdout.split()
    ips = [i for i in ips if i.count(".") == 3]
    ok = True
    for u in MEDIA_URLS:
        for ip in ips[:2]:
            r = subprocess.run(["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}",
                                "--max-time", "20", "--resolve",
                                "mantapserver.tail3947bb.ts.net:443:%s" % ip, u],
                               capture_output=True, text=True)
            code = r.stdout.strip()
            print("  %s @%s: %s" % (u.split("/")[-1], ip, code))
            if code != "200":
                ok = False
    return ok


def verify_article():
    """studiosoft.id lambat dari host ini -> butuh HTTP/1.1 + timeout besar; harus 200."""
    r = subprocess.run(["curl", "-sIL", "--http1.1", "--max-time", "200",
                        "-A", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)", ARTICLE_URL,
                        "-o", "/dev/null", "-w", "%{http_code}"],
                       capture_output=True, text=True)
    code = (r.stdout or "").strip()
    print("  artikel %s -> %s" % (ARTICLE_URL, code))
    return code == "200"


def scan(needles, statuses, accounts=None):
    hits = []
    accounts = accounts or (ACC.STUDIOSOFT_THREADS, ACC.STUDIOSOFT_FB, ACC.STUDIOSOFT_LINKEDIN)
    page = 1
    while True:
        d = c.get("/public/schedule?page=%d&limit=100" % page)
        for s in d.get("docs", []):
            if s.get("accountId") not in accounts:
                continue
            if (s.get("status") or "").lower() not in statuses:
                continue
            blob = (s.get("description") or "") + (s.get("title") or "") + \
                   " ".join((m.get("url") or "") for m in (s.get("medias") or []))
            if any(n in blob for n in needles):
                hits.append((s.get("accountId"), (s.get("scheduleAt") or "")[:16],
                             (s.get("title") or "")[:45], s.get("type"),
                             len(s.get("medias") or []), len(s.get("replies") or [])))
        if page >= (d.get("totalPages") or 1):
            break
        page += 1
    return hits


def slot_busy_us(aids, slot_utc):
    base = datetime.fromisoformat(slot_utc.replace("Z", "+00:00"))
    busy = set()
    page = 1
    while True:
        d = c.get("/public/schedule?page=%d&limit=100" % page)
        for s in d.get("docs", []):
            if s.get("accountId") in aids and (s.get("status") or "").lower() in ("pending", "scheduled", "draft"):
                st = (s.get("scheduleAt") or "")[:19]
                if st:
                    busy.add((s.get("accountId"), st))
        if page >= (d.get("totalPages") or 1):
            break
        page += 1
    clash = []
    for off in range(6):
        t = (base + timedelta(minutes=off)).strftime("%Y-%m-%dT%H:%M:%S")
        for a in aids:
            if (a, t) in busy:
                clash.append((a, t))
    return clash


def next_free_09(aids):
    base = datetime.fromisoformat(SLOT_UTC.replace("Z", "+00:00"))
    for day in range(0, 10):
        cand = (base + timedelta(days=day)).strftime("%Y-%m-%dT02:00:00.000Z")
        if not slot_busy_us(aids, cand):
            return cand
    return None


def main():
    global SLOT_UTC, SLOT_LABEL
    wib = datetime.now(timezone(timedelta(hours=7)))
    print("Time WIB: %s" % wib.strftime("%Y-%m-%d %H:%M"))

    print("\n1) SLOT CAROUSEL 09:00 WIB (Threads+FB):")
    aids = [ACC.STUDIOSOFT_THREADS, ACC.STUDIOSOFT_FB]
    clash = slot_busy_us(aids, SLOT_UTC)
    if clash:
        print("  ⚠️ %s terisi: %s" % (SLOT_LABEL, clash))
        cand = next_free_09(aids)
        if not cand:
            print("  ❌ tidak ada slot 09:00 bebas 10 hari ke depan — batal.")
            return False
        SLOT_UTC = cand
        SLOT_LABEL = (datetime.fromisoformat(cand.replace("Z", "+00:00"))
                      + timedelta(hours=7)).strftime("%d %b %Y 09:00 WIB")
    print("  ✅ slot carousel: %s (%s)" % (SLOT_LABEL, SLOT_UTC))

    print("\n2) CAPTION:")
    ok = True
    ok &= check_caption("Threads", THREADS, 500)
    ok &= check_caption("FB", FB)
    ok &= check_caption("LinkedIn", LI)
    ok &= check_caption("Reply1", REPLY1, require_cta=False)
    ok &= check_caption("Reply2", REPLY2, require_cta=False)
    if not ok:
        print("\n❌ caption gagal aturan — batal.")
        return False
    print("\n--- THREADS (%d char) ---\n%s" % (len(THREADS), THREADS))
    print("\n--- LINKEDIN (%d char) ---\n%s" % (len(LI), LI))

    print("\n3) IDEMPOTENSI (media hc21 masih pending/kecantol?):")
    hits = scan(MEDIA_FILES[:1], ("pending", "scheduled", "draft"))
    if hits:
        print("  ⚠️ sudah ada jadwal hc21 -> SKIP create:")
        for h in hits:
            print("   ", h)
        return True
    print("  ✅ belum ada jadwal hc21 yang menunggu tayang")

    print("\n4) VALIDASI FILE JPEG (lokal):")
    if not pil_ok():
        print("  ❌ file JPEG rusak — batal.")
        return False

    print("\n5) MEDIA VIA JALUR INTERNET (kedua ingress):")
    if not verify_urls_internet():
        print("  ❌ ada media tidak 200 — batal.")
        return False

    print("\n6) LINK ARTIKEL:")
    if not verify_article():
        print("  ❌ link artikel tidak 200 — batal.")
        return False

    if DRY:
        print("\n[DRY RUN] semua cek lolos; tidak ada create.")
        return True

    base_dt = datetime.fromisoformat(SLOT_UTC.replace("Z", "+00:00"))
    r2 = (base_dt + timedelta(minutes=1)).strftime("%Y-%m-%dT%H:%M:%S.000Z")
    r3 = (base_dt + timedelta(minutes=2)).strftime("%Y-%m-%dT%H:%M:%S.000Z")
    medias = [c.make_media(u, "image", "slide%d" % (i + 1)) for i, u in enumerate(MEDIA_URLS)]

    results = {}
    print("\n7) CREATE — THREADS:")
    try:
        alb = c.schedule_album(ACC.STUDIOSOFT_THREADS, THREADS, SLOT_UTC, medias, title=TITLE)
        pid = alb.get("scheduleId") or alb.get("_id") or alb.get("id")
        print("  album -> %s" % pid)
        c.create_schedule(ACC.STUDIOSOFT_THREADS, REPLY1, r2, type_="text", replies=[pid])
        c.create_schedule(ACC.STUDIOSOFT_THREADS, REPLY2, r3, type_="text", replies=[pid])
        print("  2 replies OK")
        results["threads"] = True
    except Exception as e:
        print("  GAGAL %s" % e)
        results["threads"] = False

    print("\n   FACEBOOK:")
    try:
        alb = c.schedule_album(ACC.STUDIOSOFT_FB, FB, SLOT_UTC, medias, title=TITLE)
        pid = alb.get("scheduleId") or alb.get("_id") or alb.get("id")
        print("  album -> %s (replies kosong)" % pid)
        results["fb"] = True
    except Exception as e:
        print("  GAGAL %s" % e)
        results["fb"] = False

    print("\n   LINKEDIN (text-only, %s):" % LI_SLOT_LABEL)
    try:
        res = c.create_schedule(ACC.STUDIOSOFT_LINKEDIN, LI, LI_SLOT_UTC, type_="text", replies=[])
        pid = res.get("scheduleId") or res.get("_id") or res.get("id")
        print("  text -> %s" % pid)
        results["linkedin"] = True
    except Exception as e:
        print("  GAGAL %s" % e)
        results["linkedin"] = False

    print("\n" + "=" * 55)
    ok = all(results.values())
    print("SEMUA PLATFORM TERJADWAL" if ok else "GAGAL: %s" % [k for k, v in results.items() if not v])

    if ok:
        print("\n8) VERIFIKASI (re-list pending):")
        for h in scan(MEDIA_FILES[:1], ("pending",)) + scan([ARTICLE_URL.rstrip("/")], ("pending",),
                                                            accounts=(ACC.STUDIOSOFT_LINKEDIN,)):
            print("   ", h)
        with open("/home/mantapman/.openclaw/workspace-atun/studiosoft_healthcare_log.txt", "a") as f:
            f.write("\n[%s WIB] hc35 (RE-AIR hc21) | %s | %s | Threads+FB album %s (%s) + 2 replies Threads | "
                    "LinkedIn TEXT %s (%s) | catatan: stok artikel healthcare masih HABIS (artikel terbaru di "
                    "website 3 Sep 2026, non-healthcare); hc21 belum pernah tayang (error URL media bare "
                    "26 Agu) -> re-air dgn URL /openclaw-img/ diverifikasi 200 via KEDUA ingress + PIL. "
                    "PERHATIAN: re-air hc20 (hc34) 12 Sep 02:00Z GAGAL lagi ('media could not be fetched') "
                    "padahal URL 200 -> intermiten Repliz; perlu diaudit/re-air ulang. BUTUH artikel "
                    "healthcare baru dari Bos (kandidat re-air berikutnya: hc20).\n"
                    % (wib.strftime("%Y-%m-%d %H:%M"), TITLE, ARTICLE_URL, SLOT_LABEL,
                       SLOT_UTC[11:16] + "Z", LI_SLOT_LABEL, LI_SLOT_UTC))
        print("Log ditulis.")
    return ok


if __name__ == "__main__":
    sys.exit(0 if main() else 1)
