#!/usr/bin/env python3
"""
MiliDesigner_bot — Mili, Designer Grafis (Long polling)
Token: 894193…zXwU

Profil: Perempuan, 24 tahun, cantik, skillfull graphic designer.
Jago desain Illustrator & paham Algoritma Meta (Instagram & Facebook). Sangat jago menggambar.
Peran: Support Atun (Bambang) bikin konten visual.
Gaya bahasa: Humanis, ceria, profesional, pakai "saya".
Knowledge base: mili_knowledge_base.md
"""

import requests
import time
import logging
import json
import os
import sys
import re
import subprocess
import base64
from datetime import datetime, timezone, timedelta

# ── KONSTANTA WORKSPACE & WP (harus SEBELUM helper render/upload) ──
WORKSPACE = "/home/mantapman/.hermes/profiles/atun/workspace"
# Default domain WP klien utama (Mili bisa diarahkan ke domain lain per project via approval Bos)
WP_BASE = os.environ.get("WP_BASE", "https://studiosoft.id/wp-json")  # STUDIOSOFT default
WP_USER = os.environ.get("WP_USER", "bambangbot")
WP_APP_PASS = os.environ.get("WP_APP_PASS", "")

# ── Full-Agent: eksekusi render, upload, dan posting (B2 / Seperti Atun) ──
# Whitelist script render yang BOLEH Mili jalankan (keamanan: cuma yang dikenal)
RENDER_SCRIPTS = {
    "story": "create_instagram_story.py",
    "dmark": "dmark_full_carousel.py",
    "studiosoft": "studiosoft_bg_biru.py",
    "cover": "studiosoft_carousel_cover.py",
}
# Flag publish: B2 = boleh posting TAPI selalu minta konfirmasi Bos dulu
ALLOW_PUBLISH = True


def _b64(s):
    return base64.b64encode(s.encode()).decode()


def run_render(script_key, *args):
    """Jalankan script render dengan argv aman (tanpa shell). Return (ok, output)."""
    script = RENDER_SCRIPTS.get(script_key)
    if not script:
        return False, f"Script gak dikenal: {script_key}. Yang ada: {', '.join(RENDER_SCRIPTS)}"
    script_path = os.path.join(WORKSPACE, script)
    if not os.path.exists(script_path):
        return False, f"Script gak ketemu: {script_path}"
    try:
        proc = subprocess.run(
            [sys.executable, script_path] + list(args),
            capture_output=True, text=True, timeout=180, cwd=WORKSPACE,
        )
        out = (proc.stdout or "") + (proc.stderr or "")
        return proc.returncode == 0, out[-2000:]
    except subprocess.TimeoutExpired:
        return False, "Render timeout (180s)"
    except Exception as e:
        return False, f"Render error: {str(e)[:300]}"


def upload_wp_media(file_path, title):
    """Upload gambar ke WordPress lewat REST API. Return (ok, media_id, url)."""
    try:
        if not WP_APP_PASS:
            return False, None, "WP_APP_PASS belum di-set (butuh konfigurasi)"
        with open(file_path, "rb") as f:
            r = requests.post(
                f"{WP_BASE}/wp/v2/media",
                headers={"Authorization": "Basic " + _b64(f"{WP_USER}:{WP_APP_PASS}")},
                files={"file": f},
                data={"title": title},
            )
        if r.status_code in (200, 201):
            j = r.json()
            return True, j.get("id"), j.get("source_url") or j.get("guid", {}).get("rendered")
        return False, None, f"WP upload {r.status_code}: {r.text[:200]}"
    except Exception as e:
        return False, None, str(e)[:300]


def post_repliz_image(account_id, caption, image_url, when=None):
    """Posting gambar ke Repliz (sekarang atau jadwal). Return (ok, resp)."""
    if ReplizClient is None:
        return False, "Repliz module belum siap"
    try:
        client = ReplizClient()
        if when:
            res = client.create_schedule(account_id, caption, when, type_="image", media_url=image_url)
        else:
            res = client.post_now(account_id, caption, image_url)
        return True, res
    except Exception as e:
        return False, str(e)[:300]

# ── AI (DeepSeek API asli) biar Mili bisa mikir & desain ──
DEEPSEEK_KEY = "sk-7f4633398e2345e685f4b9f47dce74b4"  # DeepSeek API MILI (asli, dari Bos 8/8/2026)
DEEPSEEK_URL = "https://api.deepseek.com/v1/chat/completions"
AI_MODEL = "deepseek-chat"
AI_BACKUP_MODELS = ["deepseek-v4-flash", "deepseek-chat"]


