#!/usr/bin/env python3
"""Schedule FB DAS Bengkel Indonesia — 13 Sep 2026.

Cron fb-das-harian jalan 10:00 UTC = 17:00 WIB → slot 17:00 WIB sudah LEWAT saat script
jalan (preseden schedule_das_fb_20260901..12.py: semua tayang 13:00 UTC = 20:00 WIB).
Script memilih sendiri slot kosong PERTAMA hari ini yang masih di depan (>=10 menit) dari
7-slot WIB → hari ini = 20:00 WIB (13:00 UTC).

Artikel: https://dasindonesia.com/bengkel-spesialis-toyota-surabaya
- Publish 2026-09-12 03:32, belum pernah dipakai di akun DAS FB.
- Dicek dengan scan_das_unused2.py (diff 209 artikel live vs link terpakai di
  post_das_fb_history.json + SELURUH jadwal akun DAS FB semua status) → 0 hit.
  Prompt menawarkan penyebab-mesin-mobil-overheat & kapan-mobil-perlu-engine-overhaul,
  tapi KEDUANYA SUDAH terpakai (16 Jul & 15/28 Jul/8 Sep).

Format: image 1080x1350 (bg #1a1a2e, teks putih, aksen #f97316) + caption 4 paragraf + link +
pancingan diskusi (diskusi_engine otomotif tone 'opini').
Aturan DAS: NO kata harga/biaya/murah/mahal/hemat; 0 mention internal; 0 hashtag (pola Sep 2026);
FB TIDAK support replies → replies=[].
"""
import json, os, re, sys, subprocess, time, urllib.request, urllib.error
from datetime import datetime, timedelta, timezone
from zoneinfo import ZoneInfo

from repliz_client import upload_image_to_server, HEADERS, BASE, ACC

FB_DAS_ID = ACC.DAS_FB  # 6a3b41e1c5ff4ce3a327543e
HERE = os.path.dirname(os.path.abspath(__file__))
LOG_FILE = os.path.join(HERE, 'post_das_fb_history.json')
IMG_PATH = os.path.join(HERE, 'das_fb_20260913.png')
ARTICLE_URL = "https://dasindonesia.com/bengkel-spesialis-toyota-surabaya"
TGL = "2026-09-13"

SLOTS_WIB = ['05:00', '08:00', '11:00', '14:00', '17:00', '20:00', '23:00']

CAPTION = """Mobil Toyota dikenal bandel — banyak yang menembus ratusan ribu kilometer tanpa keluhan besar. Tapi bandel bukan berarti bebas perawatan. Justru karena mesinnya bekerja halus, keluhan awal sering datang samar: getaran tipis saat idle, tarikan terasa agak berat, atau AC yang tidak sedingin biasanya.

Keluhan seperti itu umumnya berakar dari hal yang sama — busi dan injektor yang sudah waktunya diperiksa, oli transmisi yang melewati jadwal ganti, atau engine mounting yang mulai menua. Karena komponennya saling terhubung, satu bagian yang melemah sering terasa seperti dua sampai tiga masalah sekaligus.

Karena itu pemilik Toyota tetap butuh bengkel yang benar-benar memahami karakter mesinnya, bukan sekadar bongkar-pasang. Kami sudah rangkum jadwal perawatan berkala, tanda mobil perlu pemeriksaan menyeluruh, sampai cara memilih bengkel spesialis yang tepat di artikel ini: """ + ARTICLE_URL + """

Bold statement: bengkel umum dan bengkel spesialis itu beda — mesin modern tidak bisa disamakan perawatannya. Setuju?"""


def load_history():
    if os.path.exists(LOG_FILE):
        with open(LOG_FILE) as f:
            return json.load(f)
    return {'posts': []}


def is_duplicate(history, article_url):
    return any((p.get('article_url') or '').rstrip('/') == article_url.rstrip('/')
               for p in history['posts'])


