from __future__ import annotations

import math
from dataclasses import dataclass, field
from datetime import datetime

from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession, async_sessionmaker

from app.db.base import utcnow
from app.db.models import SupportTicket

OPEN_SUPPORT_TICKET_STATUS = "OPEN"
ANSWERED_SUPPORT_TICKET_STATUS = "ANSWERED"
CLOSED_SUPPORT_TICKET_STATUS = "CLOSED"


@dataclass(slots=True)
class AdminSupportTicketListItem:
    id: int
    public_id: str
    telegram_id: int
    username: str | None
    display_name: str
    status: str
    created_at: datetime
    updated_at: datetime
    closed_at: datetime | None


@dataclass(slots=True)
class AdminSupportTicketsPage:
    items: list[AdminSupportTicketListItem] = field(default_factory=list)
    page: int = 1
    page_size: int = 8
    total_items: int = 0
    total_pages: int = 1


class SupportTicketService:
    def __init__(self, *, session_factory: async_sessionmaker[AsyncSession]) -> None:
        self._session_factory = session_factory

    async def create_ticket(
        self,
        *,
        public_id: str,
        telegram_id: int,
        username: str | None,
        display_name: str,
        user_message: str,
        support_chat_id: int,
        support_thread_id: int | None,
        support_message_id: int,
    ) -> SupportTicket:
        async with self._session_factory() as session:
            ticket = SupportTicket(
                public_id=public_id,
                telegram_id=telegram_id,
                username=username,
                display_name=display_name,
                user_message=user_message,
                support_chat_id=support_chat_id,
                support_thread_id=support_thread_id,
                support_message_id=support_message_id,
                status=OPEN_SUPPORT_TICKET_STATUS,
            )
            session.add(ticket)
            await session.commit()
            await session.refresh(ticket)
            return ticket

    async def get_ticket(self, *, ticket_id: int) -> SupportTicket | None:
        async with self._session_factory() as session:
            return await session.get(SupportTicket, ticket_id)

    async def get_ticket_by_support_message(
        self,
        *,
        support_chat_id: int,
        support_message_id: int,
    ) -> SupportTicket | None:
        async with self._session_factory() as session:
            return await session.scalar(
                select(SupportTicket).where(
                    SupportTicket.support_chat_id == support_chat_id,
                    SupportTicket.support_message_id == support_message_id,
                )
            )

    async def mark_answered(self, *, ticket_id: int) -> SupportTicket | None:
        async with self._session_factory() as session:
            ticket = await session.get(SupportTicket, ticket_id)
            if ticket is None:
                return None

            if ticket.status != CLOSED_SUPPORT_TICKET_STATUS:
                ticket.status = ANSWERED_SUPPORT_TICKET_STATUS

            ticket.updated_at = utcnow()
            await session.commit()
            await session.refresh(ticket)
            return ticket

    async def append_note(
        self,
        *,
        ticket_id: int,
        note_text: str,
        status: str | None = None,
    ) -> SupportTicket | None:
        cleaned_note = note_text.strip()
        if not cleaned_note:
            return None

        async with self._session_factory() as session:
            ticket = await session.get(SupportTicket, ticket_id)
            if ticket is None:
                return None

            if ticket.support_note:
                ticket.support_note = f"{ticket.support_note}\n\n{cleaned_note}"
            else:
                ticket.support_note = cleaned_note
            if status and ticket.status != CLOSED_SUPPORT_TICKET_STATUS:
                ticket.status = status
            ticket.updated_at = utcnow()
            await session.commit()
            await session.refresh(ticket)
            return ticket

    async def close_ticket(
        self,
        *,
        ticket_id: int,
        closed_by_telegram_id: int | None,
    ) -> SupportTicket | None:
        async with self._session_factory() as session:
            ticket = await session.get(SupportTicket, ticket_id)
            if ticket is None:
                return None

            ticket.status = CLOSED_SUPPORT_TICKET_STATUS
            ticket.closed_by_telegram_id = closed_by_telegram_id
            ticket.closed_at = utcnow()
            ticket.updated_at = utcnow()
            await session.commit()
            await session.refresh(ticket)
            return ticket

    async def get_open_tickets_count(self) -> int:
        async with self._session_factory() as session:
            total = await session.scalar(
                select(func.count(SupportTicket.id)).where(
                    SupportTicket.status != CLOSED_SUPPORT_TICKET_STATUS
                )
            )
        return int(total or 0)

    async def get_admin_tickets_page(
        self,
        *,
        page: int,
        page_size: int = 8,
    ) -> AdminSupportTicketsPage:
        resolved_page_size = max(1, min(page_size, 20))

        async with self._session_factory() as session:
            total_items = int(
                await session.scalar(select(func.count(SupportTicket.id))) or 0
            )
            total_pages = max(1, math.ceil(total_items / resolved_page_size)) if total_items else 1
            resolved_page = min(max(page, 1), total_pages)

            rows = list(
                (
                    await session.scalars(
                        select(SupportTicket)
                        .order_by(
                            SupportTicket.created_at.desc(),
                            SupportTicket.id.desc(),
                        )
                        .offset((resolved_page - 1) * resolved_page_size)
                        .limit(resolved_page_size)
                    )
                ).all()
            )

        return AdminSupportTicketsPage(
            items=[
                AdminSupportTicketListItem(
                    id=row.id,
                    public_id=row.public_id,
                    telegram_id=row.telegram_id,
                    username=row.username,
                    display_name=row.display_name,
                    status=row.status,
                    created_at=row.created_at,
                    updated_at=row.updated_at,
                    closed_at=row.closed_at,
                )
                for row in rows
            ],
            page=resolved_page,
            page_size=resolved_page_size,
            total_items=total_items,
            total_pages=total_pages,
        )
