"""
Validaciones comunes de fila para import CSV de fracciones arancelarias americanas.
Paridad Clarion: desfase Col H, VALIDA_TODA_FRACCIONAME / VALIDA_PARCIAL_FRACCIONAME, VALIDACIONES_FRACCIONAME.
"""
from typing import Dict, Any, Optional, Set, List

from ..template_config import DESFASE_COLUMN_INDEX
from ...common.cell_value import cell_to_str
from ..common.common_validators import (
    normalize_code,
    check_optional_max_length,
    check_optional_decimal_min_zero,
    check_tipo_advalorem,
    PREFIX_MAX,
    UNIT_MAX,
    CODE_MAX,
)
from ..common.common_validators import TIPO_ADVALOREM_VALIDOS  # noqa: F401 re-export
from api.v1.modules.sitar.fracciones_usa.catalog_resolve import (
    resolve_american_fraction_from_sitar,
)

# Mensajes Clarion
MSG_COL_A_VACIO = (
    "Error: (Col. A) La columna de Fracción Americana esta vacia y no se pueden hacer las validaciones. "
    "Capturar en la Columna A una Fracción Americana nueva o una ya existente al cual desee actualizar campos"
)
MSG_COL_A_LONGITUD = (
    "Capturar en la columna A una Fraccion Arancelaria de 16 caracteres como máximo."
)
MSG_COL_D_OBLIGATORIO = (
    "Existen campos vacios que son obligatorios, es la (Col.D) Descripción. "
    "Revisar la línea del archivo y capturar los campos con la información correcta."
)
MSG_FRACCION_NO_EXISTE = (
    "Error: (Col. A) La Fraccion Americana no existe."
)
MSG_DESFASE = "Advertencia: Podría existir un desfase en esta línea."
MSG_DESFASE_SOLUCION = "Revisar esta línea del archivo CSV y verificar cada campo esté en la posición correcta."


def validate_row_desfase_fa(
    raw_row: Optional[Dict[str, Any]], line_num: int
) -> Optional[Dict[str, Any]]:
    """
    Si la fila tiene 8+ columnas y la 8ª (Col H) tiene valor → advertencia (no bloqueante).
    Devuelve dict con severity=warning para que el caller pueda acumularlo en warnings_detail.
    """
    if not raw_row:
        return None
    values_ordered = list(raw_row.values())
    if len(values_ordered) <= DESFASE_COLUMN_INDEX:
        return None
    cell = cell_to_str(values_ordered[DESFASE_COLUMN_INDEX])
    if not cell.strip():
        return None
    return {
        "line": line_num,
        "col": "COL_EXTRA",
        "msg": MSG_DESFASE,
        "solution": MSG_DESFASE_SOLUCION,
        "severity": "warning",
        "warning": True,
    }


def _validate_col_a_required(row: Dict[str, Any], line_num: int) -> Optional[Dict[str, Any]]:
    """Col A obligatoria; mensaje Clarion."""
    code_raw = (row.get("FRACCION_ARANCELARIA") or "").strip()
    if not code_raw:
        return {"line": line_num, "col": "FRACCION_ARANCELARIA", "msg": MSG_COL_A_VACIO}
    code_norm = normalize_code(code_raw)
    if not code_norm:
        return {"line": line_num, "col": "FRACCION_ARANCELARIA", "msg": MSG_COL_A_VACIO}
    if len(code_norm) > CODE_MAX:
        return {
            "line": line_num,
            "col": "FRACCION_ARANCELARIA",
            "msg": f"Error: (Col. A) La Fraccion Americana: {code_raw} supera la longitud de caracteres. {MSG_COL_A_LONGITUD}",
        }
    return None


def _validate_col_d_required(row: Dict[str, Any], line_num: int) -> Optional[Dict[str, Any]]:
    """Col D obligatoria en validación completa (VALIDA_TODA)."""
    if (row.get("DESCRIPCION") or "").strip():
        return None
    return {"line": line_num, "col": "DESCRIPCION", "msg": MSG_COL_D_OBLIGATORIO}


