"""Consultas de los catálogos del SAT.

Son globales (sin tenant_id / company_id) y de solo lectura: aquí no hay altas,
cambios ni bajas, únicamente búsqueda para llenar los selectores de captura.
"""

from sqlalchemy import or_
from sqlalchemy.orm import Session

from .models import (
    CfdiUse,
    PaymentForm,
    PaymentMethod,
    ProductService,
    Tax,
    TaxObject,
    TaxRegime,
    UnitOfMeasure,
    VoucherType,
)

# Catálogos que además del código y la descripción buscan por nombre corto.
_SEARCHABLE_EXTRA_FIELDS = {UnitOfMeasure: ("name",)}


def search_catalog(
    db: Session,
    model,
    search: str | None = None,
    active_only: bool = True,
    limit: int | None = None,
) -> list:
    """Devuelve las claves de un catálogo, filtradas por texto libre.

    ``search`` compara contra la clave o la descripción sin distinguir mayúsculas.
    """
    q = db.query(model)
    if active_only:
        q = q.filter(model.is_active.is_(True))
    if search:
        term = f"%{search.strip()}%"
        fields = [model.code, model.description]
        for extra in _SEARCHABLE_EXTRA_FIELDS.get(model, ()):
            fields.append(getattr(model, extra))
        q = q.filter(or_(*[f.ilike(term) for f in fields]))
    q = q.order_by(model.code.asc())
    if limit is not None:
        q = q.limit(limit)
    return q.all()


def get_tax_regimes(
    db: Session,
    search: str | None = None,
    active_only: bool = True,
    person_type: str | None = None,
) -> list[TaxRegime]:
    """``c_RegimenFiscal``, opcionalmente acotado al tipo de persona.

    ``person_type='fisica'`` deja solo los regímenes que puede usar una persona
    física; ``'moral'``, los de persona moral.
    """
    q = db.query(TaxRegime)
    if active_only:
        q = q.filter(TaxRegime.is_active.is_(True))
    if search:
        term = f"%{search.strip()}%"
        q = q.filter(or_(TaxRegime.code.ilike(term), TaxRegime.description.ilike(term)))
    if person_type == "fisica":
        q = q.filter(TaxRegime.applies_to_individual.is_(True))
    elif person_type == "moral":
        q = q.filter(TaxRegime.applies_to_legal_entity.is_(True))
    return q.order_by(TaxRegime.code.asc()).all()


def get_taxes(db: Session, search=None, active_only=True) -> list[Tax]:
    return search_catalog(db, Tax, search, active_only)


def get_payment_forms(db: Session, search=None, active_only=True) -> list[PaymentForm]:
    return search_catalog(db, PaymentForm, search, active_only)


def get_units_of_measure(db: Session, search=None, active_only=True) -> list[UnitOfMeasure]:
    return search_catalog(db, UnitOfMeasure, search, active_only)


def get_products_services(db: Session, search=None, active_only=True, limit=50) -> list[ProductService]:
    """``c_ClaveProdServ``. Va paginado porque alimenta un autocompletado."""
    return search_catalog(db, ProductService, search, active_only, limit=limit)


def get_voucher_types(db: Session, search=None, active_only=True) -> list[VoucherType]:
    return search_catalog(db, VoucherType, search, active_only)


def get_payment_methods(db: Session, search=None, active_only=True) -> list[PaymentMethod]:
    return search_catalog(db, PaymentMethod, search, active_only)


def get_tax_objects(db: Session, search=None, active_only=True) -> list[TaxObject]:
    return search_catalog(db, TaxObject, search, active_only)


def get_cfdi_uses(db: Session, search=None, active_only=True) -> list[CfdiUse]:
    return search_catalog(db, CfdiUse, search, active_only)
