"""
Обработчик приветствий и основных сообщений бота.

Управляет жизненным циклом диалога:
  - приветствие → создание пользователя и диалога
  - маршрутизация по состояниям (state machine)
  - анализ намерений через YandexGPT
  - быстрые паттерны (заявка/приветствие) до NLP
  - контекст из последних 10 минут
"""
import logging
import datetime
from typing import Optional
from sqlalchemy import select
from app.database import async_session

MOSCOW_TZ = datetime.timezone(datetime.timedelta(hours=3))

def moscow_now():
    """Возвращает текущее время в UTC+3 (Москва) без tzinfo."""
    return datetime.datetime.now(MOSCOW_TZ).replace(tzinfo=None)
from app.models import BotUser, BotConversation, BotMessage, BotKnowledgeBase, BotCategory, BotUnknownQuestion
from app.max_api import max_api
from app.yandex_gpt import yandex_gpt

from app.config_reader import config_reader
from app.settings_cache import settings_cache
from app.keyboards import consent_keyboard, return_to_consent_keyboard

logger = logging.getLogger(__name__)


async def handle_greeting(
    user_id: int,
    text: Optional[str] = None,
    first_name: str = "",
    last_name: str = "",
    username: str = "",
) -> None:
    """Обработка приветствия: создаёт/обновляет пользователя, создаёт диалог, отправляет приветствие."""
    assistant_name = await settings_cache.get("assistant_name", "София")

    greeting_text = (
        f"Здравствуйте! Я {assistant_name}, помощник AegisOne Engineering. "
        f"Чем я могу быть вам полезна?"
    )

    async with async_session() as db:
        existing = await db.execute(
            select(BotUser).where(BotUser.id == user_id)
        )
        user = existing.scalar_one_or_none()

        now = moscow_now()
        if not user:
            try:
                user = BotUser(
                    id=user_id,
                    first_name=first_name,
                    last_name=last_name,
                    username=username,
                    last_interaction=now,
                    total_conversations=1,
                    created_at=now,
                )
                db.add(user)
                await db.commit()
                await db.refresh(user)
            except Exception:
                await db.rollback()
                existing = await db.execute(
                    select(BotUser).where(BotUser.id == user_id)
                )
                user = existing.scalar_one_or_none()
                if user:
                    if first_name:
                        user.first_name = first_name
                    if last_name:
                        user.last_name = last_name
                    if username:
                        user.username = username
                    user.last_interaction = now
                    user.total_conversations = (user.total_conversations or 0) + 1
                    await db.commit()
        else:
            if first_name:
                user.first_name = first_name
            if last_name:
                user.last_name = last_name
            if username:
                user.username = username
            user.last_interaction = now
            user.total_conversations = (user.total_conversations or 0) + 1
            await db.commit()

        conv = BotConversation(
            user_id=user_id,
            state="awaiting_input",
            created_at=now,
        )
        db.add(conv)
        await db.commit()
        await db.refresh(conv)

    await max_api.send_message(user_id, greeting_text, conversation_id=conv.id)


