# ruff: noqa: F401,F403,F405,I001
from ._runtime import *  # noqa: F403,F405

from typing import Literal, Optional

from pydantic import BaseModel, ConfigDict, constr, field_validator

from bot.services.support_service import TicketNotFound
from db.dal import support_dal, user_dal
from db.models import SupportTicket, SupportTicketMessage


class AdminTicketReplyPayload(BaseModel):
    model_config = ConfigDict(extra="ignore")

    body: constr(min_length=1, max_length=4000)
    is_internal_note: bool = False

    @field_validator("body")
    @classmethod
    def _strip_body(cls, value: str) -> str:
        stripped = value.strip()
        if not stripped:
            raise ValueError("empty_text")
        return stripped


class AdminTicketPatchPayload(BaseModel):
    model_config = ConfigDict(extra="ignore")

    status: Optional[Literal["open", "awaiting_user", "awaiting_admin", "resolved", "closed"]] = (
        None
    )
    priority: Optional[Literal["low", "normal", "high", "urgent"]] = None
    category: Optional[Literal["billing", "technical", "account", "other"]] = None
    assigned_admin_id: Optional[int] = None


def _validate_model_payload(model_cls, payload: Dict[str, Any]):
    try:
        return model_cls.model_validate(payload), None
    except ValidationError:
        return None, _error(400, "invalid_request", "Invalid request")


def _support_ticket_payload(ticket: SupportTicket) -> Dict[str, Any]:
    return {
        "ticket_id": ticket.ticket_id,
        "user_id": ticket.user_id,
        "subject": ticket.subject,
        "category": ticket.category,
        "priority": ticket.priority,
        "status": ticket.status,
        "assigned_admin_id": ticket.assigned_admin_id,
        "last_message_at": ticket.last_message_at.isoformat() if ticket.last_message_at else None,
        "last_message_role": ticket.last_message_role,
        "unread_user_count": int(ticket.unread_user_count or 0),
        "unread_admin_count": int(ticket.unread_admin_count or 0),
        "created_at": ticket.created_at.isoformat() if ticket.created_at else None,
        "updated_at": ticket.updated_at.isoformat() if ticket.updated_at else None,
        "closed_at": ticket.closed_at.isoformat() if ticket.closed_at else None,
    }


def _user_display_name(user) -> Optional[str]:
    if not user:
        return None
    name = " ".join(
        part.strip() for part in [user.first_name, user.last_name] if part and part.strip()
    ).strip()
    return name or user.username or user.email or str(user.user_id)


def _support_message_payload(
    message: SupportTicketMessage,
    *,
    authors: Optional[Dict[int, Any]] = None,
) -> Dict[str, Any]:
    author = authors.get(message.author_user_id) if authors and message.author_user_id else None
    return {
        "message_id": message.message_id,
        "ticket_id": message.ticket_id,
        "author_role": message.author_role,
        "author_user_id": message.author_user_id,
        "author_name": _user_display_name(author),
        "body": message.body,
        "is_internal_note": bool(message.is_internal_note),
        "created_at": message.created_at.isoformat() if message.created_at else None,
        "read_by_user_at": message.read_by_user_at.isoformat() if message.read_by_user_at else None,
        "read_by_admin_at": message.read_by_admin_at.isoformat()
        if message.read_by_admin_at
        else None,
    }


def _admin_support_user_payload(user) -> Dict[str, Any]:
    if not user:
        return {}
    return {
        "user_id": user.user_id,
        "telegram_id": user.telegram_id,
        "username": user.username,
        "first_name": user.first_name,
        "last_name": user.last_name,
        "email": user.email,
        "telegram_photo_url": user.telegram_photo_url,
        "is_banned": bool(user.is_banned),
        "registration_date": user.registration_date.isoformat() if user.registration_date else None,
    }


def _support_limit_offset(request: web.Request) -> tuple[int, int]:
    limit = max(1, min(100, int(request.query.get("limit", 25) or 25)))
    offset = max(0, int(request.query.get("offset", 0) or 0))
    return limit, offset