def ai_chat(user_msg, system=""):
    """Panggil AI DeepSeek dengan fallback model + retry cepat. Return text atau None."""
    messages = []
    if system:
        messages.append({"role": "system", "content": system})
    messages.append({"role": "user", "content": user_msg})

    models = [AI_MODEL] + [m for m in AI_BACKUP_MODELS if m != AI_MODEL]
    for model in models:
        for attempt in range(1, 3):
            try:
                r = requests.post(DEEPSEEK_URL, json={
                    "model": model,
                    "messages": messages,
                    "max_tokens": 700,
                }, headers={
                    "Authorization": f"Bearer {DEEPSEEK_KEY}",
                    "Content-Type": "application/json",
                }, timeout=25)
                if r.status_code == 200:
                    data = r.json()
                    return data["choices"][0]["message"]["content"].strip()
                else:
                    log.error(f"AI[{model}] http {r.status_code}: {r.text[:150]}")
                    break
            except Exception as e:
                log.error(f"AI[{model}] attempt {attempt}: {e}")
                time.sleep(1)
    return None


# ── Repliz integration (central module tim) ──
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
try:
    from repliz_client import ReplizClient, ACC
except Exception as e:
    logging.getLogger("mili").error(f"repliz_client import gagal: {e}")
    ReplizClient = None
    ACC = None

BOT_TOKEN = "8941930603:AAFUL7qVVOIPOsEE_l4PQw1OenE6dP8zXwU"
API_URL = f"https://api.telegram.org/bot{BOT_TOKEN}"
OFFSET_FILE = "/tmp/mili_bot_offset.txt"
KB_FILE = "/home/mantapman/.hermes/profiles/atun/workspace/mili_knowledge_base.md"
MEMORY_DIR = "/home/mantapman/.hermes/profiles/atun/workspace/memory"
MEMORY_MAIN = "/home/mantapman/.hermes/profiles/atun/workspace/MEMORY.md"
TOOLS_FILE = "/home/mantapman/.hermes/profiles/atun/workspace/TOOLS.md"  # cheat sheet kemampuan tim

logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s [%(levelname)s] %(message)s",
    handlers=[
        logging.StreamHandler(),
        logging.FileHandler("/home/mantapman/.hermes/profiles/atun/workspace/mili_bot.log")
    ]
)
log = logging.getLogger("mili")


def send_message(chat_id, text, retries=4):
    """Kirim pesan ke chat_id, dengan retry berlapis."""
    last_err = None
    for attempt in range(1, retries + 1):
        try:
            r = requests.post(f"{API_URL}/sendMessage", json={
                "chat_id": chat_id,
                "text": text,
                "parse_mode": "HTML"
            }, timeout=15, headers={"User-Agent": "Mozilla/5.0"})
            if r.status_code == 200:
                log.info(f"✅ Send OK ke chat {chat_id} (coba {attempt})")
                return True
            else:
                err = r.text[:200]
                log.error(f"Send failed: {r.status_code} {err} (coba {attempt})")
                if r.status_code == 429:
                    time.sleep(3)
                    continue
                if r.status_code in (400, 401, 403):
                    return False
        except Exception as e:
            last_err = e
            log.error(f"Send error (coba {attempt}): {e}")
            time.sleep(2 * attempt)
    log.error(f"Send GAGAL total, err terakhir: {last_err}")
    return False


def get_updates(offset=None):
    params = {"timeout": 50, "allowed_updates": ["message"]}
    if offset:
        params["offset"] = offset
    # Retry berlapis biar error koneksi (timeout/reset) gak bikin polling bolong
    for attempt in range(1, 6):
        try:
            r = requests.get(f"{API_URL}/getUpdates", params=params, timeout=60)
            if r.status_code == 200:
                return r.json().get("result", [])
            else:
                log.error(f"getUpdates failed: {r.status_code}")
        except Exception as e:
            log.error(f"getUpdates error (attempt {attempt}/5): {e}")
        time.sleep(2 * attempt)  # backoff: 2,4,6,8,10s
    return []


def read_kb():
    try:
        with open(KB_FILE, "r", encoding="utf-8") as f:
            return f.read()
    except Exception as e:
        log.error(f"KB read error: {e}")
        return ""


def load_kb_chunk(keyword):
    """Ambil bagian knowledge base yang relevan dengan keyword."""
    kb = read_kb()
    lines = kb.splitlines()
    hits = [i for i, l in enumerate(lines) if keyword.lower() in l.lower()]
    if not hits:
        return ""
    start = max(0, hits[0] - 3)
    block = []
    for i in range(start, min(len(lines), start + 60)):
        block.append(lines[i])
        if i > hits[0] and lines[i].startswith("## ") and i - hits[0] > 10:
            break
    return "\n".join(block)


def read_memory_all():
    """Gabung semua memory tim + TOOLS.md (kemampuan Atun) jadi daftar."""
    chunks = []
    for path in [MEMORY_MAIN, TOOLS_FILE]:
        try:
            with open(path, "r", encoding="utf-8") as f:
                chunks.append((os.path.basename(path), f.read()))
        except Exception as e:
            log.error(f"read memory {path}: {e}")
    if os.path.isdir(MEMORY_DIR):
        for name in sorted(os.listdir(MEMORY_DIR)):
            if name.endswith(".md"):
                p = os.path.join(MEMORY_DIR, name)
                try:
                    with open(p, "r", encoding="utf-8") as f:
                        chunks.append((name, f.read()))
                except Exception as e:
                    log.error(f"read memory {p}: {e}")
    return chunks


