from typing import Annotated, Any, Optional

from fastapi import APIRouter, Depends, HTTPException
from pydantic import BaseModel
from sqlalchemy.orm import Session

from app.core.database import get_db
from app.core.models import TelegramLog, TelegramLogStatus
from app.core.config import get_telegram_config
from app.core.security import get_current_user
from app.services import receipt_service
from app.services.telegram_service import (
    download_telegram_file,
    get_telegram_webhook_info,
    send_telegram_message,
    set_telegram_webhook,
)

router = APIRouter(prefix="/api/telegram", tags=["telegram"])
_TELEGRAM_CONTEXT_MESSAGE_LIMIT = 16
_TELEGRAM_CHAT_SESSIONS: dict[str, dict[str, Any]] = {}


class TelegramSendRequest(BaseModel):
    chat_id: str
    message: str


class TelegramWebhookUpdate(BaseModel):
    url: str


class ChatResponse(BaseModel):
    reply: str
    action: Optional[str] = None
    draft_id: Optional[int] = None
    customer_id: Optional[int] = None
    source: Optional[str] = None
    tool_name: Optional[str] = None
    result: Optional[dict[str, Any]] = None


def _raise_telegram_error(result: dict) -> None:
    status_code = int(result.get("status_code") or 400)
    detail = result.get("error") or "Telegram send failed"
    raise HTTPException(status_code=status_code, detail=detail)


def _chat_id_from_update(update: dict) -> str:
    message = update.get("message") or update.get("edited_message") or {}
    chat = message.get("chat") or {}
    chat_id = chat.get("id")
    return str(chat_id).strip() if chat_id is not None else ""


def _telegram_username(update: dict) -> str | None:
    message = update.get("message") or update.get("edited_message") or {}
    sender = message.get("from") or {}
    username = (sender.get("username") or "").strip()
    return username or None


def _is_authorized_chat(chat_id: str) -> bool:
    configured_chat_id = str(get_telegram_config().get("chat_id") or "").strip()
    return bool(configured_chat_id and chat_id == configured_chat_id)


def _receipt_file_id_from_update(update: dict) -> str | None:
    message = update.get("message") or update.get("edited_message") or {}
    photos = message.get("photo") or []
    if photos:
        largest = max(photos, key=lambda item: int(item.get("file_size") or 0))
        return largest.get("file_id")

    document = message.get("document") or {}
    mime_type = (document.get("mime_type") or "").lower()
    file_name = (document.get("file_name") or "").lower()
    if mime_type.startswith("image/") or mime_type == "application/pdf" or file_name.endswith(".pdf"):
        return document.get("file_id")
    return None


def _text_from_update(update: dict) -> str:
    message = update.get("message") or update.get("edited_message") or {}
    return str(message.get("text") or message.get("caption") or "").strip()


def _receipt_summary(expense_id: int, processing_status: str) -> str:
    return (
        f"已生成待处理支出草稿 #{expense_id}。\n"
        f"处理状态：{processing_status}\n\n"
        "这还没有进入财务报告或 GST。请到网页版票据收件箱最终确认。"
    )


def _get_chat_session(chat_id: str) -> dict[str, Any]:
    session = _TELEGRAM_CHAT_SESSIONS.get(chat_id)
    if session is None:
        session = {"active": True, "context": []}
        _TELEGRAM_CHAT_SESSIONS[chat_id] = session
    return session


def _reset_chat_session(chat_id: str) -> dict[str, Any]:
    session = {"active": True, "context": []}
    _TELEGRAM_CHAT_SESSIONS[chat_id] = session
    return session


def _stop_chat_session(chat_id: str) -> dict[str, Any]:
    session = {"active": False, "context": []}
    _TELEGRAM_CHAT_SESSIONS[chat_id] = session
    return session


def _append_chat_history(chat_id: str, user_message: str, assistant_reply: str) -> None:
    session = _get_chat_session(chat_id)
    context = session.setdefault("context", [])
    context.extend(
        [
            {"role": "user", "content": user_message},
            {"role": "assistant", "content": assistant_reply},
        ]
    )
    if len(context) > _TELEGRAM_CONTEXT_MESSAGE_LIMIT:
        session["context"] = context[-_TELEGRAM_CONTEXT_MESSAGE_LIMIT:]


async def handle_ai_chat_message(db: Session, message: str, context: list[dict] | None = None):
    from app.api.ai import handle_ai_chat_message as shared_ai_chat_handler

    return await shared_ai_chat_handler(db=db, message=message, context=context)