async def handle_message(user_id: int, text: str) -> None:
    """
    Основной обработчик сообщений. Маршрутизирует по состояниям:
    - greeting/analyzing/awaiting_input → analyze_and_respond
    - awaiting_consent/consent_refused → обработка согласия
    - awaiting_contact → ручной ввод контакта
    - awaiting_inquiry → обработка заявки
    - completed/escalated → повторный анализ (escape hatch)
    """
    async with async_session() as db:
        result = await db.execute(
            select(BotConversation)
            .where(BotConversation.user_id == user_id)
            .order_by(BotConversation.id.desc())
            .limit(1)
        )
        conv = result.scalar_one_or_none()

        if not conv:
            await handle_greeting(user_id, text)
            return

        now = moscow_now()
        stale_threshold = now - datetime.timedelta(minutes=10)

        if conv.created_at and conv.created_at < stale_threshold:
            conv = BotConversation(
                user_id=user_id,
                state="awaiting_input",
                created_at=now,
            )
            db.add(conv)
            await db.commit()
            await db.refresh(conv)

        msg = BotMessage(
            conversation_id=conv.id,
            direction="incoming",
            text=text,
            created_at=now,
        )
        db.add(msg)
        await db.commit()

    state = conv.state
    logger.info(f"handle_message: user={user_id} state={state} conv_id={conv.id}")

    text_lower = text.strip().lower()
    refuse_words = ["нет", "не даю", "не хочу", "отказ", "отмена", "no", "cancel", "я отказался"]
    operator_words = ["оператор", "живой человек", "связать с оператором", "переключить на оператора"]

    if any(w in text_lower for w in operator_words):
        from app.config_reader import config_reader
        phones = config_reader.get_phones()
        email = config_reader.get_site_mail()
        phone_text = "\n".join(f"📞 {phone}" for phone, _ in phones)
        await max_api.send_message(
            user_id,
            "К сожалению, я не могу переключить вас на оператора.\n\n"
            "Но вы можете связаться с нами напрямую:\n"
            f"{phone_text}\n"
            f"📧 {email}\n\n"
            "Мы обязательно ответим и поможем!",
            conversation_id=conv.id,
        )
        return

    revoke_words = ["отозвать согласие", "отозвать", "отменить согласие"]
    if any(w in text_lower for w in revoke_words):
        from app.handlers.consent import handle_revoke_consent
        await handle_revoke_consent(user_id, conv.id)
        return

    if state in ("greeting", "analyzing", "awaiting_input"):
        await analyze_and_respond(user_id, conv.id, text)
    elif state in ("awaiting_consent", "consent_refused", "awaiting_contact"):
        if any(w in text_lower for w in refuse_words):
            if state == "awaiting_consent":
                from app.handlers.consent import handle_consent_no
                await handle_consent_no(user_id, conv.id)
            elif state == "consent_refused":
                from app.handlers.consent import handle_refused_again
                await handle_refused_again(user_id, conv.id, text)
            elif state == "awaiting_contact":
                async with async_session() as db:
                    conv_upd = await db.get(BotConversation, conv.id)
                    if conv_upd:
                        conv_upd.state = "consent_refused"
                        await db.commit()
                from app.handlers.consent import handle_refused_again
                await handle_refused_again(user_id, conv.id, text)
            return

        quick_intent = await yandex_gpt.analyze_intent(text)
        if quick_intent == "question":
            await analyze_and_respond(user_id, conv.id, text)
            return
        if state == "awaiting_consent":
            from app.handlers.consent import handle_consent_response
            await handle_consent_response(user_id, conv.id, text)
        elif state == "consent_refused":
            from app.handlers.consent import handle_refused_again
            await handle_refused_again(user_id, conv.id, text)
        elif state == "awaiting_contact":
            from app.handlers.contact import handle_manual_contact
            await handle_manual_contact(user_id, conv.id, text)
    elif state == "awaiting_inquiry":
        from app.handlers.inquiry import handle_inquiry
        await handle_inquiry(user_id, conv.id, text)
    elif state in ("completed", "escalated"):
        await analyze_and_respond(user_id, conv.id, text)


async def _get_user_history_context(user_id: int) -> str:
    """Формирует контекст из последних сообщений (10 мин) и открытых заявок пользователя."""
    from app.models import BotTicket
    async with async_session() as db:
        ten_min_ago = moscow_now() - datetime.timedelta(minutes=10)
        recent_msgs = await db.execute(
            select(BotMessage)
            .join(BotConversation)
            .where(BotConversation.user_id == user_id)
            .where(BotMessage.created_at >= ten_min_ago)
            .order_by(BotMessage.id.desc())
        )
        messages = list(reversed(recent_msgs.scalars().all()))

        open_tickets = await db.execute(
            select(BotTicket)
            .where(BotTicket.user_id == user_id)
            .where(BotTicket.status != "Закрыта")
            .order_by(BotTicket.id.desc())
        )
        tickets = open_tickets.scalars().all()

    parts = []
    if messages:
        msg_lines = []
        for m in messages:
            who = "Клиент" if m.direction == "incoming" else "Бот"
            msg_lines.append(f"{who}: {m.text or ''}")
        parts.append("Последние сообщения из прошлых диалогов:\n" + "\n".join(msg_lines))

    if tickets:
        ticket_lines = []
        for t in tickets:
            ticket_lines.append(f"  - Заявка #{t.id}: {t.title} (статус: {t.status})")
        parts.append("Открытые заявки пользователя:\n" + "\n".join(ticket_lines))

    return "\n\n".join(parts)


async def _select_ai():
    """Выбор AI-провайдера (только YandexGPT)."""
    if await yandex_gpt.is_available():
        return yandex_gpt
    return None


