from __future__ import annotations

import json

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

from app.auth import require_auth
from app.dependencies import get_db
from app.models.catalogo import (
    ServicioCatalogoCreate,
    ServicioCatalogoUpdate,
    ServicioCatalogoResponse,
    CategoriaResponse,
)
from app.models.common import ArchivedResponse, ErrorResponse, OkResponse

router = APIRouter(prefix="/catalogo", tags=["Catalogo"])


def _row_to_response(row, categoria_row=None) -> ServicioCatalogoResponse:
    entregables = row["entregablesDefault"]
    if isinstance(entregables, str):
        entregables = json.loads(entregables)

    categoria = None
    if categoria_row:
        categoria = CategoriaResponse(
            id=categoria_row["id"],
            nombre=categoria_row["nombre"],
            descripcion=categoria_row["descripcion"],
            color=categoria_row["color"],
            orden=categoria_row["orden"],
            activo=categoria_row["activo"],
            createdAt=categoria_row["createdAt"],
            updatedAt=categoria_row["updatedAt"],
        )

    return ServicioCatalogoResponse(
        id=row["id"],
        nombre=row["nombre"],
        descripcion=row["descripcion"],
        fase=row["fase"],
        tipoPago=row["tipoPago"],
        precioBase=float(row["precioBase"]),
        tiempoEntrega=row["tiempoEntrega"],
        entregablesDefault=entregables,
        categoriaId=row["categoriaId"],
        variante=row["variante"],
        activo=row["activo"],
        orden=row["orden"],
        createdAt=row["createdAt"],
        updatedAt=row["updatedAt"],
        categoriaRel=categoria,
    )


@router.get(
    "",
    response_model=list[ServicioCatalogoResponse],
    summary="List active catalog services with filters",
)
async def list_catalogo(
    db: Connection = Depends(get_db),
    auth: dict = Depends(require_auth),
    fase: int | None = Query(None, ge=0, le=3),
    tipoPago: str | None = Query(None, pattern="^(unico|mensual)$"),
    categoriaId: str | None = Query(None),
    q: str | None = Query(None, description="Search by nombre or descripcion"),
):
    where_clauses: list[str] = ['activo = true']
    params: list = []
    idx = 1

    if fase is not None:
        where_clauses.append(f"fase = ${idx}")
        params.append(fase)
        idx += 1

    if tipoPago:
        where_clauses.append(f'"tipoPago" = ${idx}')
        params.append(tipoPago)
        idx += 1

    if categoriaId:
        where_clauses.append(f'"categoriaId" = ${idx}')
        params.append(categoriaId)
        idx += 1

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

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

    rows = await db.fetch(
        f'SELECT * FROM "ServicioCatalogo" {where_sql} ORDER BY fase ASC, orden ASC',
        *params,
    )

    result: list[ServicioCatalogoResponse] = []
    for r in rows:
        cat_row = None
        if r["categoriaId"]:
            cat_row = await db.fetchrow(
                'SELECT * FROM "Categoria" WHERE id = $1', r["categoriaId"]
            )
        result.append(_row_to_response(r, cat_row))

    return result


@router.post(
    "",
    response_model=ServicioCatalogoResponse,
    status_code=status.HTTP_201_CREATED,
    summary="Create a new catalog service",
)
async def create_catalogo(
    body: ServicioCatalogoCreate,
    db: Connection = Depends(get_db),
    auth: dict = Depends(require_auth),
):
    if body.categoriaId:
        cat = await db.fetchrow(
            'SELECT id FROM "Categoria" WHERE id = $1', body.categoriaId
        )
        if not cat:
            raise HTTPException(status_code=400, detail="Categoria no encontrada")

    entregables_json = json.dumps(body.entregablesDefault)

    row = await db.fetchrow(
        'INSERT INTO "ServicioCatalogo" '
        '(nombre, descripcion, fase, "tipoPago", "precioBase", "tiempoEntrega", '
        '"entregablesDefault", "categoriaId", variante, activo, orden) '
        "VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, true, $10) RETURNING *",
        body.nombre,
        body.descripcion,
        body.fase,
        body.tipoPago,
        body.precioBase,
        body.tiempoEntrega,
        entregables_json,
        body.categoriaId,
        body.variante,
        body.orden,
    )

    cat_row = None
    if row["categoriaId"]:
        cat_row = await db.fetchrow(
            'SELECT * FROM "Categoria" WHERE id = $1', row["categoriaId"]
        )

    return _row_to_response(row, cat_row)


