#!/usr/bin/env python3
"""
UjangContentWriter_bot — Content Writer Supporter (Long polling)
Token: 8918669310:AAFj70wuF1plrLyd09y19p6pwgI3GBrYeiE

Gaya bahasa: Humanis, relaks, cepat, dan ahli dalam support.
Peran: Membantu update konten yang banyak (content writer) & membantu semua bot lain.
Knowledge base: ujang_knowledge_base.md
"""

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

# ── AI (DeepSeek API asli) biar Ujang bisa mikir & kerja ──
DEEPSEEK_KEY = "sk-8f1452fbeb524e69add6b092fdd89675"  # DeepSeek API (aktif)
DEEPSEEK_URL = "https://api.deepseek.com/v1/chat/completions"
AI_MODEL = "deepseek-chat"  # DeepSeek Chat (per Bos)
AI_BACKUP_MODELS = ["deepseek-v4-flash", "deepseek-chat"]  # cadangan kalau main timeout


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})

    # Coba model utama (AI_MODEL), lalu cadangan
    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)  # 25s biar nggak nunggu lama
                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  # 4xx/5xx udah pasti gagal, ganti model
            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:
    log = logging.getLogger("ujang")
    log.error(f"repliz_client import gagal: {e}")
    ReplizClient = None
    ACC = None

BOT_TOKEN = "8918669310:AAFj70wuF1plrLyd09y19p6pwgI3GBrYeiE"
API_URL = f"https://api.telegram.org/bot{BOT_TOKEN}"
OFFSET_FILE = "/tmp/ujang_bot_offset.txt"
KB_FILE = "/home/mantapman/.hermes/profiles/atun/workspace/ujang_knowledge_base.md"
MEMORY_DIR = "/home/mantapman/.hermes/profiles/atun/workspace/memory"
MEMORY_MAIN = "/home/mantapman/.hermes/profiles/atun/workspace/MEMORY.md"

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


def send_message(chat_id, text, retries=4):
    """Kirim pesan ke chat_id, dengan retry berlapis untuk handle koneksi putus."""
    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})")
                # 429 = rate limit → nunggu Retry-After
                if r.status_code == 429:
                    time.sleep(3)
                    continue
                # 400 (bad request, misal text kosong/chat invalid) gak akan berhasil → stop
                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)  # backoff: 2s, 4s, 6s, 8s
    log.error(f"Send GAGAL total, err terakhir: {last_err}")
    return False


def get_updates(offset=None):
    params = {"timeout": 30, "allowed_updates": ["message"]}
    if offset:
        params["offset"] = offset
    try:
        r = requests.get(f"{API_URL}/getUpdates", params=params, timeout=35)
        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: {e}")
    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 (kasar)."""
    kb = read_kb()
    lines = kb.splitlines()
    # cari baris yang mengandung keyword, ambil blok di sekitarnya
    hits = [i for i, l in enumerate(lines) if keyword.lower() in l.lower()]
    if not hits:
        return ""
    start = max(0, hits[0] - 3)
    # ambil sampai 40 baris atau ketemu header besar berikutnya
    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 isi MEMORY.md + memory/*.md jadi satu string."""
    chunks = []
    for path in [MEMORY_MAIN]:
        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):
    """Cari keyword di semua memory tim, return list (file, kutipan)."""
    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"):
        chunks = read_memory_all()
        total_lines = sum(len(c.splitlines()) for _, c in chunks)
        send_message(chat_id,
            f"🧠 Saya udah punya akses ke <b>SEMUA memory tim</b>: {len(chunks)} file, "
            f"±{total_lines} baris catatan (MEMORY.md + memory/*.md).\n\n"
            f"Berisi pengetahuan dari <b>Atun/Wati, Suwarno, dan semua bot</b>:\n"
            f"• Client & brand\n"
            f"• Aturan & template konten\n"
            f"• Progress proyek (DAS, LEIT, TSC, dll)\n"
            f"• Teknis & kredensial tim\n\n"
            f"Coba: <code>/cari Yaost</code> atau <code>/cari Repliz</code> buat nyari topik tertentu. 😊"
        )
        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