async def analyze_and_respond(user_id: int, conv_id: int, text: str) -> None:
    """
    Анализ намерений и генерация ответа.

    Быстрые паттерны (заявка/приветствие) проверяются ПЕРЕД NLP.
    Затем: analyze_intent → ticket/question/unknown → обработка.
    clarify_intent вызывается не более 1 раза за диалог.
    """
    try:
        logger.info(f"analyze_and_respond: user={user_id} conv={conv_id} text={text[:80]}")

        text_lower = text.strip().lower()

        ticket_quick = [
            "записаться", "заявк", "заказать", "оставить заявку", "нужна помощь",
            "хочу заказать", "хочу записаться", "хочу оставить", "оформить заявку",
            "связаться", "обратный звонок", "перезвоните", "перезвонить",
        ]
        if any(p in text_lower for p in ticket_quick):
            async with async_session() as db:
                conv = await db.get(BotConversation, conv_id)
                if not conv:
                    return
                conv.intent = "ticket"
                conv.state = "awaiting_consent"
                await db.commit()
            consent_text = (
                "Для обработки Вашего запроса нам нужны ваши контактные данные "
                "для обратной связи. Даёте согласие на обработку "
                "персональных данных?"
            )
            await max_api.send_message(
                user_id, consent_text, attachments=consent_keyboard(), conversation_id=conv_id
            )
            return

        greeting_words = [
            "привет", "здравствуйте", "добрый день", "доброе утро", "добрый вечер",
            "хай", "хей", "йо", "здарова", "салют",
        ]
        words_only = set(text_lower.split())
        if words_only & set(greeting_words):
            async with async_session() as db:
                conv = await db.get(BotConversation, conv_id)
                if conv:
                    conv.intent = "greeting"
                    await db.commit()
            assistant_name = await settings_cache.get("assistant_name", "София")
            await max_api.send_message(
                user_id,
                f"Здравствуйте! Я {assistant_name}, чем могу помочь?",
                conversation_id=conv_id,
            )
            return

        ai = await _select_ai()
        if not ai:
            await _limited_mode(user_id, conv_id)
            return

        history_context = await _get_user_history_context(user_id)
        intent = await ai.analyze_intent(text, history_context)
        logger.info(f"analyze_and_respond: intent={intent}")

        async with async_session() as db:
            result = await db.execute(
                select(BotConversation).where(BotConversation.id == conv_id)
            )
            conv = result.scalar_one_or_none()
            if not conv:
                return

            prev_intent = conv.intent
            conv.intent = intent

            if intent == "question":
                kb_context = await _get_knowledge_base_context(text)
                answer = await ai.generate_answer(text, kb_context, history_context)
                if answer:
                    await max_api.send_message(user_id, answer, conversation_id=conv_id)
                else:
                    await _save_unknown_question(user_id, text)
                    await max_api.send_message(user_id,
                        "По данному вопросу рекомендую связаться с нашим специалистом "
                        "для получения исчерпывающего ответа. "
                        "Контакты: +7 (861) 203-33-30, mail@aegisone.ru",
                        conversation_id=conv_id,
                    )
                conv.state = "completed"

            elif intent == "ticket":
                conv.state = "awaiting_consent"
                consent_text = (
                    "Для обработки Вашего запроса нам нужны ваши контактные данные "
                    "для обратной связи. Даёте согласие на обработку "
                    "персональных данных?"
                )
                await max_api.send_message(
                    user_id, consent_text, attachments=consent_keyboard(), conversation_id=conv_id
                )

            else:
                if prev_intent == "unknown":
                    kb_context = await _get_knowledge_base_context(text)
                    answer = await ai.generate_answer(text, kb_context, history_context)
                    if answer:
                        await max_api.send_message(user_id, answer, conversation_id=conv_id)
                        conv.state = "completed"
                    else:
                        await _send_contacts(user_id, conv_id)
                        conv.state = "completed"
                else:
                    clarification = await ai.clarify_intent(text, history_context)
                    await max_api.send_message(user_id, clarification, conversation_id=conv_id)
                    conv.state = "awaiting_input"

            await db.commit()

    except Exception as e:
        logger.exception(f"analyze_and_respond FAILED: {e}")
        await _send_contacts(user_id, conv_id)


async def _limited_mode(user_id: int, conv_id: int) -> None:
    """Режим без AI: показывает контакты компании."""
    async with async_session() as db:
        result = await db.execute(
            select(BotConversation).where(BotConversation.id == conv_id)
        )
        conv = result.scalar_one_or_none()
        if conv:
            conv.state = "completed"
            await db.commit()

    await _send_contacts(user_id, conv_id)