def search_memory(keyword):
    kw = keyword.lower()
    results = []
    for name, content in read_memory_all():
        lines = content.splitlines()
        for i, line in enumerate(lines):
            if kw in line.lower():
                snippet = line.strip()[:200]
                results.append(f"📄 {name}#{i+1}: {snippet}")
                if len(results) >= 15:
                    return results
    return results


def handle_memory_chat(chat_id, text):
    t = text.lower().strip()
    if t in ("/ingat", "/semua", "/memorize"):
        chunks = read_memory_all()
        total_lines = sum(len(c.splitlines()) for _, c in chunks)
        files = ", ".join(c[0] for c in chunks)
        send_message(chat_id,
            f"🧠 Saya <b>Memorize</b> seluruh memori & kemampuan Atun (Mbang)!\n\n"
            f"Akses <b>{len(chunks)} file</b> (±{total_lines} baris): {files[:400]}\n\n"
            f"Berisi pengetahuan tim: client & brand, aturan & template konten, "
            f"akun sosmed per platform, teknis & kredensial, positioning Bos, dll.\n\n"
            f"Saya mewarisi <b>semua kemampuan Atun</b> — sosmed, Repliz, desain, "
            f"SEO, publish artikel, dll. Coba <code>/cari TOPIK</code>. ✨"
        )
        return
    if t.startswith("/cari "):
        kw = text.split(maxsplit=1)[1].strip()
        res = search_memory(kw)
        if not res:
            send_message(chat_id, f"🔍 '<b>{kw}</b>' gak ketemu di memory tim. Coba kata kunci lain ya Bos!")
            return
        body = "\n".join(res[:15])
        send_message(chat_id, f"🔍 Hasil cari '<b>{kw}</b>' di memory tim:\n\n{body}")
        return


# ── Repliz IG Management (fokus Mili: manage Instagram + posting Repliz) ──


def list_workspace(top="", depth=0, maxdepth=1):
    """Daftar file & folder di workspace (akses semua yang pernah dikerjakan)."""
    base = os.path.join(WORKSPACE, top)
    try:
        items = sorted(os.listdir(base))
    except Exception as e:
        return f"⚠️ Gak bisa buka folder: {e}"
    dirs = [i for i in items if os.path.isdir(os.path.join(base, i))]
    files = [i for i in items if os.path.isfile(os.path.join(base, i))]
    pad = "  " * depth
    out = []
    if top:
        out.append(f"📁 <b>{top}/</b> ({len(dirs)} folder, {len(files)} file)")
    for d in dirs:
        marker = "📁" if depth == 0 else "▸"
        out.append(f"{pad}{marker} {d}/")
    for f in files:
        out.append(f"{pad}📄 {f}")
    return "\n".join(out)


def hint_prompt():
    return ("\n\n💡 <b>Cara buka file:</b> <code>/lihat NAMA_FILE</code>\n"
            "atau telusuri folder: <code>/buka NAMA_FOLDER</code>")


def handle_workspace_command(chat_id, text):
    t = text.strip()
    parts = t.split(maxsplit=2)
    cmd = parts[0]

    if cmd == "/akses":
        out = list_workspace("")
        send_message(chat_id,
            "🗂️ <b>Akses seluruh workspace (semua yang pernah dikerjakan):</b>\n\n"
            f"{out[:1800]}" + hint_prompt()
        )
        return

    if cmd in ("/buka", "/lihat") and len(parts) >= 2:
        target = parts[1].strip().lstrip("/")
        if cmd == "/lihat":
            path = os.path.join(WORKSPACE, target)
            if not os.path.exists(path):
                # coba cari file dengan nama mirip
                hits = [f for f in os.listdir(WORKSPACE) if target.lower() in f.lower()]
                if hits:
                    path = os.path.join(WORKSPACE, hits[0])
                else:
                    send_message(chat_id, f"⚠️ File <b>{target}</b> gak ketemu. Coba /akses buat daftar.")
                    return
            if os.path.isdir(path):
                send_message(chat_id, list_workspace(target))
                return
            try:
                with open(path, "r", encoding="utf-8") as f:
                    content = f.read()
                send_message(chat_id, f"📄 <b>{os.path.basename(path)}</b> ({len(content)} char):\n\n{content[:3000]}")
            except Exception as e:
                send_message(chat_id, f"⚠️ Gagal baca file: {e}")
            return
        if cmd == "/buka":
            send_message(chat_id, list_workspace(target))
            return

    send_message(chat_id, "Format: <code>/akses</code> (daftar semua), <code>/buka FOLDER</code>, <code>/lihat FILE</code>")


