# -*- coding: utf-8 -*-
import os
import re
import json
import logging
from dataclasses import dataclass
from typing import Dict, Pattern, List
from pathlib import Path
from datetime import datetime

from dotenv import load_dotenv
from telegram import Update, InlineKeyboardMarkup, InlineKeyboardButton
from telegram.ext import (
    ApplicationBuilder, ContextTypes,
    CommandHandler, MessageHandler, CallbackQueryHandler, ConversationHandler,
    filters
)

# -------------------- Configuración e init --------------------
load_dotenv()
TOKEN = os.getenv("TELEGRAM_TOKEN")

DATA_DIR = Path(__file__).parent / "data"

DEFAULT_CONFIG = {
    "website_url": "https://www.newintlcenter.org",
    "map_url": "https://www.google.com/maps?q=80+Maiden+Lane+13th+Floor+New+York+NY+10028",
    "default_lang": "es"
}

@dataclass
class Matcher:
    pattern: Pattern
    answer: str

I18N: Dict[str, dict] = {}
LANGS: Dict[str, str] = {}   # code -> display name
MATCHERS: Dict[str, List[Matcher]] = {}
CONFIG: Dict[str, str] = DEFAULT_CONFIG.copy()

# ---------- Texto al pie (siempre visible en cada respuesta) ----------
HINT_FALLBACKS = {
    "en": "💡 Type /menu to see options and /lang to change language.",
    "es": "💡 Escribe /menu para ver opciones y /lang para cambiar idioma.",
    "fr": "💡 Tape /menu pour voir les options et /lang pour changer de langue.",
    "ar": "💡 اكتب /menu لعرض الخيارات و /lang لتغيير اللغة.",
    "fa": "💡 ‎/menu را برای دیدن گزینه‌ها و /lang را برای تغییر زبان تایپ کنید.",
    "ru": "💡 Наберите /menu для опций и /lang для смены языка.",
    "uk": "💡 Введіть /menu, щоб побачити опції, та /lang, щоб змінити мову.",
}

def with_hint(s: str, lang: str = None) -> str:
    # Normaliza saltos de línea
    s = (s or "").replace("\\n", "\n").replace("\\r\\n", "\n")

    # 1) Si el i18n del idioma trae 'strings.hint', úsalo
    hint = None
    try:
        if lang and lang in I18N:
            hint_local = I18N[lang].get("strings", {}).get("hint")
            if isinstance(hint_local, str) and hint_local.strip():
                hint = hint_local
    except Exception:
        pass

    # 2) Si no hay, usa fallback del idioma; si tampoco, usa inglés
    if not hint:
        hint = HINT_FALLBACKS.get(lang, HINT_FALLBACKS["en"])

    return f"{s}\n\n{hint}"


# ------------------------------ Utilidades JSON ------------------------------
def _load_json(path: Path) -> dict:
    with path.open("r", encoding="utf-8") as f:
        return json.load(f)

def load_config() -> Dict[str, str]:
    cfg_path = DATA_DIR / "config.json"
    cfg = DEFAULT_CONFIG.copy()
    if cfg_path.exists():
        try:
            cfg.update(_load_json(cfg_path))
        except Exception as e:
            logging.warning(f"No se pudo leer config.json: {e}")
    return cfg

def discover_languages() -> Dict[str, dict]:
    """Busca archivos data/i18n_*.json y devuelve {code: {name, i18n}}."""
    langs = {}
    DATA_DIR.mkdir(parents=True, exist_ok=True)
    for p in DATA_DIR.glob("i18n_*.json"):
        code = p.stem.split("_", 1)[1]
        try:
            i18n = _load_json(p)
            name = i18n.get("name", code)
            langs[code] = {"name": name, "i18n": i18n}
        except Exception as e:
            logging.warning(f"No se pudo cargar {p.name}: {e}")
    return langs