async def _send_contacts(user_id: int, conv_id: int = None) -> None:
    """Отправка контактных данных компании (телефон, email)."""
    phones = config_reader.get_phones()
    email = config_reader.get_site_mail()

    phone_text = "\n".join(f"📞 {phone}" for phone, _ in phones)

    message = (
        "Вы можете связаться с нами:\n\n"
        f"{phone_text}\n"
        f"📧 {email}"
    )

    await max_api.send_message(user_id, message, conversation_id=conv_id)


async def _get_knowledge_base_context(query: str) -> str:
    """Формирует контекст из базы знаний по ключевым словам (ранжирование по score)."""
    async with async_session() as db:
        result = await db.execute(
            select(BotKnowledgeBase)
            .where(BotKnowledgeBase.is_active == True)
            .order_by(BotKnowledgeBase.sort_order)
        )
        cards = result.scalars().all()

    if not cards:
        return ""

    query_lower = query.lower()
    query_words = set(query_lower.split())

    matched = []
    unmatched = []
    for card in cards:
        keywords = card.keywords or []
        question_lower = (card.question or "").lower()
        keywords_lower = " ".join(keywords).lower()

        score = 0
        for w in query_words:
            if len(w) < 3:
                continue
            if w in keywords_lower:
                score += 2
            if w in question_lower:
                score += 1

        entry = (
            f"[{card.category.name if card.category else 'Общее'}] "
            f"{card.question}: {card.answer or 'Нет ответа'}"
        )
        if score > 0:
            matched.append((score, entry))
        else:
            unmatched.append(entry)

    matched.sort(key=lambda x: -x[0])
    parts = [e for _, e in matched] + unmatched

    return "\n\n".join(parts[:25])


def _texts_match(text_a: str, text_b: str) -> bool:
    """Проверка схожести двух текстов по пересечению слов (≥50%)."""
    stopwords = {"какой", "какая", "какие", "какое", "ваш", "ваша", "ваши",
                 "где", "когда", "сколько", "что", "это", "или", "для", "при",
                 "подскажите", "расскажите", "объясните", "пожалуйста"}
    words_a = set(text_a.lower().split()) - stopwords
    words_b = set(text_b.lower().split()) - stopwords
    if not words_a or not words_b:
        return False
    overlap = len(words_a & words_b) / min(len(words_a), len(words_b))
    return overlap >= 0.5


async def _auto_generate_and_add_kb(db, question_record):
    """Автогенерация карточки KB через YandexGPT при 3+ обращениях."""
    card_data = await yandex_gpt.generate_kb_card([question_record.question_text])
    if not card_data:
        return

    card = BotKnowledgeBase(
        category_id=card_data.get("category_id", 1),
        question=card_data.get("question", question_record.question_text),
        answer=card_data.get("answer"),
        keywords=card_data.get("keywords", []),
        is_active=False,
        sort_order=999,
    )
    db.add(card)

    question_record.status = "auto_added"
    question_record.generated_answer = card_data.get("answer")
    question_record.generated_keywords = card_data.get("keywords")
    question_record.generated_category_id = card_data.get("category_id")


async def _save_unknown_question(user_id: int, question_text: str) -> None:
    """Сохранение неизвестного вопроса с дедупликацией и автогенерацией при 3+ обращениях."""
    normalized_input = question_text.lower().strip()

    async with async_session() as db:
        result = await db.execute(
            select(BotUnknownQuestion)
            .where(BotUnknownQuestion.status.in_(["new", "auto_added"]))
            .order_by(BotUnknownQuestion.id.desc())
        )
        all_questions = result.scalars().all()

        matched = None
        for q in all_questions:
            compare_text = q.normalized_text or q.question_text
            if _texts_match(normalized_input, compare_text):
                matched = q
                break

        if matched:
            matched.ask_count += 1
            if user_id not in (matched.user_ids or []):
                matched.user_ids = (matched.user_ids or []) + [user_id]
            if matched.ask_count >= 3 and matched.status == "new":
                await _auto_generate_and_add_kb(db, matched)
        else:
            db.add(BotUnknownQuestion(
                question_text=question_text,
                normalized_text=normalized_input,
                ask_count=1,
                user_ids=[user_id],
                status="new",
            ))
        await db.commit()