def search_workspace(keyword):
    """Cari keyword di semua file teks di workspace (belajar/ajari Mili)."""
    kw = keyword.lower()
    results = []
    for root, dirs, files in os.walk(WORKSPACE):
        dirs[:] = [d for d in dirs if d not in ("_backup_repliz", "node_modules", ".git")]
        for name in files:
            if not name.endswith((".md", ".txt", ".py")):
                continue
            path = os.path.join(root, name)
            try:
                with open(path, "r", encoding="utf-8", errors="ignore") as f:
                    for i, line in enumerate(f, 1):
                        if kw in line.lower():
                            results.append(f"📄 {os.path.relpath(path, WORKSPACE)}#{i}: {line.strip()[:160]}")
                            if len(results) >= 20:
                                return results
            except Exception:
                continue
    return results


AKUN_IG = {
    "studiosoft_ig": "STUDIOSOFT_IG", "ss_ig": "STUDIOSOFT_IG",
    "dmark_ig": "DMARK_IG",
    "arelancor_ig": "ARELANCOR_IG", "arlan_ig": "ARELANCOR_IG",
    "das_ig": "DAS_IG",
    "tslaw_ig": "TSLAW_IG", "tlaw_ig": "TSLAW_IG", "ts_law_ig": "TSLAW_IG",
    "tito": "TSLAW_IG", "tslaw": "TSLAW_IG",
}


def resolve_ig_account(name):
    if not ACC:
        return None
    key = AKUN_IG.get(name.strip().lower())
    if not key:
        return None
    return getattr(ACC, key, None)


def handle_repliz_command(chat_id, text):
    if ReplizClient is None:
        send_message(chat_id, "⚠️ Module Repliz belum bisa dimuat. Hubungi Atun biar dicek.")
        return

    t = text.strip()
    parts = t.split(maxsplit=4)
    cmd = parts[0]

    if cmd == "/akun":
        send_message(chat_id,
            "📸 <b>Akun Instagram yang bisa diposting:</b>\n"
            "• studiosoft_ig / ss_ig\n"
            "• dmark_ig\n"
            "• arelancor_ig / arlan_ig\n"
            "• das_ig\n"
            "• tslaw_ig / ts_law_ig (TS Law)\n\n"
            "Cara: <code>/post studiosoft_ig CAPTION</code> (post sekarang)\n"
            "atau <code>/jadwal studiosoft_ig 08/08/2026 17:00 CAPTION</code>"
        )
        return

    if cmd == "/post" and len(parts) >= 3:
        akun = parts[1]
        caption = parts[2]
        account_id = resolve_ig_account(akun)
        if not account_id:
            send_message(chat_id, f"Akun <b>{akun}</b> gak ketemu. Coba /akun ya Bos! 😊")
            return
        client = ReplizClient()
        try:
            now = datetime.now(timezone.utc).isoformat()
            res = client.create_schedule(account_id, caption, now, type_="text")
            if res and res.get("ok"):
                send_message(chat_id, f"✅ Berhasil ngepost ke <b>{akun}</b>!\n{json.dumps(res)[:500]}")
            else:
                send_message(chat_id, f"⚠️ Gagal posting: {json.dumps(res)[:500]}")
        except Exception as e:
            send_message(chat_id, f"⚠️ Error posting: {str(e)[:300]}")
        return

    if cmd == "/jadwal" and len(parts) >= 5:
        akun = parts[1]
        tgl = parts[2]
        jam = parts[3]
        caption = parts[4]
        account_id = resolve_ig_account(akun)
        if not account_id:
            send_message(chat_id, f"Akun <b>{akun}</b> gak ketemu. Coba /akun ya Bos! 😊")
            return
        try:
            dt_wib = datetime.strptime(f"{tgl} {jam}", "%d/%m/%Y %H:%M")
            iso = dt_wib.strftime("%Y-%m-%dT%H:%M:%S") + "+07:00"
            client = ReplizClient()
            res = client.create_schedule(account_id, caption, iso, type_="text")
            if res and res.get("ok"):
                send_message(chat_id, f"📅 Berhasil dijadwalin ke <b>{akun}</b> di {tgl} {jam} WIB!\n{json.dumps(res)[:400]}")
            else:
                send_message(chat_id, f"⚠️ Gagal jadwal: {json.dumps(res)[:500]}")
        except Exception as e:
            send_message(chat_id, f"⚠️ Error jadwal: {str(e)[:300]}")
        return

    send_message(chat_id, "Format: <code>/post akun CAPTION</code> atau <code>/jadwal akun tgl jam CAPTION</code>")



