# ══════════════════════════════════════════════════════════
#  bot.py — SEO Automation Engine v6.1
# ══════════════════════════════════════════════════════════

import logging
import sys
import time
from datetime import datetime

from telegram import Update, InlineKeyboardButton, InlineKeyboardMarkup
from telegram.ext import (Application, CommandHandler, MessageHandler,
                          CallbackQueryHandler, filters, ContextTypes)

from config import (TELEGRAM_TOKEN, PHASE_CONFIG, TONE_MAP,
                    MAX_BULK_URLS, MAX_BULK_ARTICLE, TITLE_MODE_CONFIG)

from engines.title   import generate_title_single, generate_title_bulk
from engines.meta    import generate_meta_single, generate_meta_bulk
from engines.article import (generate_article_single, generate_article_bulk,
                              generate_article_from_context)


# ══════════════════════════════════════════════════════════
#  LOGGING
# ══════════════════════════════════════════════════════════
class _Fmt(logging.Formatter):
    _C = {'DEBUG':'\033[90m','INFO':'\033[96m','WARNING':'\033[93m',
          'ERROR':'\033[91m','CRITICAL':'\033[95m'}
    _I = {'DEBUG':'🔍','INFO':'✦','WARNING':'⚠','ERROR':'✖','CRITICAL':'🚨'}
    def format(self, r):
        t   = datetime.fromtimestamp(r.created).strftime('%H:%M:%S')
        c   = self._C.get(r.levelname,'\033[97m')
        i   = self._I.get(r.levelname,'·')
        n   = r.name.split('.')[-1][:12]
        msg = r.getMessage()
        if r.levelname in ('ERROR','CRITICAL'): msg = f'\033[91m{msg}\033[0m'
        elif r.levelname == 'WARNING':          msg = f'\033[93m{msg}\033[0m'
        return f'\033[90m[{t}]\033[0m {c}{i} {r.levelname:<7}\033[0m \033[90m│\033[0m \033[2m{n:<12}\033[0m \033[90m→\033[0m {msg}'

def setup_logging():
    h = logging.StreamHandler(sys.stdout)
    h.setFormatter(_Fmt())
    r = logging.getLogger()
    r.handlers.clear(); r.addHandler(h); r.setLevel(logging.INFO)
    for noisy in ('httpcore','telegram.vendor','google'):
        logging.getLogger(noisy).setLevel(logging.WARNING)

logger = logging.getLogger(__name__)


# ══════════════════════════════════════════════════════════
#  STARTUP
# ══════════════════════════════════════════════════════════
def run_startup():
    import os
    os.system('cls' if os.name == 'nt' else 'clear')
    C = '\033[96m\033[1m'; R = '\033[0m'; M = '\033[95m\033[1m'; G = '\033[92m'
    for line in [C,
        r'  ███████╗███████╗ ██████╗      ██████╗  ██████╗ ████████╗',
        r'  ██╔════╝██╔════╝██╔═══██╗     ██╔══██╗██╔═══██╗╚══██╔══╝',
        r'  ███████╗█████╗  ██║   ██║     ██████╔╝██║   ██║   ██║   ',
        r'  ╚════██║██╔══╝  ██║   ██║     ██╔══██╗██║   ██║   ██║   ',
        r'  ███████║███████╗╚██████╔╝     ██████╔╝╚██████╔╝   ██║   ',
        r'  ╚══════╝╚══════╝ ╚═════╝      ╚═════╝  ╚═════╝    ╚═╝   ',
        R]:
        print(line); time.sleep(0.04)
    print(f'{M}  ⚡ SEO AUTOMATION ENGINE  ·  v6.1  ·  Claude + Gemini{R}\n')
    print(f'  \033[90m{"─"*62}\033[0m')
    for icon, label in [('🔑','Credentials loaded'),('🧠','Claude Sonnet + Haiku ready'),
                        ('🔮','Gemini Pro→Flash failover ready'),('💾','Prompt caching on'),
                        ('📦','Engine modules loaded'),('📡','Telegram polling ready')]:
        sys.stdout.write(f'  {icon}  \033[97m{label:<36}\033[0m')
        sys.stdout.flush(); time.sleep(0.25)
        print(f'  {G}✓\033[0m')
    print(f'\n  \033[90m{"─"*62}\033[0m')
    print(f'\n  {G}\033[1m◉  BOT IS ONLINE\033[0m  \033[90m—  all systems nominal\033[0m\n')
    print(f'  \033[90m{"─"*62}\033[0m\n')