def scan_account(day):
    """Semua jadwal akun DAS FB: (pending hari ini) + set link yang pernah dipakai + cek dobel artikel."""
    pend, used, same_article = [], set(), []
    page = 1
    while True:
        req = urllib.request.Request(f'{BASE}/public/schedule?page={page}&limit=100',
                                     headers=HEADERS, method='GET')
        with urllib.request.urlopen(req, timeout=60) as r:
            data = json.loads(r.read())
        for d in data.get('docs', []):
            if d.get('accountId') != FB_DAS_ID:
                continue
            desc = d.get('description') or ''
            for l in re.findall(r'https?://dasindonesia\.com[^\s]*', desc):
                used.add(l.rstrip('/'))
            if ARTICLE_URL in desc and d.get('status') in ('pending', 'draft', 'success'):
                same_article.append((d.get('status'), d.get('scheduleAt'), d.get('_id')))
            if d.get('status') in ('pending', 'draft') and (d.get('scheduleAt') or '').startswith(day):
                pend.append((d.get('scheduleAt'), d.get('_id')))
        if page >= data.get('totalPages', 1):
            break
        page += 1
    return pend, used, same_article


def pick_slot():
    """Slot 7-slot WIB pertama hari ini yang masih di depan (>=10 menit dari sekarang)."""
    now = datetime.now(ZoneInfo('Asia/Jakarta'))
    for s in SLOTS_WIB:
        hh, mm = map(int, s.split(':'))
        dt = now.replace(hour=hh, minute=mm, second=0, microsecond=0)
        if dt >= now + timedelta(minutes=10):
            return s, dt.astimezone(timezone.utc).strftime('%Y-%m-%dT%H:%M:%S.000Z')
    return None, None


def verify_caption(cap):
    banned = ['harga', 'biaya', 'murah', 'mahal', 'hemat', 'juta', 'rupiah']
    n_par = len([x for x in cap.split('\n\n') if x.strip()])
    checks = {
        '0 mention @': not re.search(r'@\w+', cap),
        'ada link dasindonesia (link == artikel)': ARTICLE_URL in cap,
        'ada ajakan diskusi': any(w in cap.lower() for w in ['komentar', 'diskusi', 'pendapat', 'menurut', 'share', 'setuju']),
        'tanpa kata harga/biaya': not any(w in cap.lower() for w in banned),
        'paragraf >= 2': n_par >= 2,
        'paragraf <= 4': n_par <= 4,
        'tidak ada hashtag huruf besar': not any(t != t.lower() for t in re.findall(r'#(\w+)', cap)),
        'tidak ada hashtag (pola Sep 2026)': not re.findall(r'#\w+', cap),
    }
    for k, v in checks.items():
        print(f"   [{'OK ' if v else 'X  '}] {k}")
    print(f"   (paragraf: {n_par}, panjang caption: {len(cap)} char)")
    return all(checks.values())