async def admin_support_tickets_route(request: web.Request) -> web.Response:
    _require_admin_user_id(request)
    limit, offset = _support_limit_offset(request)
    assigned_raw = request.query.get("assigned")
    assigned_admin_id = None
    if assigned_raw and assigned_raw not in {"all", "any"}:
        assigned_admin_id = int(assigned_raw)
    async_session_factory: sessionmaker = request.app["async_session_factory"]
    async with async_session_factory() as session:
        tickets = await support_dal.list_admin_tickets(
            session,
            status=request.query.get("status") or None,
            priority=request.query.get("priority") or None,
            category=request.query.get("category") or None,
            assigned_admin_id=assigned_admin_id,
            search=request.query.get("search") or None,
            sort=request.query.get("sort") or "updated_desc",
            limit=limit,
            offset=offset,
        )
    return web.json_response(
        {
            "ok": True,
            "tickets": [
                {
                    **_support_ticket_payload(ticket),
                    "user": _admin_support_user_payload(getattr(ticket, "user", None)),
                }
                for ticket in tickets
            ],
        }
    )


async def admin_support_ticket_detail_route(request: web.Request) -> web.Response:
    _require_admin_user_id(request)
    ticket_id = int(request.match_info["id"])
    async_session_factory: sessionmaker = request.app["async_session_factory"]
    service = request.app["support_service"]
    async with async_session_factory() as session:
        ticket, messages = await support_dal.get_ticket(session, ticket_id, include_internal=True)
        if not ticket:
            return _error(404, "not_found", "Ticket not found")
        user = await user_dal.get_user_by_id(session, ticket.user_id)
        snapshot = await service.build_user_snapshot(user, session=session) if user else {}
        author_ids = {m.author_user_id for m in messages if m.author_user_id is not None}
        authors = {}
        for author_id in author_ids:
            author = await user_dal.get_user_by_id(session, author_id)
            if author:
                authors[author_id] = author
    return web.json_response(
        {
            "ok": True,
            "ticket": {
                **_support_ticket_payload(ticket),
                "user": _admin_support_user_payload(user),
            },
            "messages": [_support_message_payload(m, authors=authors) for m in messages],
            "user_snapshot": snapshot,
        }
    )


async def admin_support_ticket_reply_route(request: web.Request) -> web.Response:
    admin_id = _require_admin_user_id(request)
    ticket_id = int(request.match_info["id"])
    payload, error = _validate_model_payload(AdminTicketReplyPayload, await _read_json(request))
    if error:
        return error
    try:
        ticket, message = await request.app["support_service"].reply_as_admin(
            admin_id,
            ticket_id,
            payload.body,
            is_internal_note=payload.is_internal_note,
        )
    except TicketNotFound:
        return _error(404, "not_found", "Ticket not found")
    async_session_factory: sessionmaker = request.app["async_session_factory"]
    async with async_session_factory() as session:
        admin = await user_dal.get_user_by_id(session, admin_id)
    return web.json_response(
        {
            "ok": True,
            "ticket": _support_ticket_payload(ticket),
            "message": _support_message_payload(
                message, authors={admin_id: admin} if admin else {}
            ),
        }
    )


async def admin_support_ticket_patch_route(request: web.Request) -> web.Response:
    admin_id = _require_admin_user_id(request)
    ticket_id = int(request.match_info["id"])
    payload, error = _validate_model_payload(AdminTicketPatchPayload, await _read_json(request))
    if error:
        return error
    updates = payload.model_dump(exclude_unset=True)
    try:
        if updates.get("status") == "closed":
            ticket = await request.app["support_service"].close_ticket(admin_id, ticket_id)
            updates.pop("status", None)
            if updates:
                ticket = await request.app["support_service"]._update_and_audit(
                    admin_id,
                    ticket_id,
                    **updates,
                )
        else:
            ticket = await request.app["support_service"]._update_and_audit(
                admin_id,
                ticket_id,
                **updates,
            )
    except TicketNotFound:
        return _error(404, "not_found", "Ticket not found")
    return web.json_response({"ok": True, "ticket": _support_ticket_payload(ticket)})


async def admin_support_ticket_read_route(request: web.Request) -> web.Response:
    _require_admin_user_id(request)
    ticket_id = int(request.match_info["id"])
    await request.app["support_service"].mark_read_as_admin(ticket_id)
    return web.json_response({"ok": True})


async def admin_support_stats_route(request: web.Request) -> web.Response:
    _require_admin_user_id(request)
    async_session_factory: sessionmaker = request.app["async_session_factory"]
    async with async_session_factory() as session:
        stats = await support_dal.admin_stats(session)
    return web.json_response({"ok": True, "stats": stats})