# ══════════════════════════════════════════════════════════
#  INPUT FORMAT CONFIG
# ══════════════════════════════════════════════════════════
MODE_KEYS = {
    'TITLE_SINGLE':   ['url', 'brand', 'keyword'],
    'TITLE_BULK':     ['url', 'brand', 'keyword'],
    'META_SINGLE':    ['url', 'brand', 'title'],
    'META_BULK':      ['url', 'brand', 'title'],
    'ARTICLE_SINGLE': ['brand', 'title', 'meta_desc'],
    'ARTICLE_BULK':   ['brand', 'title', 'meta_desc'],
    'FULL':           ['url', 'brand', 'keyword'],
}

# Input hint — ditampilin setelah user pilih AI
MODE_HINT = {
    'TITLE_SINGLE':   (
        "Kirim 3 baris\n\n"
        "`URL`\n`BRAND`\n`KEYWORD`"
    ),
    'TITLE_BULK':     (
        "Kirim hingga 10 block, pisahkan dengan baris kosong\n\n"
        "`URL`\n`BRAND`\n`KEYWORD`\n\n"
        "`URL`\n`BRAND`\n`KEYWORD`"
    ),
    'META_SINGLE':    (
        "Kirim 3 baris\n\n"
        "`URL`\n`BRAND`\n`TITLE TAG`"
    ),
    'META_BULK':      (
        "Kirim hingga 10 block, pisahkan dengan baris kosong\n\n"
        "`URL`\n`BRAND`\n`TITLE TAG`\n\n"
        "`URL`\n`BRAND`\n`TITLE TAG`"
    ),
    'ARTICLE_SINGLE': (
        "Kirim 3 baris\n\n"
        "`BRAND`\n`TITLE TAG`\n`META DESC`"
    ),
    'ARTICLE_BULK':   (
        "Kirim hingga 5 block, pisahkan dengan baris kosong\n\n"
        "`BRAND`\n`TITLE TAG`\n`META DESC`\n\n"
        "`BRAND`\n`TITLE TAG`\n`META DESC`"
    ),
    'FULL':           (
        "Kirim 3 baris\n\n"
        "`URL`\n`BRAND`\n`KEYWORD`"
    ),
}

MODE_LABEL = {
    'TITLE_SINGLE':   'Title Generator',
    'TITLE_BULK':     'Title Generator  ·  Bulk',
    'META_SINGLE':    'Meta Desc Generator',
    'META_BULK':      'Meta Desc Generator  ·  Bulk',
    'ARTICLE_SINGLE': 'Artikel Generator',
    'ARTICLE_BULK':   'Artikel Generator  ·  Bulk',
    'FULL':           'Full Content',
}


def _parse_single(text: str, keys: list):
    lines = [l.strip() for l in text.strip().split('\n') if l.strip()]
    if len(lines) < len(keys):
        return None
    return {k: lines[i] for i, k in enumerate(keys)}


def _parse_bulk(text: str, keys: list, max_n: int) -> list:
    blocks = [b.strip() for b in text.strip().split('\n\n') if b.strip()]
    result = []
    for block in blocks[:max_n]:
        p = _parse_single(block, keys)
        if p:
            result.append(p)
    return result


# ══════════════════════════════════════════════════════════
#  KEYBOARD BUILDER
# ══════════════════════════════════════════════════════════
def _kb(*rows):
    return InlineKeyboardMarkup(
        [[InlineKeyboardButton(t, callback_data=d) for t, d in row] for row in rows]
    )