AKUN_ALIAS = {
    "studiosoft_threads": "STUDIOSOFT_THREADS", "ss_threads": "STUDIOSOFT_THREADS",
    "studiosoft_ig": "STUDIOSOFT_IG", "ss_ig": "STUDIOSOFT_IG",
    "studiosoft_fb": "STUDIOSOFT_FB", "ss_fb": "STUDIOSOFT_FB",
    "studiosoft_linkedin": "STUDIOSOFT_LINKEDIN", "ss_li": "STUDIOSOFT_LINKEDIN",
    "dmark_fb": "DMARK_FB", "dmark_ig": "DMARK_IG", "dmark_linkedin": "DMARK_LINKEDIN", "dmark_li": "DMARK_LINKEDIN",
    "leit_fb": "LEIT_FB", "leit_threads": "LEIT_THREADS", "leit_linkedin": "LEIT_LINKEDIN", "leit_li": "LEIT_LINKEDIN",
    "tslaw_fb": "TSLAW_FB", "tslaw_threads": "TSLAW_THREADS", "tslaw_linkedin": "TSLAW_LINKEDIN", "tslaw_li": "TSLAW_LINKEDIN",
    "arelancor_fb": "ARELANCOR_FB", "arelancor_ig": "ARELANCOR_IG",
    "das_fb": "DAS_FB", "das_ig": "DAS_IG",
    "tsc_threads": "TSC_THREADS",
    "rsia_fb": "RSIA_FB",
}