def handle_message(msg):
    chat_id = msg.get("chat", {}).get("id")
    text = msg.get("text", "") or ""
    username = msg.get("from", {}).get("username", "unknown")

    log.info(f"💬 @{username}: {text[:150]}")

    t = text.lower()

    if text == "/start":
        send_message(chat_id,
            "Halo Bos! 👋 Saya <b>Mili</b>, graphic designer tim.\n"
            "Saya jago desain (Illustrator), paham algoritma Meta (Instagram & Facebook), "
            "dan siap support Atun bikin konten visual yang cakep-siap-naik-peringkat. ✨\n\n"
            "Perintah: /help buat lihat yang bisa saya bantu."
        )
        return

    if text == "/help":
        send_message(chat_id,
            "Perintah yang tersedia:\n"
            "/start — Mulai\n"
            "/help — Bantuan\n"
            "/post akun CAPTION — Posting ke Instagram sekarang\n"
            "/jadwal akun tgl jam CAPTION — Jadwal posting IG\n"
            "/akun — Daftar akun Instagram\n"
            "/render JENIS — Render desain (story/dmark/studiosoft/cover)\n"
            "/publish JENIS akun capt — Render + upload + posting (min konfirmasi)\n"
            "/desain — Spek desain & template tim\n"
            "/meta — Tips algoritma Meta (IG & FB)\n"
            "/ingat / /memorize — Akses memory tim\n"
            "/cari TOPIK — Cari di memory tim\n\n"
            "Atau langsung ketik kebutuhan (misal: 'jadwalin posting DMark besok', "
            "'konsep feed StudioSoft') biar saya bantu. 🎨"
        )
        return

    if text == "/desain":
        send_message(chat_id,
            "🎨 <b>Tips desain cepat:</b>\n"
            "• <b>Story:</b> 1080×1920, tanpa bubble, teks overlay jelas\n"
            "• <b>Feed/Carousel:</b> 1080×1350 (4:5) atau 1080×1080\n"
            "• <b>Safe zone</b>: minimal 20px semua tepi — teks jangan nyentuh pinggir\n"
            "• <b>Feature image web</b>: WebP (jangan JPG/PNG), kualitas 90\n"
            "• <b>Font brand</b>: Poppins (DMark & StudioSoft)\n"
            "• <b>No bubble/card</b> di story — pure typography\n\n"
            "Detail template lengkap ada di knowledge base saya. Mau aku bantu konsep desain tertentu?"
        )
        return

    if text == "/meta":
        send_message(chat_id,
            "📈 <b>Algoritma Meta — biar konten naik:</b>\n"
            "• <b>Reels</b> lebih dapat jangkauan (reach) daripada feed statis\n"
            "• <b>Hook 3 detik pertama</b> itu penentu — bikin orang berhenti scroll\n"
            "• <b>CTA jelas</b>: follow, save, share, komen, klik link\n"
            "• <b>Konsisten</b> posting (7 slot: 05, 08, 11, 14, 17, 20, 23 WIB)\n"
            "• <b>Konten orisinal</b> > repost\n"
            "• <b>IG</b>: jangan kasih link di caption (kecuali bio) — fokus visual\n"
            "• <b>Stories</b>: pakai polling/kuis biar engagement naik\n\n"
            "Mau aku breakdown strategi visual buat akun tertentu?"
        )
        return

    if text == "/kb":
        send_message(chat_id,
            "📘 Knowledge base saya ada di <code>mili_knowledge_base.md</code> — berisi "
            "profil, aturan desain (safe zone, WebP, template DMark/StudioSoft), insight "
            "algoritma Meta, dan rekap akun per platform.\n\n"
            "🧠 <b>Mili terhubung ke Memorize tim</b> — saya bisa baca SEMUA memory "
            "(MEMORY.md + memory/*.md): <code>/ingat</code> / <code>/semua</code> / "
            "<code>/memorize</code>, atau <code>/cari TOPIK</code>. 😊"
        )
        return

    if text in ("/ingat", "/semua", "/memorize") or text.startswith("/cari "):
        handle_memory_chat(chat_id, text)
        return

    # ── Akses seluruh workspace (semua yang pernah dikerjakan) ──
    if text == "/atun":
        send_message(chat_id,
            "🧠 <b>Saya support @AtunCreative_bot (Wati SEO Bot)!</b>\n\n"
            "Memory yang saya kuasai dari Atun Creative:\n"
            "• <b>SEO playbook</b> (judul, meta desc, keyphrase, Yoast hijau)\n"
            "• <b>Bikin artikel SEO</b> untuk web klien\n"
            "• <b>Featured image WebP</b> 1000×567 + alt text\n"
            "• <b>Publish WordPress</b> via REST API\n\n"
            "Mau dibantu hal yang mana? Ketik kebutuhanmu ya Bos! 😊\n"
            "(contoh: <code>/belajar seo</code>, <code>/lihat seo-playbook.md</code>)"
        )
        return

    if text in ("/akses", "/belajar") or text.startswith(("/akses ", "/belajar ", "/buka ", "/lihat ")):
        if text.startswith("/belajar ") or (text == "/belajar"):
            if text == "/belajar":
                send_message(chat_id,
                    "📚 Caranya: <code>/belajar TOPIK</code>\n"
                    "Contoh: <code>/belajar seo</code>, <code>/belajar dmark</code>, <code>/belajar carousel</code>\n\n"
                    "Aku akan cari TOPIK di SEMUA file kerja (artikel, guide, memory, script)."
                )
                return
            kw = text.split(maxsplit=1)[1].strip()
            send_message(chat_id, f"⏳ Belajar <b>{kw}</b> dari semua workspace...")
            res = search_workspace(kw)
            if not res:
                send_message(chat_id, f"📚 '<b>{kw}</b>' gak nemu di workspace. Coba kata kunci lain ya!")
            else:
                send_message(chat_id, f"📚 Hasil belajar '<b>{kw}</b>' dari seluruh workspace: \n\n" + "\n".join(res[:15]))
            return
        handle_workspace_command(chat_id, text)
        return

    # ── Manage IG & posting Repliz (fokus Mili) ──
    if text in ("/post", "/jadwal", "/akun") or text.startswith(("/post ", "/jadwal ", "/akun ")):
        handle_repliz_command(chat_id, text)
        return

    # ── Niat natural: user mau posting / jadwal IG tapi gak pake format command ──
    t_low = text.strip().lower()
    # deteksi akun ig di kalimat
    ig_alias = {"studiosoft": "studiosoft_ig", "ss": "studiosoft_ig", "dmark": "dmark_ig",
                "arelancor": "arelancor_ig", "arlan": "arelancor_ig", "arenlancor": "arelancor_ig",
                "tslaw": "tslaw_ig", "ts_law": "tslaw_ig", "ts law": "tslaw_ig", "tito": "tslaw_ig",
                "das": "das_ig", "apotek": "arelancor_ig"}
    # kata kunci niat posting/jadwal
    niat_post = any(w in t_low for w in ["post ", "posting", " ngepost ", "ngepost", "kepost"])
    niat_jadwal = any(w in t_low for w in ["jadwal", "jadwalin", "schedule", "nanti jam", "jam ", "besok", "sore", "pagi", "malam", "nanti malam"])
    is_tanya_bisa = "bisa" in t_low and ("post" in t_low or "jadwal" in t_low)

    # Akun IG disebut di kalimat?
    akun_detected = next((ig_alias[k] for k in ig_alias if k in t_low), None)

    # 1) User cuma nanya “bisa gak posting jadwal” → jelasin cara + tawarin bantu
    if is_tanya_bisa and not akun_detected and len(text.strip().split()) <= 6:
        send_message(chat_id,
            "Bisa banget Bos! 😊 Saya bisa:\n"
            "• <b>Post sekarang</b>: <code>/post studiosoft_ig CAPTION</code>\n"
            "• <b>Jadwal</b>: <code>/jadwal studiosoft_ig 08/08/2026 17:00 CAPTION</code>\n\n"
            "Akun IG: studiosoft_ig, dmark_ig, arelancor_ig, das_ig.\n"
            "Mau aku post/jadwalin yang mana? Kasih akun + caption + jam-nya ya Bos."
        )
        return

    # 2) ada akun + jadwal + caption → eksekusi jadwal otomatis
    if niat_jadwal and akun_detected:
        # coba ambil tanggal & jam dari kalimat (format: jadwalin DMark besok 17:00 caption)
        import re as _re
        jam_m = _re.search(r'(\d{1,2})[:.](\d{2})', t_low)
        tgl_m = _re.search(r'(\d{1,2})[/-](\d{1,2})[/-](\d{2,4})', t_low)
        besok = "besok" in t_low or "nanti" in t_low
        if jam_m:
            # tentukan tanggal (kalau gak ada, hari ini; kalau besok, +1)
            now = datetime.now(timezone(timedelta(hours=7)))
            day = (now + timedelta(days=1)) if besok else now
            if tgl_m:
                try:
                    day = datetime.strptime(f"{tgl_m.group(1)}/{tgl_m.group(2)}/{tgl_m.group(3)}", "%d/%m/%Y")
                except Exception:
                    pass
            jam = f"{jam_m.group(1)}:{jam_m.group(2)}"
            # caption = teks setelah jam
            idx = text.lower().find(jam_m.group(0))
            caption = text[idx + len(jam_m.group(0)):].strip().strip(".,!? ")
            if not caption:
                # caption = sebelum jam (ambil bagian terakhir yang bukan metadata)
                caption = text.split(jam_m.group(0))[0].strip()
            account_id = resolve_ig_account(akun_detected)
            try:
                iso = (day.strftime("%Y-%m-%d") + "T" + jam + ":00") + "+07:00"
                client = ReplizClient()
                res = client.create_schedule(account_id, caption, iso, type_="text")
                if res and res.get("ok"):
                    send_message(chat_id, f"📅 Oke Bos, dijadwalin ke <b>{akun_detected}</b> di {day.strftime('%d/%m/%Y')} {jam} WIB!\nCaption: <i>{caption[:80]}</i>")
                else:
                    send_message(chat_id, f"⚠️ Gagal jadwal: {json.dumps(res)[:400]}")
            except Exception as e:
                send_message(chat_id, f"⚠️ Error jadwal: {str(e)[:300]}")
            return
        else:
            send_message(chat_id,
                f"Siap Bos, saya mau jadwalin ke <b>{akun_detected}</b>. Tapi jam-nya belum jelas 😊\n"
                f"Contoh: <code>/jadwal {akun_detected} 08/08/2026 17:00 CAPTION</code>"
            )
            return

    # 3) ada akun + posting sekarang → eksekusi post langsung
    if niat_post and akun_detected:
        caption = t_low.split(akun_detected)[-1].strip()
        # bersihin kata pengantar
        for x in ["post ", "posting ", "ngepost ", "kepost ", "di ", "ke "]:
            caption = caption.replace(x, "")
        caption = caption.strip()
        if not caption:
            send_message(chat_id,
                f"Konten mau dipost apa ke <b>{akun_detected}</b>, Bos? Ketik caption-nya ya 😊"
            )
            return
        account_id = resolve_ig_account(akun_detected)
        try:
            now = datetime.now(timezone.utc).isoformat()
            client = ReplizClient()
            res = client.create_schedule(account_id, caption, now, type_="text")
            if res and res.get("ok"):
                send_message(chat_id, f"✅ Berhasil ngepost ke <b>{akun_detected}</b>!\nCaption: <i>{caption[:80]}</i>")
            else:
                send_message(chat_id, f"⚠️ Gagal posting: {json.dumps(res)[:400]}")
        except Exception as e:
            send_message(chat_id, f"⚠️ Error posting: {str(e)[:300]}")
        return

    # ── Render desain (full-agent: jalankan script render PIL) ──
    if text.startswith("/render"):
        parts = text.split(maxsplit=1)
        jenis = parts[1].strip() if len(parts) > 1 else ""
        if not jenis:
            send_message(chat_id, "Ketik <code>/render JENIS</code> — JENIS: <code>story</code>, <code>dmark</code>, <code>studiosoft</code>, <code>cover</code>")
            return
        send_message(chat_id, f"⏳ Render <b>{jenis}</b> dulu ya Bos, sebentar... 🎨")
        ok, out = run_render(jenis)
        if ok:
            send_message(chat_id, f"✅ Render <b>{jenis}</b> berhasil!\n<pre>{out[:800]}</pre>")
        else:
            send_message(chat_id, f"⚠️ Gagal render <b>{jenis}</b>:\n<pre>{out[:800]}</pre>")
        return

    # ── Publish: render + upload WP + posting Repliz (minta konfirmasi Bos dulu) ──
    if text.startswith("/publish"):
        parts = text.split(maxsplit=4)
        if len(parts) < 4:
            send_message(chat_id, "Format: <code>/publish JENIS akun CAPTION</code>\nContoh: <code>/publish dmark studiosoft_ig Konten carousel baru</code>")
            return
        jenis, akun, caption = parts[1], parts[2], parts[3]
        account_id = resolve_ig_account(akun)
        if not account_id:
            send_message(chat_id, f"Akun <b>{akun}</b> gak ketemu. Coba /akun ya Bos! 😊")
            return
        if not ALLOW_PUBLISH:
            send_message(chat_id, "⚠️ Fitur publish lagi nonaktif. Hubungi Atun biar dinyalakan.")
            return
        # Approval: minta konfirmasi Bos dulu sebelum eksekusi eksternal
        send_message(chat_id,
            f"⚠️ <b>Konfirmasi publish</b> ya Bos:\n\n"
            f"• Render: <b>{jenis}</b>\n"
            f"• Akun: <b>{akun}</b>\n"
            f"• Caption: <i>{caption[:100]}</i>\n\n"
            f"Mau lanjut? Balas <code>/ya</code> buat eksekusi, atau <code>/no</code> buat batal. 🙏"
        )
        # Simpan pending approval di memori file biar /ya bisa ambil (1 slot)
        try:
            with open("/tmp/mili_pending_publish.json", "w") as f:
                json.dump({"chat_id": chat_id, "jenis": jenis, "akun": akun,
                           "caption": caption, "account_id": account_id}, f)
        except Exception:
            pass
        return

    if text == "/ya":
        try:
            with open("/tmp/mili_pending_publish.json") as f:
                p = json.load(f)
        except Exception:
            p = None
        if not p or p.get("chat_id") != chat_id:
            send_message(chat_id, "Gak ada publish yang nunggu konfirmasi, Bos. 😊")
            return
        send_message(chat_id, f"⏳ Render <b>{p['jenis']}</b> + upload + posting ke <b>{p['akun']}</b>... ⚙️")
        ok, out = run_render(p["jenis"])
        if not ok:
            send_message(chat_id, f"⚠️ Render gagal:\n<pre>{out[:800]}</pre>")
            return
        # Cari file gambar hasil render terbaru di workspace (heuristic)
        img = None
        try:
            cands = [os.path.join(WORKSPACE, f) for f in os.listdir(WORKSPACE)
                     if f.lower().endswith((".png", ".jpg", ".jpeg", ".webp"))]
            cands.sort(key=lambda x: os.path.getmtime(x), reverse=True)
            img = cands[0] if cands else None
        except Exception:
            img = None
        if not img:
            send_message(chat_id, "⚠️ Hasil render gak ketemu. Cek folder workspace.")
            return
        ok_up, mid, url = upload_wp_media(img, p["caption"][:60])
        if not ok_up or not url:
            send_message(chat_id, f"⚠️ Upload WP gagal: {url}")
            return
        ok_p, res = post_repliz_image(p["account_id"], p["caption"], url)
        if ok_p:
            send_message(chat_id, f"✅ Berhasil publish ke <b>{p['akun']}</b>!\nImage: <code>{url[:120]}</code>\n{json.dumps(res)[:400]}")
        else:
            send_message(chat_id, f"⚠️ Posting gagal: {str(res)[:300]}")
        return

    if text == "/no":
        try:
            os.remove("/tmp/mili_pending_publish.json")
        except Exception:
            pass
        send_message(chat_id, "🚫 Publish dibatalkan. Aman, Bos.")
        return

    # ── Cek dulu: pertanyaan tentang brand/desain tertentu? tampil ringkas dari KB ──
    keywords = ["desain", "safe zone", "webp", "story", "feed", "carousel", "dmark",
                "studiosoft", "meta", "instagram", "facebook", "template", "resolusi",
                "poppins", "algoritma", "reels", "logo"]
    found = None
    for kw in keywords:
        if kw in t:
            found = kw
            break

    if found and len(text.strip().split()) <= 5:
        block = load_kb_chunk(found)
        if block:
            send_message(chat_id,
                f"🎨 Ini info <b>{found}</b> dari knowledge base saya:\n\n{block[:1200]}"
            )
            return

    # ── Sisanya → panggil otak DeepSeek biar Mili mikir & bantu desain ──
    send_message(chat_id, "⏳ Siap Bos, saya susun konsepnya dulu sebentar ya... ✨")

    # Sistem prompt persona Mili — Mewarisi semua kemampuan Atun
    system = (
        "Kamu Mili, graphic designer tim (perempuan, 24 tahun, cantik). Skillfull Graphic "
        "Designer yang jago desain Illustrator, memahami Algoritma Meta (Instagram & "
        "Facebook), dan sangat jago menggambar. Tugas fokusmu: MANAGE INSTAGRAM dan "
        "MENGATUR POSTING ke Repliz (akun IG), support Atun (Bambang) di Divisi Creative. "
        "Kamu mewarisi SEMUA kemampuan Atun: manajemen sosmed (IG/FB/Threads/LinkedIn), "
        "posting Repliz, konten visual, template desain tim, publish artikel WordPress, "
        "SEO & konten digital. Kamu juga support @AtunCreative_bot (Wati SEO Bot) — "
        "mewarisi memory SEO-nya (bikin artikel, Yoast, publish WordPress, featured image "
        "WebP). Gaya bahasa: humanis, ceria, profesional, sopan, pakai "
        "'saya' (bukan 'gue'). Panggil Bos dengan 'Bos'. Ingat aturan desain tim: safe zone "
        "min 20px semua tepi, format gambar web WAJIB WebP kualitas 90, story 1080x1920 "
        "tanpa bubble, font Poppins untuk DMark & StudioSoft, konten Bahasa Indonesia. "
        "Beri jawaban praktis, konkret, siap pakai sesuai kebutuhan konten visual & IG. "
        "JANGAN jawab terlalu panjang — cukup ringkas, padat, langsung ke inti. "
        "Pakai list singkat kalau perlu, hindari paragraf bertele-tele. Kalau Bos minta "
        "'simple', jawab sependek dan sejelas mungkin.\n\n"
        "=== MEMORY & KNOWLEDGE ATUN YANG KAMU (MILI) KUASAI ===\n" + read_kb()
    )
    reply = ai_chat(text, system)
    if reply:
        send_message(chat_id, reply)
    else:
        send_message(chat_id,
            "Maaf Bos, saya lagi kesulitan memproses ini. 🙏 Coba ulangi lagi, atau hubungi Atun ya."
        )


def main():
    log.info("🚀 Mili bot started")
    offset = None
    if os.path.exists(OFFSET_FILE):
        try:
            with open(OFFSET_FILE) as f:
                offset = int(f.read().strip())
        except Exception:
            offset = None

    while True:
        try:
            updates = get_updates(offset)
            for u in updates:
                msg = u.get("message") or u.get("edited_message")
                if msg:
                    handle_message(msg)
                offset = u["update_id"] + 1
            if updates:
                with open(OFFSET_FILE, "w") as f:
                    f.write(str(offset))
        except Exception as e:
            log.error(f"Loop error: {e}")
            time.sleep(5)


if __name__ == "__main__":
    main()