# ── Static keyboards ──────────────────────────────────────
KB_MAIN = _kb(
    [('⚡  Content Generator', 'menu_content')],
    [('◻  HTML Generator',     'menu_html')]
)

KB_CONTENT = _kb(
    [('📝  Title Generator',   'feature_TITLE')],
    [('🔍  Meta Description',  'feature_META')],
    [('📄  Artikel',           'feature_ARTICLE')],
    [('✦  Full Content',       'mode_FULL')],
    [('←  Kembali',            'menu_main')]
)

KB_PHASE = _kb(
    [('Awal   0–3 bln',  'set_phase_AWAL'),   ('Tengah   50/50',   'set_phase_TENGAH')],
    [('Akhir   Full KW', 'set_phase_AKHIR'),  ('Mature   Defend',  'set_phase_MATURE')]
)

KB_TMODE = _kb(
    [('🔀  Semantic Blend',    'set_tmode_BLEND'),
     ('🎯  Full Keyword',      'set_tmode_FULLKW')],
    [('🎮  Full Game Online',  'set_tmode_FULLGAME'),
     ('🌿  Pure Niche',        'set_tmode_PURE')]
)

KB_AI = _kb(
    [('⚡  Claude',  'set_ai_claude'),
     ('🔮  Gemini',  'set_ai_gemini')]
)

KB_TONE = lambda: InlineKeyboardMarkup(
    [[InlineKeyboardButton(t, callback_data=f'set_tone_{t}')] for t in TONE_MAP]
)


# ══════════════════════════════════════════════════════════
#  UI TEXT HELPERS
# ══════════════════════════════════════════════════════════
def _ai_tag(ai: str) -> str:
    return '⚡ Claude' if ai == 'claude' else '🔮 Gemini'

def _processing_msg(label: str, ai: str, detail: str = '') -> str:
    lines = [f'*{label}*', _ai_tag(ai)]
    if detail:
        lines.append(detail)
    lines.append('_Generating..._')
    return '\n'.join(lines)


# ══════════════════════════════════════════════════════════
#  HANDLERS
# ══════════════════════════════════════════════════════════
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    context.user_data.clear()
    text = (
        "*SEO Automation Engine*\n"
        "_Powered by Claude  ·  Gemini_\n\n"
        "Pilih modul untuk mulai"
    )
    if update.message:
        await update.message.reply_text(text, reply_markup=KB_MAIN, parse_mode="Markdown")
    else:
        await update.callback_query.edit_message_text(text, reply_markup=KB_MAIN, parse_mode="Markdown")