@router.get(
    "/{servicio_id}",
    response_model=ServicioCatalogoResponse,
    responses={404: {"model": ErrorResponse}},
    summary="Get catalog service by ID with categoria join",
)
async def get_catalogo(
    servicio_id: str,
    db: Connection = Depends(get_db),
    auth: dict = Depends(require_auth),
):
    row = await db.fetchrow(
        'SELECT * FROM "ServicioCatalogo" WHERE id = $1', servicio_id
    )
    if not row:
        raise HTTPException(status_code=404, detail="Servicio no encontrado")

    cat_row = None
    if row["categoriaId"]:
        cat_row = await db.fetchrow(
            'SELECT * FROM "Categoria" WHERE id = $1', row["categoriaId"]
        )

    return _row_to_response(row, cat_row)


@router.put(
    "/{servicio_id}",
    response_model=ServicioCatalogoResponse,
    responses={404: {"model": ErrorResponse}},
    summary="Update a catalog service",
)
async def update_catalogo(
    servicio_id: str,
    body: ServicioCatalogoUpdate,
    db: Connection = Depends(get_db),
    auth: dict = Depends(require_auth),
):
    existing = await db.fetchrow(
        'SELECT id FROM "ServicioCatalogo" WHERE id = $1', servicio_id
    )
    if not existing:
        raise HTTPException(status_code=404, detail="Servicio no encontrado")

    if body.categoriaId:
        cat = await db.fetchrow(
            'SELECT id FROM "Categoria" WHERE id = $1', body.categoriaId
        )
        if not cat:
            raise HTTPException(status_code=400, detail="Categoria no encontrada")

    field_map = {
        "nombre": "nombre",
        "descripcion": "descripcion",
        "fase": "fase",
        "tipoPago": '"tipoPago"',
        "precioBase": '"precioBase"',
        "tiempoEntrega": '"tiempoEntrega"',
        "entregablesDefault": '"entregablesDefault"',
        "categoriaId": '"categoriaId"',
        "variante": "variante",
        "activo": "activo",
        "orden": "orden",
    }

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

    for field, col in field_map.items():
        value = getattr(body, field, None)
        if value is not None:
            if field == "entregablesDefault":
                value = json.dumps(value)
            updates.append(f"{col} = ${idx}")
            params.append(value)
            idx += 1

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

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

    cat_row = None
    if row["categoriaId"]:
        cat_row = await db.fetchrow(
            'SELECT * FROM "Categoria" WHERE id = $1', row["categoriaId"]
        )

    return _row_to_response(row, cat_row)


@router.delete(
    "/{servicio_id}",
    response_model=ArchivedResponse,
    responses={404: {"model": ErrorResponse}},
    summary="Delete or soft-delete a catalog service",
)
async def delete_catalogo(
    servicio_id: str,
    db: Connection = Depends(get_db),
    auth: dict = Depends(require_auth),
):
    existing = await db.fetchrow(
        'SELECT id FROM "ServicioCatalogo" WHERE id = $1', servicio_id
    )
    if not existing:
        raise HTTPException(status_code=404, detail="Servicio no encontrado")

    ref_count = await db.fetchval(
        'SELECT COUNT(*) FROM "ServicioCotizado" WHERE "servicioCatalogoId" = $1',
        servicio_id,
    )

    if ref_count > 0:
        await db.execute(
            'UPDATE "ServicioCatalogo" SET activo = false WHERE id = $1', servicio_id
        )
        return ArchivedResponse(ok=True, archived=True)

    await db.execute('DELETE FROM "ServicioCatalogo" WHERE id = $1', servicio_id)
    return ArchivedResponse(ok=True, archived=False)