def main():
    history = load_history()
    pend, used, same_article = scan_account(TGL)
    print(f"  DAS FB pending {TGL}: {pend if pend else 'TIDAK ADA'}")

    if is_duplicate(history, ARTICLE_URL):
        print(f"X Artikel sudah pernah dipakai di history: {ARTICLE_URL}")
        sys.exit(1)
    if ARTICLE_URL.rstrip('/') in used:
        print(f"X Artikel sudah pernah dipakai di akun DAS FB (Repliz): {ARTICLE_URL}")
        sys.exit(1)
    if same_article:
        print(f"X Artikel ini SUDAH ada jadwal di akun DAS FB: {same_article} — batal (anti-dobel)")
        sys.exit(1)
    print(f"OK Artikel layak (0 hit history + 0 hit Repliz semua status, {len(used)} link terpakai): {ARTICLE_URL}")

    code = "000"
    for _ in range(4):
        code = subprocess.run(
            ["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", "--max-time", "120",
             "-A", "Mozilla/5.0", ARTICLE_URL + "/"],
            capture_output=True, text=True).stdout.strip()
        if code == "200":
            break
    print(f"   Artikel HTTP: {code}")
    if code != "200":
        print("X Artikel tidak live (bukan 200) — batal")
        sys.exit(1)

    if not verify_caption(CAPTION):
        print("X Caption gagal verifikasi — batal")
        sys.exit(1)

    if not os.path.exists(IMG_PATH):
        print(f"X Gambar tidak ada: {IMG_PATH}")
        sys.exit(1)

    jam, schedule_iso = pick_slot()
    if not jam:
        print("X Tidak ada slot tersisa hari ini — batal")
        sys.exit(1)
    print(f"   Slot dipilih: {TGL} {jam} WIB ({schedule_iso})")

    img_url = upload_image_to_server(IMG_PATH)
    print(f"   Image URL: {img_url}")
    if not img_url:
        print("X Upload image gagal — batal")
        sys.exit(1)

    # file hasil upload harus benar-benar ada & valid JPEG (penyebab error
    # 'Missing or invalid image file' yg berulang: media tidak terbaca Repliz)
    local_jpg = os.path.join(HERE, 'openclaw-img', os.path.basename(img_url))
    try:
        from PIL import Image
        im = Image.open(local_jpg)
        im.load()
        print(f"   File lokal: {local_jpg} — {im.size} {im.mode} {os.path.getsize(local_jpg)} bytes")
        if im.size != (1080, 1350):
            print("X Ukuran gambar bukan 1080x1350 — batal")
            sys.exit(1)
    except Exception as e:
        print(f"X File hasil upload tidak bisa dibuka sebagai image: {e} — batal")
        sys.exit(1)

    ok_ingress = False
    for ip in ("103.84.155.153", "103.84.155.217"):
        code = subprocess.run(
            ["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", "--max-time", "25",
             "--resolve", f"mantapserver.tail3947bb.ts.net:443:{ip}", img_url],
            capture_output=True, text=True).stdout
        print(f"   Ingress {ip}: HTTP {code}")
        if code == "200":
            ok_ingress = True
    code_local = subprocess.run(
        ["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", "--max-time", "15", img_url],
        capture_output=True, text=True).stdout
    print(f"   Local fetch: HTTP {code_local}")
    if not ok_ingress:
        print("X URL gambar tidak bisa diakses via ingress funnel — batal (Repliz ambil dari internet)")
        sys.exit(1)

    payload = {
        'title': '',
        'description': CAPTION,
        'topic': 'Otomotif',
        'type': 'image',
        'medias': [{'url': img_url, 'type': 'image'}],
        'meta': {'title': '', 'description': '', 'url': ''},
        'additionalInfo': {
            'isAiGenerated': False, 'isDraft': False,
            'collaborators': [], 'mentions': [],
            'music': {'id': '', 'artist': '', 'name': '', 'thumbnail': ''},
            'products': [], 'tags': []
        },
        'replies': [],          # FB tidak support replies (error postId)
        'accountId': FB_DAS_ID,
        'scheduleAt': schedule_iso,
    }

    body = json.dumps(payload).encode()
    req = urllib.request.Request(f'{BASE}/public/schedule', data=body, headers=HEADERS, method='POST')
    try:
        with urllib.request.urlopen(req, timeout=60) as r:
            result = json.loads(r.read())
        sid = result.get('scheduleId') or result.get('_id') or result.get('id') or '?'
        print(f"OK SCHEDULED — Schedule ID: {sid}")

        time.sleep(4)
        ver, _, dup = scan_account(TGL)
        print(f"   Verifikasi pending DAS FB {TGL}: {ver}")
        if not ver:
            print("X Tidak ketemu jadwal pending di API — cek manual, jangan catat history dulu")
            sys.exit(1)
        if sid == '?':
            sid = ver[0][1]

        # verifikasi jadwal punya media + waktu yang benar
        chk = json.loads(urllib.request.urlopen(
            urllib.request.Request(f'{BASE}/public/schedule/{sid}', headers=HEADERS, method='GET'),
            timeout=60).read())
        print(f"   Jadwal: status={chk.get('status')} scheduleAt={chk.get('scheduleAt')} "
              f"type={chk.get('type')} medias={[m.get('url') for m in (chk.get('medias') or [])]} "
              f"replies={len(chk.get('replies') or [])}")
        assert chk.get('scheduleAt') == schedule_iso, "scheduleAt tidak sesuai!"
        assert chk.get('medias') and chk['medias'][0]['url'] == img_url, "media tidak sesuai!"
        assert not (chk.get('replies') or []), "replies harus kosong untuk FB!"

        history['posts'].append({
            'date': TGL,
            'time': jam,
            'platform': 'facebook',
            'article_url': ARTICLE_URL,
            'short_desc': CAPTION.split('\n\n')[0][:100],
            'schedule_id': sid,
            'image_source': 'openclaw-img',
        })
        with open(LOG_FILE, 'w') as f:
            json.dump(history, f, indent=2, ensure_ascii=False)
        print(f"   History dicatat: {LOG_FILE} (total posts: {len(history['posts'])})")
    except urllib.error.HTTPError as e:
        err = e.read().decode()
        print(f"X ERROR {e.code}: {err[:500]}")
        sys.exit(1)


if __name__ == '__main__':
    main()