async def handle_input(update: Update, context: ContextTypes.DEFAULT_TYPE):
    mode = context.user_data.get('workflow_mode')
    if not mode:
        await update.message.reply_text(
            "Sesi belum aktif\nKetik /start dan pilih mode dulu",
            parse_mode="Markdown")
        return

    text = update.message.text.strip()
    keys = MODE_KEYS.get(mode, [])

    try:
        # ── BULK ────────────────────────────────────────
        if 'BULK' in mode:
            max_n = MAX_BULK_ARTICLE if 'ARTICLE' in mode else MAX_BULK_URLS
            items = _parse_bulk(text, keys, max_n)
            if not items:
                await update.message.reply_text(
                    f"*Format tidak valid*\n\n{MODE_HINT.get(mode,'')}",
                    parse_mode="Markdown"); return
            context.user_data['bulk_data'] = items
            ai = context.user_data.get('ai_provider', 'claude')

            if 'ARTICLE' in mode:
                await update.message.reply_text(
                    f"*{len(items)} domain diterima*\n{_ai_tag(ai)}\n_Generating..._",
                    parse_mode="Markdown")
                await generate_article_bulk(update.message, context)
                return

            await update.message.reply_text(
                f"*{len(items)} domain diterima*\n\nPilih fase transisi",
                reply_markup=KB_PHASE, parse_mode="Markdown"); return

        # ── ARTICLE SINGLE — langsung generate ──────────
        if mode == 'ARTICLE_SINGLE':
            parsed = _parse_single(text, keys)
            if not parsed:
                await update.message.reply_text(
                    f"*Format tidak valid*\n\n{MODE_HINT['ARTICLE_SINGLE']}",
                    parse_mode="Markdown"); return
            context.user_data.update(parsed)
            ai = context.user_data.get('ai_provider', 'claude')
            await update.message.reply_text(
                f"*Artikel Generator*\n{_ai_tag(ai)}\n_Generating..._",
                parse_mode="Markdown")
            await generate_article_single(update.message, context)
            return

        # ── SINGLE + FULL → fase ─────────────────────────
        parsed = _parse_single(text, keys)
        if not parsed:
            await update.message.reply_text(
                f"*Format tidak valid*\n\n{MODE_HINT.get(mode,'')}",
                parse_mode="Markdown"); return
        context.user_data.update(parsed)

        prompt = "Pilih fase transisi" if mode != 'FULL' else "*Full Content*\nPilih fase transisi"
        await update.message.reply_text(prompt, reply_markup=KB_PHASE, parse_mode="Markdown")

    except Exception as e:
        await update.message.reply_text(f"Input error\n`{e}`", parse_mode="Markdown")