def load_faq(lang: str) -> Dict[str, str]:
    """
    Carga data/faq_<lang>.json si existe; si no, arma un fallback simple.
    Placeholders: {WEBSITE_URL}, {MAP_URL}, {ADDRESS}, {MAP_LABEL}, {PROGRAMS}
    """
    path = DATA_DIR / f"faq_{lang}.json"
    if path.exists():
        try:
            return _load_json(path)
        except Exception as e:
            logging.warning(f"No se pudo leer {path.name}: {e}")

    i18n = I18N.get(lang, {})
    website = CONFIG["website_url"]
    map_label = i18n.get("open_map_label", "🗺️ Map")

    return {
        ("horario|horarios|schedule" if lang != "en" else "schedule|time|when"):
            i18n.get("schedule_text", f"🗓️ Schedules change. See: {website}"),

        ("direcci[oó]n|ubicaci[oó]n|address|location" if lang == "es" else
         "adresse|localisation|address|location" if lang == "fr" else
         "location|address|where"):
            f"📍 {{ADDRESS}}\n{map_label}: {{MAP_URL}}\n🌐 {website}",

        ("programa|programas|courses" if lang == "es" else
         "programme|programmes|cours" if lang == "fr" else
         "program|programs|course|english"):
            "{PROGRAMS}",

        ("costos|precio|fees|pagar" if lang == "es" else
         "coût|cout|prix|frais|payer|fees|price|pay" if lang == "fr" else
         "cost|price|fees|pay"):
            i18n.get("free_text", "💵 Our courses are free.") + f" {website}"
    }

def build_matchers(lang: str) -> List[Matcher]:
    data = load_faq(lang)
    matchers: List[Matcher] = []
    for patt, ans in data.items():
        matchers.append(Matcher(pattern=re.compile(patt, flags=re.IGNORECASE), answer=ans))
    return matchers

# ------------------------------ UI helpers ------------------------------
def t(lang: str, key: str) -> str:
    i18n = I18N.get(lang, {})
    strings = i18n.get("strings", {}) if isinstance(i18n.get("strings"), dict) else {}

    default_strings = {
        "welcome": "Hi! I’m the *International Center* assistant. How can I help you today?",
        "help": "Type your question (e.g., *schedule*, *apply*, *location*). Use /menu or /lang.",
        "lang_set": "✅ Language updated.",
        "no_match": f"I couldn’t find an exact answer 🤔. Check {DEFAULT_CONFIG['website_url']} or use /menu.",
        "choose_language": "Choose language / Elige idioma / Choisissez la langue:",
        # ---- Fallbacks del formulario ----
        "enroll_intro": "📝 Por favor comparte tus datos para cursos.",
        "ask_name": "👤 Tu nombre completo:",
        "ask_phone": "📱 Tu teléfono (incluye código de país si puedes):",
        "ask_email": "📧 Tu correo electrónico (o escribe *omitir*):",
        "ask_country": "🌎 ¿De qué país eres?",
        "ask_lang": "🗣️ ¿Qué idioma hablas principalmente?",
        "ask_internet": "🌐 ¿Tienes acceso estable a Internet?",
        "btn_yes": "Sí",
        "btn_no": "No",
        "ask_status": "🏷️ ¿Cuál es tu estatus en el país?",
        "status_pend_asy": "Asilo pendiente",
        "status_asy": "Asilado/a",
        "status_tps": "TPS",
        "status_cit": "Ciudadano/a",
        "status_other": "Otro (especificar)",
        "ask_status_other": "✍️ Especifica tu estatus:",
        "ask_heard": "📣 ¿Cómo supiste de nosotros?",
        "heard_friend": "Un amigo",
        "heard_web": "Página web",
        "heard_press": "Prensa",
        "heard_radio": "Radio",
        "heard_tv": "TV",
        "heard_other": "Otro (especificar)",
        "ask_heard_other": "✍️ Cuéntanos cómo supiste de nosotros:",
        "enroll_thanks": "✅ ¡Gracias! Te contactaremos pronto."
    }

    val = strings.get(key)
    if isinstance(val, str):
        cleaned = val.strip()
        # si es "..." o "…" forzamos fallback
        if cleaned and cleaned not in {"...", "…"}:
            return val

    fallback = default_strings.get(key, key)
    logging.info("t(): usando fallback para %s[%s] -> %r", lang, key, fallback)
    return fallback

def _color_label(idx: int, label: str) -> str:
    colors = ["🟦", "🟨", "🟥", "🟩", "🟪", "🟧"]
    return f"{colors[idx % len(colors)]} {label}"

