import uuid
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func
from sqlalchemy.orm import selectinload
from tickets.orm import SupportTicket, TicketMessage
from tickets.models import TicketCreate, TicketUpdate, MessageCreate
from shared.exceptions import NotFoundError


async def create_ticket(db: AsyncSession, data: TicketCreate) -> SupportTicket:
    ticket = SupportTicket(**data.model_dump())
    db.add(ticket)
    await db.commit()
    return await _get_ticket(db, ticket.id, include_internal=True)


async def get_ticket(db: AsyncSession, ticket_id: uuid.UUID, include_internal: bool = True) -> SupportTicket:
    return await _get_ticket(db, ticket_id, include_internal)


async def list_tickets(
    db: AsyncSession,
    status: str | None, priority: str | None, customer_id: str | None,
    page: int, limit: int,
) -> tuple[list[SupportTicket], int]:
    limit  = min(100, max(1, limit))
    offset = (max(1, page) - 1) * limit

    q = select(SupportTicket).options(selectinload(SupportTicket.messages))
    if status:      q = q.where(SupportTicket.status == status)
    if priority:    q = q.where(SupportTicket.priority == priority)
    if customer_id: q = q.where(SupportTicket.customer_id == customer_id)

    total = (await db.execute(select(func.count()).select_from(q.subquery()))).scalar()
    rows  = (await db.execute(q.order_by(SupportTicket.created_at.desc()).limit(limit).offset(offset))).scalars().all()
    return rows, total


async def update_ticket(db: AsyncSession, ticket_id: uuid.UUID, data: TicketUpdate) -> SupportTicket:
    ticket = await _get_ticket(db, ticket_id)
    for field, value in data.model_dump(exclude_unset=True).items():
        setattr(ticket, field, value)
    await db.commit()
    return await _get_ticket(db, ticket_id)


async def add_message(db: AsyncSession, ticket_id: uuid.UUID, data: MessageCreate) -> SupportTicket:
    ticket = await _get_ticket(db, ticket_id)
    msg = TicketMessage(ticket_id=ticket_id, **data.model_dump())
    db.add(msg)

    # Auto-advance ticket status based on who replied (skip if already resolved/closed)
    if ticket.status not in ("resolved", "closed"):
        if data.sender_type == "staff" and not data.is_internal:
            ticket.status = "waiting_on_customer"
        elif data.sender_type == "customer":
            ticket.status = "waiting_on_staff"

    await db.commit()
    return await _get_ticket(db, ticket_id)


async def escalate_to_issue(db: AsyncSession, ticket_id: uuid.UUID, entry_id: uuid.UUID) -> SupportTicket:
    ticket = await _get_ticket(db, ticket_id)
    ticket.linked_entry_id = entry_id
    await db.commit()
    return await _get_ticket(db, ticket_id)


async def list_by_customer(db: AsyncSession, customer_id: str) -> list[SupportTicket]:
    q = select(SupportTicket).options(selectinload(SupportTicket.messages)).where(
        SupportTicket.customer_id == customer_id
    ).order_by(SupportTicket.created_at.desc())
    return (await db.execute(q)).scalars().all()


async def list_by_device(db: AsyncSession, device_id: str) -> list[SupportTicket]:
    q = select(SupportTicket).options(selectinload(SupportTicket.messages)).where(
        SupportTicket.device_id == device_id
    ).order_by(SupportTicket.created_at.desc())
    return (await db.execute(q)).scalars().all()


async def _get_ticket(db: AsyncSession, ticket_id: uuid.UUID, include_internal: bool = True) -> SupportTicket:
    q = select(SupportTicket).options(selectinload(SupportTicket.messages)).where(SupportTicket.id == ticket_id)
    result = (await db.execute(q)).scalar_one_or_none()
    if not result:
        raise NotFoundError("Ticket")
    if not include_internal:
        result.messages = [m for m in result.messages if not m.is_internal]
    return result