@router.post("/webhook")
async def telegram_webhook(update: dict, db: Session = Depends(get_db)):
    chat_id = _chat_id_from_update(update)
    if not _is_authorized_chat(chat_id):
        return {"ok": True, "ignored": True}

    file_id = _receipt_file_id_from_update(update)
    if not file_id:
        text = _text_from_update(update)
        if text:
            command = text.lower()
            if command == "/new":
                _reset_chat_session(chat_id)
                await send_telegram_message(
                    chat_id,
                    "Started a new AI chat with /new. Previous context was cleared.\nSend your next request any time. Use /stop to pause again.",
                )
                return {"ok": True, "handled": "new_chat"}

            if command == "/stop":
                _stop_chat_session(chat_id)
                await send_telegram_message(
                    chat_id,
                    "AI chat stopped for this Telegram conversation.\nSend /new when you want to start again.",
                )
                return {"ok": True, "handled": "stop_chat"}

            session = _get_chat_session(chat_id)
            if not session.get("active", True):
                await send_telegram_message(
                    chat_id,
                    "This AI chat is currently stopped.\nSend /new to start a fresh conversation.",
                )
                return {"ok": True, "handled": "chat_stopped"}

            chat_response = await handle_ai_chat_message(
                db=db,
                message=text,
                context=list(session.get("context") or []),
            )
            await send_telegram_message(chat_id, chat_response.reply)
            _append_chat_history(chat_id, text, chat_response.reply)
            return {"ok": True, "handled": "ai_chat", "action": chat_response.action}
        return {"ok": True, "ignored": True}

    await send_telegram_message(chat_id, "已收到收据，正在保存并分析，请稍等。")
    downloaded = await download_telegram_file(file_id)
    if not downloaded.get("success"):
        await send_telegram_message(chat_id, f"收据下载失败：{downloaded.get('error') or '未知错误'}")
        return {"ok": False, "error": downloaded.get("error")}

    result = await receipt_service.process_receipt_upload(
        db=db,
        filename=downloaded["filename"],
        content_type=downloaded["content_type"],
        file_bytes=downloaded["file_bytes"],
        upload_user=f"telegram:{_telegram_username(update) or chat_id}",
        source_channel="telegram",
    )
    await send_telegram_message(chat_id, _receipt_summary(result["expense_id"], result["processing_status"]))
    return {"ok": True, **result}


@router.post("/send")
async def send_message(
    req: TelegramSendRequest,
    current_user: Annotated[dict, Depends(get_current_user)],
    db: Session = Depends(get_db),
):
    """Send a Telegram message."""
    result = await send_telegram_message(req.chat_id, req.message)
    log = TelegramLog(
        chat_id=req.chat_id,
        message=req.message,
        status=TelegramLogStatus.sent if result["success"] else TelegramLogStatus.failed,
        error_message=result.get("error"),
    )
    db.add(log)
    db.commit()
    if not result["success"]:
        _raise_telegram_error(result)
    return {"success": True}


@router.post("/test")
async def test_telegram(
    current_user: Annotated[dict, Depends(get_current_user)],
    db: Session = Depends(get_db),
):
    """Send test message to configured chat_id."""
    from app.core.config import get_telegram_config

    config = get_telegram_config()
    chat_id = (config.get("chat_id") or "").strip()
    if not chat_id:
        raise HTTPException(status_code=400, detail="Telegram chat_id not configured")

    test_msg = (
        "ERP System Test Message\n"
        "------------------------\n"
        "Telegram bot is working."
    )
    result = await send_telegram_message(chat_id, test_msg)
    log = TelegramLog(
        chat_id=chat_id,
        message=test_msg,
        status=TelegramLogStatus.sent if result["success"] else TelegramLogStatus.failed,
        error_message=result.get("error"),
    )
    db.add(log)
    db.commit()
    if not result["success"]:
        _raise_telegram_error(result)
    return {"success": True, "message": "Test sent"}


@router.get("/webhook-info")
async def webhook_info(
    current_user: Annotated[dict, Depends(get_current_user)],
):
    """Get Telegram webhook status from Telegram API."""
    result = await get_telegram_webhook_info()
    if not result.get("success"):
        _raise_telegram_error(result)
    return result


@router.post("/webhook-url")
async def update_webhook_url(
    req: TelegramWebhookUpdate,
    current_user: Annotated[dict, Depends(get_current_user)],
):
    """Set Telegram webhook URL. Must be a public HTTPS URL."""
    result = await set_telegram_webhook(req.url)
    if not result.get("success"):
        _raise_telegram_error(result)
    return result


@router.get("/logs")
async def get_logs(
    current_user: Annotated[dict, Depends(get_current_user)],
    page: int = 1,
    page_size: int = 50,
    db: Session = Depends(get_db),
):
    """Get Telegram sending logs."""
    query = db.query(TelegramLog).order_by(TelegramLog.sent_at.desc())
    total = query.count()
    items = query.offset((page - 1) * page_size).limit(page_size).all()
    return {"items": items, "total": total, "page": page, "page_size": page_size}