async def handle_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    await query.answer()
    data  = query.data

    # ── Navigation ──────────────────────────────────────
    if data == 'menu_main':
        await start(update, context); return

    elif data == 'menu_content':
        await query.edit_message_text(
            "*Content Generator*\nPilih fitur",
            reply_markup=KB_CONTENT, parse_mode="Markdown"); return

    elif data == 'menu_html':
        await query.edit_message_text(
            "*HTML Generator*\n\n_Fitur ini sedang dalam pengembangan_\n\nKembali ke menu untuk pilih fitur lain",
            reply_markup=_kb([('←  Kembali', 'menu_content')]),
            parse_mode="Markdown"); return

    # ── Feature sub-menus ───────────────────────────────
    elif data == 'feature_TITLE':
        await query.edit_message_text(
            "*Title Generator*\n3 title per URL  ·  Claude atau Gemini\n\nPilih mode",
            reply_markup=_kb(
                [('Single  1 URL',  'mode_TITLE_SINGLE'),
                 ('Bulk  max 10',   'mode_TITLE_BULK')],
                [('←  Kembali',     'menu_content')]
            ), parse_mode="Markdown"); return

    elif data == 'feature_META':
        await query.edit_message_text(
            "*Meta Desc Generator*\n2 meta desc per URL  ·  Claude atau Gemini\n\nPilih mode",
            reply_markup=_kb(
                [('Single  1 URL',  'mode_META_SINGLE'),
                 ('Bulk  max 10',   'mode_META_BULK')],
                [('←  Kembali',     'menu_content')]
            ), parse_mode="Markdown"); return

    elif data == 'feature_ARTICLE':
        await query.edit_message_text(
            "*Artikel Generator*\n1 artikel per domain  ·  Claude atau Gemini\n\nPilih mode",
            reply_markup=_kb(
                [('Single  1 domain', 'mode_ARTICLE_SINGLE'),
                 ('Bulk  max 5',      'mode_ARTICLE_BULK')],
                [('←  Kembali',       'menu_content')]
            ), parse_mode="Markdown"); return

    # ── Mode → AI selector ──────────────────────────────
    elif data.startswith('mode_'):
        mode  = data.replace('mode_', '')
        context.user_data['workflow_mode'] = mode
        label = MODE_LABEL.get(mode, mode)
        hint  = MODE_HINT.get(mode, '')
        await query.edit_message_text(
            f"*{label}*\n\nPilih AI",
            reply_markup=KB_AI, parse_mode="Markdown"); return

    # ── AI selected → tampil hint, tunggu user kirim input ──
    elif data.startswith('set_ai_'):
        ai    = data.replace('set_ai_', '')
        context.user_data['ai_provider'] = ai
        # Reset sisa data lama dari sesi sebelumnya
        for k in ('url','brand','keyword','title','meta_desc','bulk_data',
                  'web_context','selected_phase','selected_tone','title_mode',
                  'titles','meta_options','selected_title','selected_meta',
                  'revision_note','step'):
            context.user_data.pop(k, None)

        mode  = context.user_data.get('workflow_mode', '')
        label = MODE_LABEL.get(mode, mode)
        hint  = MODE_HINT.get(mode, '')

        # Tampil hint saja — fase/generate dipicu oleh handle_input setelah user kirim data
        await query.edit_message_text(
            f"*{label}*\n{_ai_tag(ai)}\n\n{hint}",
            parse_mode="Markdown"); return

    # ── Phase ───────────────────────────────────────────
    elif data.startswith('set_phase_'):
        context.user_data['selected_phase'] = PHASE_CONFIG[data.split('_')[2]]
        mode = context.user_data.get('workflow_mode', '')

        # Title & Full → mode title dulu
        if 'TITLE' in mode or mode == 'FULL':
            await query.edit_message_text(
                "Pilih mode title",
                reply_markup=KB_TMODE, parse_mode="Markdown")
        else:
            await query.edit_message_text(
                "Pilih tone",
                reply_markup=KB_TONE(), parse_mode="Markdown")

    # ── Title mode ──────────────────────────────────────
    elif data.startswith('set_tmode_'):
        tmode = data.replace('set_tmode_', '')
        context.user_data['title_mode'] = tmode
        label = TITLE_MODE_CONFIG.get(tmode, {}).get('label', '')
        await query.edit_message_text(
            f"Mode  `{label}`\n\nPilih tone",
            reply_markup=KB_TONE(), parse_mode="Markdown")

    # ── Tone → dispatch ─────────────────────────────────
    elif data.startswith('set_tone_'):
        context.user_data['selected_tone'] = data.split('set_tone_')[1]
        mode       = context.user_data.get('workflow_mode', '')
        tmode      = context.user_data.get('title_mode', 'BLEND')
        ai         = context.user_data.get('ai_provider', 'claude')
        bulk_n     = len(context.user_data.get('bulk_data', []))
        tmode_lbl  = TITLE_MODE_CONFIG.get(tmode, {}).get('label', '')

        if mode == 'TITLE_BULK':
            await query.edit_message_text(
                _processing_msg('Title  ·  Bulk', ai, f'{bulk_n} domain  ·  {tmode_lbl}'),
                parse_mode="Markdown")
            await generate_title_bulk(query.message, context)

        elif 'TITLE' in mode:
            await query.edit_message_text(
                _processing_msg('Title', ai, tmode_lbl),
                parse_mode="Markdown")
            await generate_title_single(query.message, context)

        elif mode == 'META_BULK':
            await query.edit_message_text(
                _processing_msg('Meta Desc  ·  Bulk', ai, f'{bulk_n} domain'),
                parse_mode="Markdown")
            await generate_meta_bulk(query.message, context)

        elif 'META' in mode:
            await query.edit_message_text(
                _processing_msg('Meta Desc', ai),
                parse_mode="Markdown")
            await generate_meta_single(query.message, context)

        elif mode == 'FULL':
            await query.edit_message_text(
                _processing_msg('Full Content  ·  1/3  Title', ai, tmode_lbl),
                parse_mode="Markdown")
            await generate_title_single(query.message, context)

    # ── Title: regen + notes ─────────────────────────────
    elif data == 'regen_title':
        context.user_data.pop('revision_note', None)
        tmode = context.user_data.get('title_mode', 'BLEND')
        ai    = context.user_data.get('ai_provider', 'claude')
        await query.edit_message_text(
            _processing_msg('Title  ·  Regen', ai,
                            TITLE_MODE_CONFIG.get(tmode,{}).get('label','')),
            parse_mode="Markdown")
        await generate_title_single(query.message, context)

    elif data == 'note_title':
        await query.edit_message_text(
            "*Revision Mode*\n\nTulis instruksi revisi\n_contoh: lebih clickbait, hilangkan brand di depan_",
            parse_mode="Markdown")
        context.user_data['step'] = 'WAITING_REVISION_NOTE'

    elif data.startswith('select_title_'):
        idx   = int(data.split('_')[2])
        title = context.user_data['titles'][idx]
        context.user_data['selected_title'] = title
        mode  = context.user_data.get('workflow_mode')
        ai    = context.user_data.get('ai_provider', 'claude')

        if mode == 'FULL':
            context.user_data['title'] = title
            await query.edit_message_text(
                _processing_msg('Full Content  ·  2/3  Meta Desc', ai),
                parse_mode="Markdown")
            await generate_meta_single(query.message, context)
        else:
            await query.edit_message_text(
                f"*Title terpilih*\n\n`{title}`",
                parse_mode="Markdown")
            await query.message.reply_text(
                "Selesai",
                reply_markup=_kb(
                    [('Ulangi', f'mode_{mode}'), ('Menu', 'menu_main')]
                ), parse_mode="Markdown")
            context.user_data.clear()

    # ── Meta: regen + select ─────────────────────────────
    elif data == 'regen_meta':
        ai = context.user_data.get('ai_provider', 'claude')
        await query.edit_message_text(
            _processing_msg('Meta Desc  ·  Regen', ai),
            parse_mode="Markdown")
        await generate_meta_single(query.message, context)

    elif data.startswith('select_meta_'):
        idx       = int(data.split('_')[2])
        meta_opts = context.user_data.get('meta_options', [])
        ai        = context.user_data.get('ai_provider', 'claude')
        if idx < len(meta_opts):
            desc, _ = meta_opts[idx]
            context.user_data['selected_meta'] = desc
        mode = context.user_data.get('workflow_mode')

        if mode == 'FULL':
            await query.edit_message_text(
                _processing_msg('Full Content  ·  3/3  Artikel', ai),
                parse_mode="Markdown")
            await generate_article_from_context(query.message, context)
        else:
            await query.edit_message_text(
                f"*Meta terpilih*\n\n`{context.user_data.get('selected_meta','')[:120]}`",
                parse_mode="Markdown")
            context.user_data.clear()