def menu_keyboard(lang: str) -> InlineKeyboardMarkup:
    i18n = I18N.get(lang, {})
    m = i18n.get("menu", {})

    programs = _color_label(0, m.get("programs", "Programas"))
    schedule = _color_label(1, m.get("schedule", "Horarios"))
    location = _color_label(2, m.get("location", "Ubicación"))
    faq      = _color_label(5, m.get("faq", "FAQ"))
    enroll   = _color_label(3, m.get("enroll", "Interesado/a en cursos"))
    langlbl  = _color_label(4, m.get("language", "Language"))
    sitelbl  = m.get("site", "🌐 Sitio web")

    return InlineKeyboardMarkup([
        [InlineKeyboardButton(programs, callback_data="programs")],
        [InlineKeyboardButton(schedule, callback_data="schedule")],
        [InlineKeyboardButton(location, callback_data="location")],
        [InlineKeyboardButton(faq, callback_data="faq")],
        [InlineKeyboardButton(enroll, callback_data="enroll")],
        [InlineKeyboardButton(langlbl, callback_data="langmenu")],
        [InlineKeyboardButton(sitelbl, url=CONFIG["website_url"])]
    ])

def lang_keyboard() -> InlineKeyboardMarkup:
    row, rows = [], []
    for code, name in LANGS.items():
        row.append(InlineKeyboardButton(name, callback_data=f"setlang_{code}"))
        if len(row) == 3:
            rows.append(row); row = []
    if row:
        rows.append(row)
    return InlineKeyboardMarkup(rows)

def get_lang(context: ContextTypes.DEFAULT_TYPE) -> str:
    lang = context.user_data.get("lang")
    if lang in LANGS:
        return lang
    return CONFIG.get("default_lang", "en") if CONFIG.get("default_lang") in LANGS else next(iter(LANGS.keys()))

# ------------------------------- Handlers básicos ---------------------------------
async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    lang = get_lang(context)
    await update.message.reply_text(
        with_hint(t(lang, "welcome").replace("{WEBSITE_URL}", CONFIG["website_url"]), lang),
        reply_markup=menu_keyboard(lang),
        parse_mode="Markdown"
    )

async def help_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
    lang = get_lang(context)
    await update.message.reply_text(
        with_hint(t(lang, "help").replace("{WEBSITE_URL}", CONFIG["website_url"]), lang)
    )

async def menu_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
    lang = get_lang(context)
    await update.message.reply_text(with_hint("👇", lang), reply_markup=menu_keyboard(lang))

async def lang_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
    if context.args:
        code = context.args[0].lower()
        if code in LANGS:
            context.user_data["lang"] = code
            await update.message.reply_text(with_hint(t(code, "lang_set"), code))
            return
    current = get_lang(context)
    await update.message.reply_text(with_hint(t(current, "choose_language"), current), reply_markup=lang_keyboard())

