"""
REVISA_CANT_SERIES
Validates series count against export quantity for each line that carries
serial numbers (LlevaSerie = 1 / has_serial = True).

Rules (ported from legacy SCAII REVISA_CANT_SERIES):
  1. If the line carries series but no series records exist → error SERIES_VACIAS.
  2. If SisGen:CantvsCantSeries = 1:
     a. RFC-exception companies (hardcoded set):
        - If invoice is a cambio de régimen (is_regime_change): only validate
          when the line's unit of measure is 'PZA'.
        - Otherwise: always validate count vs quantity.
     b. All other companies: always validate count vs quantity.

Note: the GNiv:CantSerievsCant = 0 block (series > quantity warning) was
commented-out in the original Clarion and is therefore not ported.
"""

from decimal import Decimal
from typing import List

from sqlalchemy.orm import Session

from api.v1.modules.a76.general_catalogs.company.models import Company
from api.v1.modules.a76.invoices.models import InvoiceHeader
from api.v1.modules.a76.items.models import LineItem
from api.v1.modules.a76.items.series.models import Serie
from api.v1.modules.a76.app_settings.service import AppSettingsService
from core.exceptions import ErrorCollector

# RFCs where qty-vs-series validation is conditional on UOM = PZA when is_regime_change
_RFC_EXCEPCION_PZA = {
    "IMS030409FZ0",
    "TOP140430PB6",
    "AMA7504258K2",
    "BZG111091T9",
}


def _validate_line_series(
    db: Session,
    invoice: InvoiceHeader,
    line: LineItem,
    company_rfc: str,
    errors: ErrorCollector,
    valida_cant_series: int = 0,
) -> None:
    """Validates series count for a single line that has has_serial = True."""
    series_count = (
        db.query(Serie)
        .filter(Serie.line_item_id == line.id)
        .count()
    )

    # Rule 1: series flag active but no series records exist
    if series_count == 0:
        errors.add_error(
            field=f"line[{line.line_number}].series",
            message="La opción de contiene series esta activada y no existen registros de Series",
            solution=[
                "Desactivar la opción de Lleva series o registrar las series a esta partida."
            ],
            code="SERIES_VACIAS",
        )
        return

    # Rule 2: quantity vs series count check (controlled by SisGen flag)
    if valida_cant_series != 1:
        return

    qty = line.quantity.quantity if line.quantity else None
    if qty is None:
        return

    is_regime_change = bool(
        invoice.compliance_mx and invoice.compliance_mx.is_regime_change
    )

    uom_code = ""
    if line.unit_of_measure_info:
        uom_code = (line.unit_of_measure_info.code or "").strip().upper()

    if company_rfc in _RFC_EXCEPCION_PZA:
        # RFC-exception: when cambio de régimen only validate for PZA lines
        if is_regime_change and uom_code != "PZA":
            return
    # For all other companies (and exception RFCs without cambio de régimen),
    # always compare count vs quantity

    if series_count != qty:
        errors.add_error(
            field=f"line[{line.line_number}].series",
            message="La Cantidad de Series No Coincide con la Cantidad de la Partida.",
            solution=[f"Nivelar las series de la Partida {line.line_number}."],
            code="SERIES_VS_CANT",
        )


def review_qty_series(
    db: Session,
    invoice: InvoiceHeader,
    lines: List[LineItem],
    tenant_id: str,
    company_id: str,
    errors: ErrorCollector,
) -> None:
    """
    REVISA_CANT_SERIES
    Iterates all line items and validates series count for those that carry
    serial numbers (has_serial = True / LlevaSerie = 1).
    """
    company = db.get(Company, company_id)
    company_rfc = (company.rfc or "").strip().upper() if company else ""

    settings = AppSettingsService.get_resolved_settings(db, int(tenant_id), int(company_id))
    q_gen = settings.get("qsisgen", {})
    s_gen = settings.get("ssisgen", {})

    # Switch maestro: validarseries = 0 desactiva toda la validación de series (SSisGen/QSisGen)
    valida_series_global = int(q_gen.get("validarseries") or s_gen.get("validarseries", 0))
    if valida_series_global != 1:
        return

    valida_cant_series = int(q_gen.get("cantvscantseries") or s_gen.get("cantvscantseries", 0))

    for line in lines:
        if not (line.description and line.description.has_serial):
            continue

        _validate_line_series(db, invoice, line, company_rfc, errors, valida_cant_series)