# ── Text edits ────────────────────────────────────────────
async def handle_text_edits(update: Update, context: ContextTypes.DEFAULT_TYPE):
    step = context.user_data.get('step')
    text = update.message.text.strip()
    mode = context.user_data.get('workflow_mode')

    if step == 'WAITING_REVISION_NOTE':
        context.user_data['revision_note'] = text
        ai  = context.user_data.get('ai_provider', 'claude')
        msg = await update.message.reply_text(
            f"*Revision*  {_ai_tag(ai)}\n`{text}`\n_Re-evaluating..._",
            parse_mode="Markdown")
        await generate_title_single(msg, context)
        return

    if step == 'WAITING_TITLE_SELECTION':
        context.user_data['selected_title'] = text
        await update.message.reply_text(
            f"*Title terpilih*\n\n`{text}`",
            reply_markup=_kb(
                [('Ulangi', f'mode_{mode}'), ('Menu', 'menu_main')]
            ), parse_mode="Markdown")
        context.user_data.clear()


# ══════════════════════════════════════════════════════════
#  MAIN
# ══════════════════════════════════════════════════════════
def main():
    setup_logging()
    run_startup()

    app = Application.builder().token(TELEGRAM_TOKEN).build()
    app.add_handler(CommandHandler("start", start))
    app.add_handler(CallbackQueryHandler(handle_callback))
    app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, handle_input))

    logger.info("Bot polling started")
    app.run_polling()


if __name__ == "__main__":
    main()