from __future__ import annotations

import math

from fastapi import APIRouter, Depends, HTTPException, Query, status
from asyncpg import Connection

from app.auth import require_auth
from app.dependencies import PaginationParams, get_db
from app.models.cliente import ClienteCreate, ClienteUpdate, ClienteResponse
from app.models.common import PaginatedResponse, Meta, ErrorResponse, OkResponse

router = APIRouter(prefix="/clientes", tags=["Clientes"])


@router.get(
    "",
    response_model=PaginatedResponse[ClienteResponse],
    summary="List clients with pagination and search",
)
async def list_clientes(
    db: Connection = Depends(get_db),
    auth: dict = Depends(require_auth),
    pagination: PaginationParams = Depends(PaginationParams),
    q: str | None = Query(None, description="Search by nombre, empresa, or email"),
):
    where_clauses: list[str] = []
    params: list = []
    idx = 1

    if q:
        where_clauses.append(
            f"(nombre ILIKE ${idx} OR empresa ILIKE ${idx} OR email ILIKE ${idx})"
        )
        params.append(f"%{q}%")
        idx += 1

    where_sql = f"WHERE {' AND '.join(where_clauses)}" if where_clauses else ""

    count_row = await db.fetchrow(
        f"SELECT COUNT(*) AS total FROM \"Cliente\" {where_sql}", *params
    )
    total = count_row["total"]
    pages = math.ceil(total / pagination.limit) if total > 0 else 0

    rows = await db.fetch(
        f'SELECT * FROM "Cliente" {where_sql} ORDER BY "createdAt" DESC '
        f"LIMIT ${idx} OFFSET ${idx + 1}",
        *params,
        pagination.limit,
        pagination.offset,
    )

    data = [
        ClienteResponse(
            id=r["id"],
            nombre=r["nombre"],
            empresa=r["empresa"],
            email=r["email"],
            telefono=r["telefono"],
            createdAt=r["createdAt"],
            updatedAt=r["updatedAt"],
        )
        for r in rows
    ]

    return PaginatedResponse(
        data=data,
        meta=Meta(total=total, page=pagination.page, limit=pagination.limit, pages=pages),
    )


@router.post(
    "",
    response_model=ClienteResponse,
    status_code=status.HTTP_201_CREATED,
    summary="Create a new client",
)
async def create_cliente(
    body: ClienteCreate,
    db: Connection = Depends(get_db),
    auth: dict = Depends(require_auth),
):
    row = await db.fetchrow(
        'INSERT INTO "Cliente" (nombre, empresa, email, telefono) '
        "VALUES ($1, $2, $3, $4) RETURNING *",
        body.nombre,
        body.empresa,
        body.email,
        body.telefono,
    )
    return ClienteResponse(
        id=row["id"],
        nombre=row["nombre"],
        empresa=row["empresa"],
        email=row["email"],
        telefono=row["telefono"],
        createdAt=row["createdAt"],
        updatedAt=row["updatedAt"],
    )


@router.get(
    "/{cliente_id}",
    response_model=ClienteResponse,
    responses={404: {"model": ErrorResponse}},
    summary="Get client by ID with recent cotizaciones",
)
async def get_cliente(
    cliente_id: str,
    db: Connection = Depends(get_db),
    auth: dict = Depends(require_auth),
):
    row = await db.fetchrow('SELECT * FROM "Cliente" WHERE id = $1', cliente_id)
    if not row:
        raise HTTPException(status_code=404, detail="Cliente no encontrado")

    cotizaciones = await db.fetch(
        'SELECT * FROM "Cotizacion" WHERE "clienteId" = $1 '
        'ORDER BY "createdAt" DESC LIMIT 10',
        cliente_id,
    )

    cliente = ClienteResponse(
        id=row["id"],
        nombre=row["nombre"],
        empresa=row["empresa"],
        email=row["email"],
        telefono=row["telefono"],
        createdAt=row["createdAt"],
        updatedAt=row["updatedAt"],
    )

    return cliente.model_copy(
        update={"cotizaciones": [dict(c) for c in cotizaciones]}
    )


@router.put(
    "/{cliente_id}",
    response_model=ClienteResponse,
    responses={404: {"model": ErrorResponse}},
    summary="Update a client",
)
async def update_cliente(
    cliente_id: str,
    body: ClienteUpdate,
    db: Connection = Depends(get_db),
    auth: dict = Depends(require_auth),
):
    existing = await db.fetchrow('SELECT id FROM "Cliente" WHERE id = $1', cliente_id)
    if not existing:
        raise HTTPException(status_code=404, detail="Cliente no encontrado")

    updates: list[str] = []
    params: list = []
    idx = 1

    for field in ("nombre", "empresa", "email", "telefono"):
        value = getattr(body, field, None)
        if value is not None:
            updates.append(f"{field} = ${idx}")
            params.append(value)
            idx += 1

    if not updates:
        raise HTTPException(status_code=400, detail="No fields to update")

    params.append(cliente_id)
    row = await db.fetchrow(
        f'UPDATE "Cliente" SET {", ".join(updates)} WHERE id = ${idx} RETURNING *',
        *params,
    )

    return ClienteResponse(
        id=row["id"],
        nombre=row["nombre"],
        empresa=row["empresa"],
        email=row["email"],
        telefono=row["telefono"],
        createdAt=row["createdAt"],
        updatedAt=row["updatedAt"],
    )


@router.delete(
    "/{cliente_id}",
    response_model=OkResponse,
    responses={409: {"model": ErrorResponse}, 404: {"model": ErrorResponse}},
    summary="Delete a client (fails if cotizaciones exist)",
)
async def delete_cliente(
    cliente_id: str,
    db: Connection = Depends(get_db),
    auth: dict = Depends(require_auth),
):
    existing = await db.fetchrow('SELECT id FROM "Cliente" WHERE id = $1', cliente_id)
    if not existing:
        raise HTTPException(status_code=404, detail="Cliente no encontrado")

    cot_count = await db.fetchval(
        'SELECT COUNT(*) FROM "Cotizacion" WHERE "clienteId" = $1', cliente_id
    )
    if cot_count > 0:
        raise HTTPException(
            status_code=409,
            detail="No se puede eliminar: el cliente tiene cotizaciones asociadas",
        )

    await db.execute('DELETE FROM "Cliente" WHERE id = $1', cliente_id)
    return OkResponse()