# ------------------------------- Callback genérico --------------------------------
async def on_callback(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    data = query.data
    await query.answer()

    if data.startswith("setlang_"):
        code = data.split("_", 1)[1]
        if code in LANGS:
            context.user_data["lang"] = code
            await query.edit_message_text(with_hint(t(code, "lang_set"), code))
        return

    lang = get_lang(context)
    i18n = I18N.get(lang, {})

    if data == "programs":
        text = i18n.get("programs_text", "📚 Programs. {WEBSITE_URL}")
        kb = InlineKeyboardMarkup([[InlineKeyboardButton(i18n.get("menu", {}).get("site", "🌐 Website"), url=CONFIG["website_url"])]])
        await query.edit_message_text(
            with_hint(text.replace("{WEBSITE_URL}", CONFIG["website_url"]), lang),
            reply_markup=kb,
            parse_mode="Markdown",
            disable_web_page_preview=True
        )

    elif data == "schedule":
        text = i18n.get("schedule_text", f"🗓️ See schedules on: {CONFIG['website_url']}")
        await query.edit_message_text(with_hint(text.replace("{WEBSITE_URL}", CONFIG["website_url"]), lang))

    elif data == "location":
        addr = i18n.get("address", "80 Maiden Lane, 13th Floor, New York, NY 10028")
        open_map_label = i18n.get("open_map_label", "🗺️ Open map")
        site_label = i18n.get("menu", {}).get("site", "🌐 Website")
        msg = f"📍 {addr}"
        kb = InlineKeyboardMarkup([
            [InlineKeyboardButton(open_map_label, url=CONFIG["map_url"])],
            [InlineKeyboardButton(site_label, url=CONFIG["website_url"])],
        ])
        await query.edit_message_text(with_hint(msg, lang), reply_markup=kb)

    elif data == "faq":
        hint = i18n.get("faq_hint", t(lang, "help"))
        await query.edit_message_text(with_hint(hint, lang), parse_mode="Markdown")

    elif data == "langmenu":
        await query.edit_message_text(
            with_hint(t(get_lang(context), "choose_language"), lang),
            reply_markup=lang_keyboard()
        )

# ------------------------------- Conversación: Interesado en cursos ----------------
# Estados
ASK_NAME, ASK_PHONE, ASK_EMAIL, ASK_COUNTRY, ASK_LANG, ASK_INTERNET, ASK_STATUS, ASK_STATUS_OTHER, ASK_HEARD, ASK_HEARD_OTHER = range(10)
LEADS_FILE = DATA_DIR / "leads.csv"

EMAIL_RE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
PHONE_RE = re.compile(r"^\+?\d[\d\s\-().]{6,}$")

def internet_kb(lang: str) -> InlineKeyboardMarkup:
    return InlineKeyboardMarkup([
        [InlineKeyboardButton(t(lang, "btn_yes"), callback_data="internet_yes"),
         InlineKeyboardButton(t(lang, "btn_no"), callback_data="internet_no")]
    ])

def status_kb(lang: str) -> InlineKeyboardMarkup:
    return InlineKeyboardMarkup([
        [InlineKeyboardButton(t(lang, "status_pend_asy"), callback_data="status_pend_asy")],
        [InlineKeyboardButton(t(lang, "status_asy"), callback_data="status_asy")],
        [InlineKeyboardButton(t(lang, "status_tps"), callback_data="status_tps")],
        [InlineKeyboardButton(t(lang, "status_cit"), callback_data="status_cit")],
        [InlineKeyboardButton(t(lang, "status_other"), callback_data="status_other")]
    ])

def heard_kb(lang: str) -> InlineKeyboardMarkup:
    return InlineKeyboardMarkup([
        [InlineKeyboardButton(t(lang, "heard_friend"), callback_data="heard_friend")],
        [InlineKeyboardButton(t(lang, "heard_web"), callback_data="heard_web")],
        [InlineKeyboardButton(t(lang, "heard_press"), callback_data="heard_press")],
        [InlineKeyboardButton(t(lang, "heard_radio"), callback_data="heard_radio")],
        [InlineKeyboardButton(t(lang, "heard_tv"), callback_data="heard_tv")],
        [InlineKeyboardButton(t(lang, "heard_other"), callback_data="heard_other")],
    ])

# --- YES/NO y estatus por texto ---
YES_WORDS = {"si", "sí", "s", "yes", "y", "oui", "ja", "evet", "да", "так"}
NO_WORDS  = {"no", "n", "non", "nein", "hayir", "нет", "ні", "not"}

def parse_yes_no(text: str):
    t_ = (text or "").strip().lower().replace("í", "i")
    if t_ in YES_WORDS: return "yes"
    if t_ in NO_WORDS:  return "no"
    return None

STATUS_SYNONYMS = {
    "pend_asy": ["asilo pendiente", "pendiente asilo", "pending asylum", "solicitud de asilo"],
    "asy":      ["asilado", "asilada", "asylee", "refugiado", "refugiada", "asilo aprobado"],
    "tps":      ["tps"],
    "cit":      ["ciudadano", "ciudadana", "citizen", "us citizen"],
    "other":    ["otro", "otra", "otros", "other"]
}

def match_status(text: str):
    t_ = (text or "").strip().lower()
    for code, words in STATUS_SYNONYMS.items():
        if any(w in t_ for w in words):
            return code
    return None

HEARD_SYNONYMS = {
    "heard_friend": ["amigo", "una amiga", "friend", "familia", "conocido"],
    "heard_web":    ["web", "pagina", "página", "website", "site", "internet"],
    "heard_press":  ["prensa", "periodico", "periódico", "diario", "press", "news"],
    "heard_radio":  ["radio"],
    "heard_tv":     ["tv", "tele", "television", "televisión"],
    "heard_other":  ["otro", "other"],
}

def match_heard(text: str):
    t_ = (text or "").strip().lower()
    for code, words in HEARD_SYNONYMS.items():
        if any(w in t_ for w in words):
            return code
    return None

# Entry por comando
async def enroll_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
    lang = get_lang(context)
    await update.message.reply_text(with_hint(t(lang, "enroll_intro"), lang))
    await update.message.reply_text(with_hint(t(lang, "ask_name"), lang))
    context.chat_data["enroll_running"] = True
    return ASK_NAME

# Entry por botón del menú (callback) — SIEMPRE mensajes nuevos
async def enroll_entry_cb(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    await query.answer()
    lang = get_lang(context)

    logging.info("Enroll callback recibido user_id=%s chat_id=%s",
                 getattr(update.effective_user, "id", None),
                 getattr(update.effective_chat, "id", None))

    context.chat_data["enroll_running"] = True

    intro   = t(lang, "enroll_intro") or "📝 Por favor comparte tus datos para cursos."
    first_q = t(lang, "ask_name")     or "👤 Tu nombre completo:"
    await query.message.reply_text(with_hint(f"{intro}\n\n{first_q}", lang))
    return ASK_NAME

async def ask_phone(update: Update, context: ContextTypes.DEFAULT_TYPE):
    context.user_data["lead_name"] = (update.message.text or "").strip()
    lang = get_lang(context)
    await update.message.reply_text(with_hint(t(lang, "ask_phone"), lang))
    return ASK_PHONE

async def ask_email(update: Update, context: ContextTypes.DEFAULT_TYPE):
    phone = (update.message.text or "").strip()
    lang = get_lang(context)
    if not PHONE_RE.match(phone):
        await update.message.reply_text(with_hint("Formato de teléfono inválido. Ej.: +1 212 555 1234", lang))
        return ASK_PHONE
    context.user_data["lead_phone"] = phone
    await update.message.reply_text(with_hint(t(lang, "ask_email"), lang), parse_mode="Markdown")
    return ASK_EMAIL

async def ask_country(update: Update, context: ContextTypes.DEFAULT_TYPE):
    email = (update.message.text or "").strip()
    lang = get_lang(context)
    if email.lower() in {"omitir", "ninguno", "no tengo", "skip", "none", "no"} or email == "":
        context.user_data["lead_email"] = ""
    else:
        if not EMAIL_RE.match(email):
            await update.message.reply_text(with_hint("Correo inválido. Escribe uno válido o *omitir*.", lang), parse_mode="Markdown")
            return ASK_EMAIL
        context.user_data["lead_email"] = email
    await update.message.reply_text(with_hint(t(lang, "ask_country"), lang))
    return ASK_COUNTRY

async def ask_language(update: Update, context: ContextTypes.DEFAULT_TYPE):
    context.user_data["lead_country"] = (update.message.text or "").strip()
    lang = get_lang(context)
    await update.message.reply_text(with_hint(t(lang, "ask_lang"), lang))
    return ASK_LANG

async def ask_internet(update: Update, context: ContextTypes.DEFAULT_TYPE):
    context.user_data["lead_pref_lang"] = (update.message.text or "").strip()
    lang = get_lang(context)
    await update.message.reply_text(with_hint(t(lang, "ask_internet"), lang), reply_markup=internet_kb(lang))
    return ASK_INTERNET

# Botones: Sí/No
async def set_internet(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    await query.answer()
    has_net = "Sí" if query.data.endswith("yes") else "No"
    context.user_data["lead_internet"] = has_net
    lang = get_lang(context)
    await query.message.reply_text(with_hint(t(lang, "ask_status"), lang), reply_markup=status_kb(lang))
    return ASK_STATUS

# Texto: Sí/No
async def set_internet_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
    lang = get_lang(context)
    choice = parse_yes_no(update.message.text)
    if not choice:
        await update.message.reply_text(
            with_hint("Responde *Sí* o *No* (o usa los botones).", lang),
            reply_markup=internet_kb(lang), parse_mode="Markdown"
        )
        return ASK_INTERNET
    context.user_data["lead_internet"] = "Sí" if choice == "yes" else "No"
    await update.message.reply_text(with_hint(t(lang, "ask_status"), lang), reply_markup=status_kb(lang))
    return ASK_STATUS

# Botones: Estatus
async def set_status(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    await query.answer()
    code = query.data.replace("status_", "")
    mapping = {
        "pend_asy": t(get_lang(context), "status_pend_asy"),
        "asy": t(get_lang(context), "status_asy"),
        "tps": t(get_lang(context), "status_tps"),
        "cit": t(get_lang(context), "status_cit"),
        "other": t(get_lang(context), "status_other"),
    }
    context.user_data["lead_status"] = mapping.get(code, code)
    lang = get_lang(context)
    if code == "other":
        await query.message.reply_text(with_hint(t(lang, "ask_status_other"), lang))
        return ASK_STATUS_OTHER
    else:
        await query.message.reply_text(with_hint(t(lang, "ask_heard"), lang), reply_markup=heard_kb(lang))
        return ASK_HEARD

# Texto: Estatus
async def set_status_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
    lang = get_lang(context)
    code = match_status(update.message.text)
    if not code:
        msg = ("Por favor elige una opción o escribe: *Asilo pendiente*, *Asilado*, *TPS*, "
               "*Ciudadano* u *Otro*.")
    # show keyboard again
        await update.message.reply_text(with_hint(msg, lang), reply_markup=status_kb(lang), parse_mode="Markdown")
        return ASK_STATUS
    mapping = {
        "pend_asy": t(lang, "status_pend_asy"),
        "asy": t(lang, "status_asy"),
        "tps": t(lang, "status_tps"),
        "cit": t(lang, "status_cit"),
        "other": t(lang, "status_other"),
    }
    context.user_data["lead_status"] = mapping.get(code, code)
    if code == "other":
        await update.message.reply_text(with_hint(t(lang, "ask_status_other"), lang))
        return ASK_STATUS_OTHER
    else:
        await update.message.reply_text(with_hint(t(lang, "ask_heard"), lang), reply_markup=heard_kb(lang))
        return ASK_HEARD

async def ask_status_other_done(update: Update, context: ContextTypes.DEFAULT_TYPE):
    context.user_data["lead_status_other"] = (update.message.text or "").strip()
    lang = get_lang(context)
    await update.message.reply_text(with_hint(t(lang, "ask_heard"), lang), reply_markup=heard_kb(lang))
    return ASK_HEARD

# Heard por botones
async def set_heard_cb(update: Update, context: ContextTypes.DEFAULT_TYPE):
    query = update.callback_query
    await query.answer()
    code = query.data  # heard_friend, heard_web...
    lang = get_lang(context)

    labels = {
        "heard_friend": t(lang, "heard_friend"),
        "heard_web": t(lang, "heard_web"),
        "heard_press": t(lang, "heard_press"),
        "heard_radio": t(lang, "heard_radio"),
        "heard_tv": t(lang, "heard_tv"),
        "heard_other": t(lang, "heard_other"),
    }

    if code == "heard_other":
        await query.message.reply_text(with_hint(t(lang, "ask_heard_other"), lang))
        return ASK_HEARD_OTHER

    context.user_data["lead_heard"] = labels.get(code, code)
    # Finaliza
    await _finalize_enroll_via_message(query.message, context)
    return ConversationHandler.END

# Heard por texto
async def set_heard_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
    lang = get_lang(context)
    code = match_heard(update.message.text)
    if code is None:
        # Toma texto libre como "otro"
        context.user_data["lead_heard"] = (update.message.text or "").strip()
        return await _finalize_enroll_via_message(update.message, context)

    if code == "heard_other":
        await update.message.reply_text(with_hint(t(lang, "ask_heard_other"), lang))
        return ASK_HEARD_OTHER

    # Mapear a etiqueta
    labels = {
        "heard_friend": t(lang, "heard_friend"),
        "heard_web": t(lang, "heard_web"),
        "heard_press": t(lang, "heard_press"),
        "heard_radio": t(lang, "heard_radio"),
        "heard_tv": t(lang, "heard_tv"),
    }
    context.user_data["lead_heard"] = labels.get(code, code)
    return await _finalize_enroll_via_message(update.message, context)

async def heard_other_done(update: Update, context: ContextTypes.DEFAULT_TYPE):
    context.user_data["lead_heard"] = (update.message.text or "").strip()
    return await _finalize_enroll_via_message(update.message, context)

# Guardado y cierre común
def _save_lead(context: ContextTypes.DEFAULT_TYPE, chat, user):
    LEADS_FILE.parent.mkdir(parents=True, exist_ok=True)
    new = not LEADS_FILE.exists()
    data = {
        "timestamp": datetime.utcnow().isoformat(),
        "chat_id": chat.id if chat else "",
        "user_id": user.id if user else "",
        "username": getattr(user, "username", "") if user else "",
        "name": context.user_data.get("lead_name", ""),
        "phone": context.user_data.get("lead_phone", ""),
        "email": context.user_data.get("lead_email", ""),
        "country": context.user_data.get("lead_country", ""),
        "pref_lang": context.user_data.get("lead_pref_lang", ""),
        "internet": context.user_data.get("lead_internet", ""),
        "status": context.user_data.get("lead_status", ""),
        "status_other": context.user_data.get("lead_status_other", ""),
        "heard": context.user_data.get("lead_heard", "")
    }
    line = ",".join([f"\"{str(data[k]).replace('\"','\"\"')}\"" for k in data.keys()])
    header = ",".join(data.keys())
    with LEADS_FILE.open("a", encoding="utf-8", newline="") as f:
        if new:
            f.write(header + "\n")
        f.write(line + "\n")
    logging.info("Lead guardado en %s: %s", str(LEADS_FILE.resolve()), data)

def _build_summary(context: ContextTypes.DEFAULT_TYPE) -> str:
    return (
        "✅ Datos guardados:\n"
        f"• Nombre: {context.user_data.get('lead_name','')}\n"
        f"• Teléfono: {context.user_data.get('lead_phone','')}\n"
        f"• Email: {context.user_data.get('lead_email','')}\n"
        f"• País: {context.user_data.get('lead_country','')}\n"
        f"• Idioma: {context.user_data.get('lead_pref_lang','')}\n"
        f"• Internet: {context.user_data.get('lead_internet','')}\n"
        f"• Estatus: {context.user_data.get('lead_status','')}\n"
        f"• ¿Cómo supo?: {context.user_data.get('lead_heard','')}"
    )

async def _finalize_enroll_via_message(message, context: ContextTypes.DEFAULT_TYPE):
    _save_lead(context, message.chat, message.from_user)
    lang = get_lang(context)
    await message.reply_text(with_hint(_build_summary(context), lang))
    await message.reply_text(with_hint(t(lang, "enroll_thanks"), lang))
    context.chat_data["enroll_running"] = False
    return ConversationHandler.END

# ------------------------------- Respuesta libre --------------------------------
def t_address(lang: str) -> str:
    i18n = I18N.get(lang, {})
    return i18n.get("address", "80 Maiden Lane, 13th Floor, New York, NY 10028")

def t_programs(lang: str) -> str:
    i18n = I18N.get(lang, {})
    return i18n.get("programs_text", "")

def answer_from_faq(text: str, lang: str) -> str:
    i18n = I18N.get(lang, {})
    programs = t_programs(lang)
    addr = t_address(lang)
    map_label = i18n.get("open_map_label", "🗺️ Map")

    for m in MATCHERS[lang]:
        if m.pattern.search(text):
            ans = m.answer
            return (ans
                    .replace("{WEBSITE_URL}", CONFIG["website_url"])
                    .replace("{MAP_URL}", CONFIG["map_url"])
                    .replace("{MAP_LABEL}", map_label)
                    .replace("{ADDRESS}", addr)
                    .replace("{PROGRAMS}", programs))
    return t(lang, "no_match").replace("{WEBSITE_URL}", CONFIG["website_url"])

async def on_text(update: Update, context: ContextTypes.DEFAULT_TYPE):
    lang = get_lang(context)
    user_text = update.message.text or ""
    reply = answer_from_faq(user_text, lang)
    await update.message.reply_text(with_hint(reply, lang), disable_web_page_preview=True, parse_mode="Markdown")

# ------------------------------- Error handler ----------------------------
async def error_handler(update: object, context: ContextTypes.DEFAULT_TYPE):
    logging.exception("⚠️ Exception while handling an update:", exc_info=context.error)

# --------------------------------- Main ----------------------------------
def init_runtime():
    global CONFIG, I18N, LANGS, MATCHERS
    CONFIG = load_config()

    discovered = discover_languages()
    if not discovered:
        discovered = {
            "en": {"name": "English", "i18n": {
                "name": "English",
                "menu": {
                    "programs": "📚 Programs", "schedule": "🗓️ Schedule", "location": "📍 Location",
                    "faq": "❓ FAQ", "site": "🌐 Website", "language": "🌐 Language", "enroll": "📝 I’m interested"
                },
                "strings": {
                    "welcome": "Hi! I’m the *International Center* assistant.",
                    "help": "Type your question (e.g., *schedule*, *apply*, *location*). Use /menu or /lang.",
                    "lang_set": "✅ Language updated.",
                    "no_match": "I couldn’t find an exact answer 🤔. Check {WEBSITE_URL} or use /menu.",
                    "choose_language": "Choose language:",
                    "enroll_intro": "📝 Please share your details to sign up for courses.",
                    "ask_name": "👤 Your full name:",
                    "ask_phone": "📱 Your phone (with country code if possible):",
                    "ask_email": "📧 Your email (or type *skip*):",
                    "ask_country": "🌎 What country are you from?",
                    "ask_lang": "🗣️ What language do you speak mainly?",
                    "ask_internet": "🌐 Do you have a stable Internet connection?",
                    "btn_yes": "Yes",
                    "btn_no": "No",
                    "ask_status": "🏷️ What is your status in the country?",
                    "status_pend_asy": "Asylum pending",
                    "status_asy": "Asylee",
                    "status_tps": "TPS",
                    "status_cit": "Citizen",
                    "status_other": "Other (specify)",
                    "ask_status_other": "✍️ Please specify your status:",
                    "ask_heard": "📣 How did you hear about us?",
                    "heard_friend": "A friend",
                    "heard_web": "Website",
                    "heard_press": "Press",
                    "heard_radio": "Radio",
                    "heard_tv": "TV",
                    "heard_other": "Other (specify)",
                    "ask_heard_other": "✍️ Tell us how you heard about us:",
                    "enroll_thanks": "✅ Thank you! We’ll contact you soon."
                },
                "programs_text": "📚 Programs. {WEBSITE_URL}",
                "schedule_text": "🗓️ See schedules on: {WEBSITE_URL}",
                "address": "80 Maiden Lane, 13th Floor, New York, NY 10028",
                "open_map_label": "🗺️ Open map",
                "faq_hint": "Type keywords like: *schedule*, *apply*, *location*, *cost*."
            }}
        }

    I18N = {code: data["i18n"] for code, data in discovered.items()}
    LANGS = {code: data["name"] for code, data in discovered.items()}
    MATCHERS = {code: build_matchers(code) for code in LANGS.keys()}

if __name__ == "__main__":
    logging.basicConfig(format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", level=logging.INFO)
    if not TOKEN:
        raise SystemExit("Missing TELEGRAM_TOKEN in environment (.env)")

    init_runtime()

    app = ApplicationBuilder().token(TOKEN).build()

    # ---- Conversación de registro de interesados (PRIMERO) ----
    conv = ConversationHandler(
        entry_points=[
            CommandHandler("enroll", enroll_cmd),
            CallbackQueryHandler(enroll_entry_cb, pattern="^enroll$")  # botón del menú
        ],
        states={
            ASK_NAME:        [MessageHandler(filters.TEXT & ~filters.COMMAND, ask_phone)],
            ASK_PHONE:       [MessageHandler(filters.TEXT & ~filters.COMMAND, ask_email)],
            ASK_EMAIL:       [MessageHandler(filters.TEXT & ~filters.COMMAND, ask_country)],
            ASK_COUNTRY:     [MessageHandler(filters.TEXT & ~filters.COMMAND, ask_language)],
            ASK_LANG:        [MessageHandler(filters.TEXT & ~filters.COMMAND, ask_internet)],

            # Internet: botones + texto
            ASK_INTERNET: [
                CallbackQueryHandler(set_internet, pattern="^internet_(yes|no)$"),
                MessageHandler(filters.TEXT & ~filters.COMMAND, set_internet_text),
            ],

            # Estatus: botones + texto
            ASK_STATUS: [
                CallbackQueryHandler(set_status, pattern="^status_"),
                MessageHandler(filters.TEXT & ~filters.COMMAND, set_status_text),
            ],

            ASK_STATUS_OTHER: [MessageHandler(filters.TEXT & ~filters.COMMAND, ask_status_other_done)],

            # Heard: botones + texto
            ASK_HEARD: [
                CallbackQueryHandler(set_heard_cb, pattern="^heard_"),
                MessageHandler(filters.TEXT & ~filters.COMMAND, set_heard_text),
            ],
            ASK_HEARD_OTHER: [MessageHandler(filters.TEXT & ~filters.COMMAND, heard_other_done)],
        },
        fallbacks=[CommandHandler("cancel", lambda u, c: (c.chat_data.__setitem__("enroll_running", False),
                                                         u.message.reply_text(with_hint("Operación cancelada.", get_lang(c))))[1])],
        allow_reentry=True
    )
    app.add_handler(conv)

    # ---- Handlers básicos ----
    app.add_handler(CommandHandler("start", start))
    app.add_handler(CommandHandler("help", help_cmd))
    app.add_handler(CommandHandler("menu", menu_cmd))
    app.add_handler(CommandHandler("lang", lang_cmd))

    # ---- Callback genérico y texto libre ----
    app.add_handler(CallbackQueryHandler(on_callback))
    app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, on_text))

    # ---- Error handler ----
    app.add_error_handler(error_handler)

    logging.info("IC bot v4 is running (polling)...")
    app.run_polling(drop_pending_updates=True)