def _validate_uom_catalog(
    row: Dict[str, Any], line_num: int, valid_uom_codes: Optional[Set[str]]
) -> Optional[Dict[str, Any]]:
    """Col C: si no vacía, debe existir en catálogo U.M. (Clarion GUniMedida)."""
    val = (row.get("UNIDAD_DE_MEDIDA") or "").strip().upper()
    if not val or valid_uom_codes is None:
        return None
    if val in valid_uom_codes:
        return None
    return {
        "line": line_num,
        "col": "UNIDAD_DE_MEDIDA",
        "msg": (
            f"Error: (Col. C) La Unidad de Medida: {row.get('UNIDAD_DE_MEDIDA') or ''} no existe en el Catálogo de U.M. "
            "Revisar esta Unidad de Medida en el archivo, en caso de ser correcta dar la de alta en el Catálogo de U.M."
        ),
    }


def _validaciones_fraccioname(
    row: Dict[str, Any],
    line_num: int,
    valid_uom_codes: Optional[Set[str]],
) -> List[Dict[str, Any]]:
    """VALIDACIONES_FRACCIONAME: Col A len ≤16, Col C en catálogo U.M., Col E PO/ME/vacío, F/G ≥0."""
    errors: List[Dict[str, Any]] = []

    for err in [
        _validate_col_a_required(row, line_num),
        check_optional_max_length(row, "PREFIJO", PREFIX_MAX, line_num),
        check_optional_max_length(row, "UNIDAD_DE_MEDIDA", UNIT_MAX, line_num),
        _validate_uom_catalog(row, line_num, valid_uom_codes),
        check_tipo_advalorem(row, "TIPO_DE_ADVALOREM", line_num),
        check_optional_decimal_min_zero(row, "ADVALOREM_PCT", line_num),
        check_optional_decimal_min_zero(row, "ADVALOREM_DLLS", line_num),
    ]:
        if err:
            errors.append(err)

    return errors


def validate_row_us_tariff_fraction(
    row: Dict[str, Any],
    line_num: int,
    *,
    actualizar: bool = False,
    existing_fraction_codes: Optional[Set[str]] = None,
    valid_uom_codes: Optional[Set[str]] = None,
    raw_row: Optional[Dict[str, Any]] = None,
) -> List[Dict[str, Any]]:
    """
    Valida una fila de CSV de fracción arancelaria americana.
    - raw_row: si se pasa y la 8ª columna tiene valor, se devuelve advertencia de desfase (no bloqueante; el caller decide si la trata como error).
    - Col A vacía → error.
    - Si actualizar y fracción no existe en existing_fraction_codes → error "La Fracción Americana no existe".
    - Si actualizar y fracción existe → VALIDA_PARCIAL (solo VALIDACIONES_FRACCIONAME; Col D no obligatoria).
    - Si no actualizar → VALIDA_TODA (Col D obligatoria + VALIDACIONES_FRACCIONAME).
    """
    # Desfase: advertencia (no bloqueante por defecto; se devuelve como error para que scan la registre en errors_detail pero no bloquea commit si no está en error_lines)
    # Plan: "advertencia no bloqueante" → no añadimos a error_lines; guardamos en warnings. Para simplificar, desfase lo devolvemos como error de severidad warning y en tasks no lo añadimos a error_lines (solo a warnings_detail). Mejor: desfase retornamos None (no error) y el caller puede llamar a validate_row_desfase_fa por separado y acumular warnings. Así no bloqueamos. Entonces en validate_row_us_tariff_fraction no llamamos desfase como error; en tasks llamamos primero validate_row_desfase_fa y si hay warning lo guardamos en warnings_detail, luego llamamos validate_row_us_tariff_fraction que puede devolver error. OK.
    # So we don't return desfase from validate_row_us_tariff_fraction; tasks will call validate_row_desfase_fa and collect warnings. So no change here for desfase inside this function.

    errors: List[Dict[str, Any]] = []

    err = _validate_col_a_required(row, line_num)
    if err:
        errors.append(err)

    code_norm = normalize_code((row.get("FRACCION_ARANCELARIA") or "").strip())
    fraction_exists = (
        bool(code_norm)
        and resolve_american_fraction_from_sitar(code_norm) is not None
    )

    if actualizar and not fraction_exists:
        errors.append({
            "line": line_num,
            "col": "FRACCION_ARANCELARIA",
            "msg": MSG_FRACCION_NO_EXISTE,
        })

    use_full = not actualizar or not fraction_exists
    if use_full:
        err = _validate_col_d_required(row, line_num)
        if err:
            errors.append(err)

    errors.extend(_validaciones_fraccioname(row, line_num, valid_uom_codes))
    return errors
