"""Lógica de negocio de los catálogos de referencia del CRM."""

from typing import Any

from fastapi import HTTPException, status
from sqlalchemy import and_, or_
from sqlalchemy.orm import Session

from core.security import is_hub_admin

from .dto import CatalogItemCreate, CatalogItemUpdate, CatalogMeta
from .models import CatalogItem
from .seed_data import GLOBAL_CATALOGS, TENANT_CATALOG_LABELS

# Metadata de catálogos (labels y si el cliente puede llenarlos).
CATALOG_LABELS: dict[str, str] = {k: v["label"] for k, v in GLOBAL_CATALOGS.items()}
CATALOG_LABELS.update(TENANT_CATALOG_LABELS)
# Catálogos que administra el cliente (tenant). El resto son globales (Aduanasoft).
TENANT_CATALOG_KEYS = set(TENANT_CATALOG_LABELS.keys())
KNOWN_CATALOGS = set(CATALOG_LABELS.keys())


def _require_known(catalog: str) -> None:
    if catalog not in KNOWN_CATALOGS:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Catálogo '{catalog}' no existe")


def list_meta(db: Session, tenant_id: int) -> list[CatalogMeta]:
    """Lista todos los catálogos disponibles con su conteo (global + del tenant)."""
    out: list[CatalogMeta] = []
    for key, label in CATALOG_LABELS.items():
        is_tenant = key in TENANT_CATALOG_KEYS
        count = (
            db.query(CatalogItem)
            .filter(
                CatalogItem.catalog == key,
                or_(CatalogItem.tenant_id.is_(None), CatalogItem.tenant_id == tenant_id),
            )
            .count()
        )
        out.append(
            CatalogMeta(
                catalog=key,
                label=label,
                scope="tenant" if is_tenant else "global",
                is_system=bool(GLOBAL_CATALOGS.get(key, {}).get("is_system", False)),
                count=count,
            )
        )
    return out


def list_items(
    db: Session,
    catalog: str,
    tenant_id: int,
    parent_code: str | None = None,
    include_inactive: bool = False,
) -> list[CatalogItem]:
    _require_known(catalog)
    q = db.query(CatalogItem).filter(
        CatalogItem.catalog == catalog,
        or_(CatalogItem.tenant_id.is_(None), CatalogItem.tenant_id == tenant_id),
    )
    if not include_inactive:
        q = q.filter(CatalogItem.is_active.is_(True))
    if parent_code:
        q = q.filter(CatalogItem.parent_code == parent_code)
    return q.order_by(CatalogItem.sort_order, CatalogItem.label).all()


def _resolve_write_scope(catalog: str, scope: str | None, current_user: dict) -> int | None:
    """Devuelve el tenant_id a usar al escribir (None = global) y valida permisos.

    - scope 'global' → solo hub_admin puede tocar catálogos globales (Aduanasoft).
    - scope 'tenant' (default) → se guarda en el tenant del usuario.
    """
    wants_global = scope == "global"
    if wants_global:
        if not is_hub_admin(current_user):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Solo un administrador de Aduanasoft puede editar catálogos globales.",
            )
        return None
    return int(current_user["tenant_id"])


def create_item(
    db: Session, catalog: str, data: CatalogItemCreate, current_user: dict, scope: str | None = None
) -> CatalogItem:
    _require_known(catalog)
    target_tenant = _resolve_write_scope(catalog, scope, current_user)

    # No duplicar por (catalog, code, tenant_id)
    exists = (
        db.query(CatalogItem)
        .filter(
            CatalogItem.catalog == catalog,
            CatalogItem.code == data.code,
            CatalogItem.tenant_id.is_(None) if target_tenant is None else CatalogItem.tenant_id == target_tenant,
        )
        .first()
    )
    if exists:
        raise HTTPException(
            status_code=status.HTTP_409_CONFLICT,
            detail=f"Ya existe la clave '{data.code}' en el catálogo '{catalog}'.",
        )

    item = CatalogItem(
        catalog=catalog,
        code=data.code,
        label=data.label,
        parent_catalog=data.parent_catalog,
        parent_code=data.parent_code,
        tenant_id=target_tenant,
        sort_order=data.sort_order,
        is_active=data.is_active,
        is_system=False,
        created_by=current_user.get("sub"),
        updated_by=current_user.get("sub"),
    )
    db.add(item)
    db.commit()
    db.refresh(item)
    return item


def _get_writable(db: Session, catalog: str, item_id: int, current_user: dict) -> CatalogItem:
    _require_known(catalog)
    item = db.query(CatalogItem).filter(CatalogItem.id == item_id, CatalogItem.catalog == catalog).first()
    if not item:
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Elemento no encontrado")
    if item.tenant_id is None:
        # Global (Aduanasoft): solo hub_admin.
        if not is_hub_admin(current_user):
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail="Solo un administrador de Aduanasoft puede editar este catálogo global.",
            )
    elif item.tenant_id != int(current_user["tenant_id"]):
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="Elemento no encontrado")
    return item


def update_item(
    db: Session, catalog: str, item_id: int, data: CatalogItemUpdate, current_user: dict
) -> CatalogItem:
    item = _get_writable(db, catalog, item_id, current_user)
    payload: dict[str, Any] = data.model_dump(exclude_unset=True)
    for field, value in payload.items():
        setattr(item, field, value)
    item.updated_by = current_user.get("sub")
    db.commit()
    db.refresh(item)
    return item


def delete_item(db: Session, catalog: str, item_id: int, current_user: dict) -> None:
    item = _get_writable(db, catalog, item_id, current_user)
    if item.is_system:
        raise HTTPException(
            status_code=status.HTTP_400_BAD_REQUEST,
            detail="Un catálogo base del sistema no se puede borrar; puedes desactivarlo.",
        )
    db.delete(item)
    db.commit()
