"""Telegram bot management API routes."""

import logging

from fastapi import APIRouter, Depends, HTTPException, Query, status
from pydantic import BaseModel
from sqlmodel import select
from sqlmodel.ext.asyncio.session import AsyncSession

from notify_bridge_core.notifications.telegram.client import TelegramClient

from ..auth.dependencies import get_current_user
from ..commands.handler import register_commands_with_telegram, sync_chat_command_binding
from ..commands.webhook import register_webhook, unregister_webhook
from ..database.engine import get_session
from ..database.models import AppSetting, NotificationTarget, TargetReceiver, TelegramBot, TelegramChat, User
from ..services.notifier import _get_test_message, resolve_telegram_chat_locale
from ..services.telegram_poller import schedule_bot_polling, unschedule_bot_polling
from .app_settings import get_setting
from .helpers import get_owned_entity

_LOGGER = logging.getLogger(__name__)

router = APIRouter(prefix="/api/telegram-bots", tags=["telegram-bots"])


class BotCreate(BaseModel):
    name: str
    token: str


class BotUpdate(BaseModel):
    name: str | None = None
    icon: str | None = None
    update_mode: str | None = None


@router.get("")
async def list_bots(
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """List all registered Telegram bots."""
    result = await session.exec(
        select(TelegramBot).where(TelegramBot.user_id == user.id)
    )
    return [_bot_response(b) for b in result.all()]


@router.post("", status_code=status.HTTP_201_CREATED)
async def create_bot(
    body: BotCreate,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Register a new Telegram bot (validates token via getMe)."""
    bot_info = await _get_me(body.token)
    if not bot_info:
        raise HTTPException(status_code=400, detail="Invalid bot token")

    bot = TelegramBot(
        user_id=user.id,
        name=body.name,
        token=body.token,
        bot_username=bot_info.get("username", ""),
        bot_id=bot_info.get("id", 0),
    )
    session.add(bot)
    await session.commit()
    await session.refresh(bot)
    return _bot_response(bot)


@router.put("/{bot_id}")
async def update_bot(
    bot_id: int,
    body: BotUpdate,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Update a bot's display name, icon, commands config, and update mode."""
    bot = await _get_user_bot(session, bot_id, user.id)
    if body.name is not None:
        bot.name = body.name
    if body.icon is not None:
        bot.icon = body.icon
    # Handle mode switching
    if body.update_mode is not None and body.update_mode != bot.update_mode:
        if body.update_mode not in ("none", "polling", "webhook"):
            raise HTTPException(
                status_code=400,
                detail=f"Invalid update_mode: {body.update_mode!r}. Must be 'none', 'polling', or 'webhook'.",
            )
        if body.update_mode == "webhook":
            # Validate and register webhook BEFORE stopping polling
            base_url = await get_setting(session, "external_url")
            if not base_url:
                raise HTTPException(
                    status_code=400,
                    detail="Cannot switch to webhook: external domain URL not configured. Set it in Settings.",
                )
            webhook_url = f"{base_url.rstrip('/')}/api/telegram/webhook/{bot.webhook_path_id}"
            secret = await get_setting(session, "telegram_webhook_secret")
            result = await register_webhook(bot.token, webhook_url, secret or None)
            if not result.get("success"):
                raise HTTPException(
                    status_code=400,
                    detail=f"Webhook registration failed: {result.get('error', 'unknown')}",
                )
            # Webhook registered successfully — now stop polling
            unschedule_bot_polling(bot.id)
        elif body.update_mode == "polling":
            # Switching to polling: unregister webhook, start polling
            await unregister_webhook(bot.token)
            schedule_bot_polling(bot.id)
        elif body.update_mode == "none":
            # Disable listener: stop polling and clear any webhook so Telegram
            # stops delivering updates. This makes the bot send-only, which is
            # safe when another instance owns the listener.
            unschedule_bot_polling(bot.id)
            await unregister_webhook(bot.token)
        bot.update_mode = body.update_mode

    session.add(bot)
    await session.commit()
    await session.refresh(bot)
    return _bot_response(bot)


@router.delete("/{bot_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_bot(
    bot_id: int,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Delete a registered bot and its chats."""
    from .delete_protection import check_telegram_bot, raise_if_used
    bot = await _get_user_bot(session, bot_id, user.id)
    raise_if_used(await check_telegram_bot(session, bot.id), bot.name)
    # Delete associated chats
    result = await session.exec(select(TelegramChat).where(TelegramChat.bot_id == bot_id))
    for chat in result.all():
        await session.delete(chat)
    await session.delete(bot)
    await session.commit()


@router.get("/{bot_id}/token")
async def get_bot_token(
    bot_id: int,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Get the full bot token (used by frontend to construct target config)."""
    bot = await _get_user_bot(session, bot_id, user.id)
    return {"token": bot.token}


# --- Chat management ---

@router.get("/{bot_id}/chats")
async def list_bot_chats(
    bot_id: int,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """List persisted chats for a bot."""
    await _get_user_bot(session, bot_id, user.id)  # Auth check
    result = await session.exec(
        select(TelegramChat).where(TelegramChat.bot_id == bot_id)
    )
    return [_chat_response(c) for c in result.all()]


@router.post("/{bot_id}/chats/discover")
async def discover_chats(
    bot_id: int,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Discover new chats via Telegram getUpdates and persist them.

    Merges newly discovered chats with existing ones (no duplicates).
    Returns the full updated chat list.
    """
    bot = await _get_user_bot(session, bot_id, user.id)
    discovered = await _fetch_chats_from_telegram(bot.token)

    # Load existing chats to avoid duplicates
    result = await session.exec(
        select(TelegramChat).where(TelegramChat.bot_id == bot_id)
    )
    existing = {c.chat_id: c for c in result.all()}

    new_count = 0
    for chat_data in discovered:
        cid = str(chat_data["id"])
        if cid in existing:
            # Update title/username if changed
            existing_chat = existing[cid]
            existing_chat.title = chat_data.get("title", existing_chat.title)
            existing_chat.username = chat_data.get("username", existing_chat.username)
            session.add(existing_chat)
        else:
            new_chat = TelegramChat(
                bot_id=bot_id,
                chat_id=cid,
                title=chat_data.get("title", ""),
                chat_type=chat_data.get("type", "private"),
                username=chat_data.get("username", ""),
            )
            session.add(new_chat)
            new_count += 1

    await session.commit()

    # Return full list
    result = await session.exec(
        select(TelegramChat).where(TelegramChat.bot_id == bot_id)
    )
    return [_chat_response(c) for c in result.all()]


@router.post("/{bot_id}/sync-commands")
async def sync_commands(
    bot_id: int,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Register enabled commands with Telegram BotFather API."""
    bot = await _get_user_bot(session, bot_id, user.id)
    success = await register_commands_with_telegram(bot)
    return {"success": success}


@router.post("/{bot_id}/webhook/register")
async def register_bot_webhook(
    bot_id: int,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Register Telegram webhook for this bot."""
    bot = await _get_user_bot(session, bot_id, user.id)
    base_url = await get_setting(session, "external_url")
    if not base_url:
        return {"success": False, "error": "External domain URL not configured. Set it in Telegram Settings."}
    webhook_url = f"{base_url.rstrip('/')}/api/telegram/webhook/{bot.webhook_path_id}"
    secret = await get_setting(session, "telegram_webhook_secret")
    result = await register_webhook(bot.token, webhook_url, secret or None)
    if not result.get("success"):
        return result
    # Verify with getWebhookInfo
    info = await _get_webhook_info(bot.token)
    if info and info.get("url") == webhook_url:
        result["verified"] = True
        result["webhook_url"] = webhook_url
    else:
        result["verified"] = False
        result["warning"] = "Webhook set but verification failed"
    return result


@router.post("/{bot_id}/webhook/unregister")
async def unregister_bot_webhook(
    bot_id: int,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Unregister Telegram webhook for this bot."""
    bot = await _get_user_bot(session, bot_id, user.id)
    result = await unregister_webhook(bot.token)
    return result


@router.get("/{bot_id}/webhook/status")
async def get_webhook_status(
    bot_id: int,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Get current webhook status from Telegram."""
    bot = await _get_user_bot(session, bot_id, user.id)
    info = await _get_webhook_info(bot.token)
    return {
        "url": info.get("url", "") if info else "",
        "has_custom_certificate": info.get("has_custom_certificate", False) if info else False,
        "pending_update_count": info.get("pending_update_count", 0) if info else 0,
        "last_error_message": info.get("last_error_message", "") if info else "",
        "last_error_date": info.get("last_error_date", 0) if info else 0,
    }


@router.post("/{bot_id}/chats/{chat_id}/test")
async def test_chat(
    bot_id: int,
    chat_id: str,
    locale: str = Query("en"),
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Send a test message to a chat via the bot.

    Locale resolution is delegated to ``resolve_telegram_chat_locale`` so this
    endpoint, the per-receiver fan-out, and the target receiver test all
    apply the same priority order (override → language_code → fallback).
    """
    bot = await _get_user_bot(session, bot_id, user.id)
    effective_locale = await resolve_telegram_chat_locale(
        session, bot_id=bot_id, chat_id=chat_id, fallback=locale,
    )
    from ..services.http_session import get_http_session
    message = _get_test_message(effective_locale, "telegram")
    http = await get_http_session()
    client = TelegramClient(http, bot.token)
    return await client.send_message(chat_id, message)


class ChatUpdate(BaseModel):
    language_override: str | None = None
    title: str | None = None
    commands_enabled: bool | None = None


@router.put("/{bot_id}/chats/{chat_db_id}")
async def update_chat(
    bot_id: int,
    chat_db_id: int,
    body: ChatUpdate,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Update a chat's language_code or title."""
    await _get_user_bot(session, bot_id, user.id)
    chat = await session.get(TelegramChat, chat_db_id)
    if not chat or chat.bot_id != bot_id:
        raise HTTPException(status_code=404, detail="Chat not found")
    updates = body.model_dump(exclude_unset=True)
    # Track whether anything changed that affects the chat-scoped command
    # binding registered with Telegram (so the per-chat language_override
    # actually takes effect on the bot's command list, not just the reply
    # locale). We push it inline rather than via the debounced auto-sync
    # so the user sees the change reflected on Telegram immediately —
    # Telegram clients still cache the menu until the next "/" or chat
    # re-open, but the source of truth is correct from the moment save
    # returns.
    sync_relevant_keys = {"language_override", "commands_enabled"}
    needs_sync = any(
        key in updates and getattr(chat, key) != value
        for key, value in updates.items()
        if key in sync_relevant_keys
    )
    for key, value in updates.items():
        setattr(chat, key, value)
    session.add(chat)
    await session.commit()
    await session.refresh(chat)
    if needs_sync:
        bot = await session.get(TelegramBot, bot_id)
        if bot is not None:
            try:
                await sync_chat_command_binding(bot, chat)
            except Exception:
                # Telegram-side failure shouldn't block the save — the
                # debounced bot-wide sync will retry on the next change.
                _LOGGER.warning(
                    "Inline command sync failed for bot=%d chat=%s",
                    bot_id, chat.chat_id, exc_info=True,
                )
    return _chat_response(chat)


@router.delete("/{bot_id}/chats/{chat_db_id}", status_code=status.HTTP_204_NO_CONTENT)
async def delete_chat(
    bot_id: int,
    chat_db_id: int,
    user: User = Depends(get_current_user),
    session: AsyncSession = Depends(get_session),
):
    """Delete a persisted chat entry."""
    await _get_user_bot(session, bot_id, user.id)  # Auth check
    chat = await session.get(TelegramChat, chat_db_id)
    if not chat or chat.bot_id != bot_id:
        raise HTTPException(status_code=404, detail="Chat not found")
    await session.delete(chat)
    await session.commit()


# --- Helpers ---

async def _get_webhook_info(token: str) -> dict | None:
    """Call Telegram getWebhookInfo via TelegramClient."""
    from ..services.http_session import get_http_session
    http = await get_http_session()
    client = TelegramClient(http, token)
    result = await client.get_webhook_info()
    return result.get("result") if result.get("success") else None


async def _get_me(token: str) -> dict | None:
    """Call Telegram getMe via TelegramClient."""
    from ..services.http_session import get_http_session
    http = await get_http_session()
    client = TelegramClient(http, token)
    result = await client.get_me()
    return result.get("result") if result.get("success") else None


async def _fetch_chats_from_telegram(token: str) -> list[dict]:
    """Fetch chats from Telegram getUpdates via TelegramClient."""
    from ..services.http_session import get_http_session
    http = await get_http_session()
    client = TelegramClient(http, token)
    result = await client.get_updates(limit=100)
    if not result.get("success"):
        return []

    seen: dict[int, dict] = {}
    for update in result.get("result", []):
        msg = update.get("message", {})
        chat = msg.get("chat", {})
        chat_id = chat.get("id")
        if chat_id and chat_id not in seen:
            seen[chat_id] = {
                "id": chat_id,
                "title": chat.get("title") or (chat.get("first_name", "") + (" " + chat.get("last_name", "")).strip()),
                "type": chat.get("type", "private"),
                "username": chat.get("username", ""),
            }
    return list(seen.values())


def _chat_response(c: TelegramChat) -> dict:
    return {
        "id": c.id,
        "chat_id": c.chat_id,
        "title": c.title,
        "type": c.chat_type,
        "username": c.username,
        "language_code": getattr(c, 'language_code', '') or '',
        "language_override": getattr(c, 'language_override', '') or '',
        "commands_enabled": getattr(c, 'commands_enabled', False),
        "discovered_at": c.discovered_at.isoformat(),
    }


def _bot_response(b: TelegramBot) -> dict:
    return {
        "id": b.id,
        "name": b.name,
        "icon": b.icon,
        "bot_username": b.bot_username,
        "bot_id": b.bot_id,
        "webhook_path_id": b.webhook_path_id,
        "update_mode": b.update_mode or "none",
        "token_preview": f"{b.token[:8]}...{b.token[-4:]}" if len(b.token) > 12 else "***",
        "created_at": b.created_at.isoformat(),
    }


async def _get_user_bot(session: AsyncSession, bot_id: int, user_id: int) -> TelegramBot:
    return await get_owned_entity(
        session, TelegramBot, bot_id, user_id, not_found_msg="Bot not found",
    )



# Re-export for backward compatibility
from ..services.telegram import save_chat_from_webhook  # noqa: F401