def resolve_account(name):
    if not ACC:
        return None
    key = AKUN_ALIAS.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 Repliz yang bisa dipake:</b>\n"
            "• studiosoft_threads / studiosoft_ig / studiosoft_fb / studiosoft_linkedin\n"
            "• dmark_fb / dmark_ig / dmark_linkedin\n"
            "• leit_fb / leit_threads / leit_linkedin\n"
            "• tslaw_fb / tslaw_threads / tslaw_linkedin\n"
            "• arelancor_fb / arelancor_ig\n"
            "• das_fb / das_ig\n"
            "• tsc_threads\n"
            "• rsia_fb\n\n"
            "Cara: <code>/post studiosoft_fb CAPTION</code> (post sekarang)\n"
            "atau <code>/jadwal studiosoft_fb 03/08/2026 17:00 CAPTION</code>"
        )
        return

    if cmd == "/post" and len(parts) >= 3:
        akun = parts[1]
        caption = parts[2]
        account_id = resolve_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_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>Ujang</b>, supporter tim content writer.\n"
            "Saya siap bantu update konten banyak & bantu kerjaan bot lain — "
            "cepat, santai, tapi tetep ahli. 😄\n\n"
            "Perintah: /help buat lihat yang bisa saya lakukan."
        )
        return

    if text == "/help":
        send_message(chat_id,
            "Perintah yang tersedia:\n"
            "/start — Mulai\n"
            "/help — Bantuan\n"
            "/kara — Ringkasan tim & Bos\n"
            "/aturan — Aturan konten penting\n"
            "/clients — Daftar client\n"
            "/kb — Info knowledge base\n\n"
            "Atau langsung ketik topik (misal: 'StudioSoft', 'tarif posting', 'LEIT') "
            "biar saya cariin di knowledge base. 🚀"
        )
        return

    if text == "/kara":
        send_message(chat_id,
            "🧑‍💼 <b>Bos:</b> Uky Yanuhandoko — Pakar SEO, Digital Marketing Specialist, "
            "Website Developer (StudioSoft).\n\n"
            "🤖 <b>Tim:</b> Bambang/Atun (asisten utama sosmed), Wati (finance), "
            "Suwarno (consulting), Ujang (content supporter).\n\n"
            "Saya supporter — bantu update konten & bantu semua bot lain. 💪"
        )
        return

    if text == "/aturan":
        send_message(chat_id,
            "📌 <b>Aturan konten penting:</b>\n"
            "• Semua konten Bahasa Indonesia, gaya humanis pakai 'saya'\n"
            "• 🚫 NO caption redundant — tiap post wajib angle/insight beda\n"
            "• Cek link sebelum posting (wajib 200 & ada isinya)\n"
            "• Threads max 500 karakter\n"
            "• SSC (surabaya-skincentre) vs TSC (theskincodeclinic) JANGAN tertukar\n"
            "• Positioning Bos FIXED: Pakar SEO, Digital Marketing Specialist, Developer"
        )
        return

    if text == "/clients":
        send_message(chat_id,
            "🌐 <b>Client tim:</b>\n"
            "• StudioSoft & DMark Creative (brand Bos)\n"
            "• Apotek Arek Lancor, TS Law, DAS Bengkel\n"
            "• LEIT, TSC, Surabaya Skin Centre (SSC)\n"
            "• RSIA Kendangsari, EveryBe, Abimata, Nucleasoft, Yasma Natura\n"
            "• + pemerintahan (Pemkot Surabaya, Pemprov Jatim, dll)"
        )
        return

    if text == "/kb":
        send_message(chat_id,
            "📘 Knowledge base saya lengkap ada di <code>ujang_knowledge_base.md</code> — "
            "berisi semua client, aturan, template, akun sosial media, & credential tim.\n\n"
            "Saya juga bisa baca <b>SEMUA memory tim</b> (Atun/Wati/Suwarno). Coba "
            "<code>/ingat</code> atau ketik <code>/cari TOPIK</code>. 😊"
        )
        return

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

    if text in ("/post", "/jadwal", "/akun") or text.startswith(("/post ", "/jadwal ", "/akun ")):
        handle_repliz_command(chat_id, text)
        return

    # ── Cek dulu: ini pertanyaan/info tentang client tertentu? tampil ringkas dari KB ──
    keywords = ["atelier", "dmark", "studiosoft", "leit", "tsc", "ssc", "repliz",
                "invoice", "template", "everybe", "rsia", "abimata", "das", "ts law"]
    found = None
    for kw in keywords:
        if kw in t:
            found = kw
            break

    # Kalau cuma nyebut satu brand (pendek), tampil info cepat dari KB tanpa AI
    if found and len(text.strip().split()) <= 4:
        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 Ujang mikir & kerja (seperti Mbang) ──
    send_message(chat_id, "⏳ Siap Bos, aku kerjakan dulu sebentar ya... 😊")
    system = (
        "Kamu Ujang, supporter tim content writer (pria, 20 tahun) - muda, cepat, "
        "dapat diandalkan. Gaya bahasa: humanis, relaks, sopan, dan ahli dalam support. "
        "ATURAN WAJIB: (1) Selalu panggil lawan bicara 'Bos' - JANGAN panggil 'Anda', "
        "'kamu', 'bro', 'bang', atau 'lo/gue'. (2) Kamu menyebut diri sendiri 'aku/saya' - "
        "JANGAN pakai kata 'gue'. (3) Bahasa Indonesia formal santai yang sopan. "
        "(4) Jangan panggil Bos dengan istilah tidak sopan apa pun. "
        "(5) Jawab langsung ke inti, ringkas tapi lengkap, jangan bertele-tele. "
        "(6) Kalau diminta bikin konten (caption/artikel), kasih hasil langsung yang bisa dipakai, "
        "tidak perlu pembuka panjang atau basa-basi berlebihan. "
        "Kamu punya pengetahuan penuh tim (client, aturan, template, akun sosmed, "
        "positioning Bos). Berikut knowledge base tim:\n\n"
        + read_kb()
    )
    answer = ai_chat(text, system=system)
    if answer:
        send_message(chat_id, answer)
    else:
        # Coba jawab pakai knowledge base dulu sebelum nyerah
        kb_reply = load_kb_chunk(t[:20])
        if kb_reply:
            send_message(chat_id, kb_reply[:1200])
        else:
            send_message(chat_id,
                "Maaf Bos, otakku lagi eror bentar (server AI-nya delay). 😅\n"
                "Coba ulang beberapa saat lagi ya, atau kalau udah, ketik /help "
                "buat lihat perintah lain."
            )


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

    while True:
        try:
            updates = get_updates(offset)
            for u in updates:
                if "message" in u:
                    handle_message(u["message"])
                offset = u["update_id"] + 1
                with open(OFFSET_FILE, "w") as f:
                    f.write(str(offset))
            time.sleep(1)
        except KeyboardInterrupt:
            log.info("Stopped by user")
            break
        except Exception as e:
            log.error(f"Main loop error: {e}")
            time.sleep(5)


if __name__ == "__main__":
    main()
