"""
Validaciones comunes de fila para import CSV de clientes y proveedores.
Paridad Clarion: VALIDA_TODA_CLIENTE_O_PROV, VALIDA_PARCIAL_CLIENTE_O_PROV, VALIDACIONES_CLIENTE_O_PROV.
Origen de reglas: código legacy Clarion (EstructuraCatClienteProv).
Mapa columnas: A=PROCEDENCIA, B=TIPO, C=SHORT_NAME, D=NOMBRE, E=RFC, F–AG (ver template_config).
"""
from typing import Dict, Any, Optional, Set, List

from ..common.common_validators import (
    RFC_MAX,
    NAME_MAX,
    SHORT_NAME_MAX_CLARION,
    CURP_MAX,
    check_required_max,
    check_max_length,
    check_rfc_format,
    check_tax_id_format,
    check_tipo_client_provider,
    check_procedencia,
    check_short_name_max_clarion,
    check_tipo_programa_secon,
    check_es_prosec_num_aut,
    check_vinculacion,
    check_es_empresa_certificada_registro,
    check_transformador_submaq,
    check_desfase,
)


# --- Mensajes obligatorios (VALIDA_TODA) ---
MSG_CAMPOS_OBLIGATORIOS = "Existen campos vacíos que son obligatorios: {campos}."
MSG_CAMPOS_OBLIGATORIOS_SOLUCION = "Revisar la línea del archivo y capturar los campos con la información correcta."
MSG_CLAVE_VACIA = (
    "Error: La Clave de Cliente/Proveedor está vacía y no se pueden hacer las validaciones."
)
MSG_CLAVE_VACIA_SOLUCION = (
    "Capturar una Clave de Cliente/Proveedor nueva o existente a la cual desee agregar, remplazar o actualizar campos."
)
MSG_CLAVE_NO_EXISTE = "Error: (Col.C) Clave de Proveedor/Cliente no existe."
MSG_CLAVE_NO_EXISTE_SOLUCION = "La clave debe existir en el catálogo cuando el modo es Actualizar."


def validate_row_desfase(row: Dict[str, Any], line_num: int) -> Optional[Dict[str, Any]]:
    """Si COL_EXTRA tiene valor → advertencia de desfase."""
    return check_desfase(row, line_num)


def validate_row_clave_vacia(row: Dict[str, Any], line_num: int) -> Optional[Dict[str, Any]]:
    """Clave (SHORT_NAME) vacía → error inmediato."""
    val = (row.get("SHORT_NAME") or "").strip()
    if not val:
        return {
            "line": line_num,
            "col": "SHORT_NAME",
            "msg": MSG_CLAVE_VACIA,
            "solution": MSG_CLAVE_VACIA_SOLUCION,
        }
    return None


def validaciones_cliente_o_prov(row: Dict[str, Any], line_num: int) -> List[Dict[str, Any]]:
    """
    VALIDACIONES_CLIENTE_O_PROV: reglas de dominio compartidas.
    Col A (E/N + coherencia con L), B (C/P/A), C (máx 8), Q/R/S, T/U, V, W/X, AF.
    """
    errors: List[Dict[str, Any]] = []

    checks = [
        check_procedencia,
        check_tipo_client_provider,
        check_short_name_max_clarion,
        check_tipo_programa_secon,
        check_es_prosec_num_aut,
        check_vinculacion,
        check_es_empresa_certificada_registro,
        check_transformador_submaq,
    ]

    for fn in checks:
        err = fn(row, line_num)
        if err:
            errors.append(err)

    return errors


def valida_toda_cliente_o_prov(
    row: Dict[str, Any],
    line_num: int,
    actualizar: bool = False,
) -> List[Dict[str, Any]]:
    """
    VALIDA_TODA: obligatorios Col A (Procedencia), Col D (Nombre) cuando no es ACT.
    Si modo ACT y clave no existe → error se devuelve antes (en validate_row_client_provider).
    Luego ejecuta VALIDACIONES_CLIENTE_O_PROV.
    """
    campos_oblig = []
    # Col A - Procedencia obligatoria
    if not (row.get("PROCEDENCIA") or "").strip():
        campos_oblig.append("(Col.A) Tipo Cliente Procedencia (E/N)")
    # Col D - Nombre obligatorio solo cuando no es actualizar
    if not actualizar and not (row.get("NOMBRE") or "").strip():
        campos_oblig.append("(Col.D) Nombre")
    errors: List[Dict[str, Any]] = []
    if campos_oblig:
        errors.append({
            "line": line_num,
            "col": "PROCEDENCIA" if not (row.get("PROCEDENCIA") or "").strip() else "NOMBRE",
            "msg": MSG_CAMPOS_OBLIGATORIOS.format(campos=", ".join(campos_oblig)),
            "solution": MSG_CAMPOS_OBLIGATORIOS_SOLUCION,
        })

    errors.extend(validaciones_cliente_o_prov(row, line_num))
    return errors


def valida_parcial_cliente_o_prov(row: Dict[str, Any], line_num: int) -> List[Dict[str, Any]]:
    """VALIDA_PARCIAL: solo VALIDACIONES_CLIENTE_O_PROV (no exige A ni D)."""
    return validaciones_cliente_o_prov(row, line_num)


def validate_row_client_provider(
    row: Dict[str, Any],
    line_num: int,
    actualizar: bool = False,
    existing_short_names: Optional[Set[str]] = None,
) -> List[Dict[str, Any]]:
    """
    Valida una fila de CSV de clientes y proveedores.
    - Desfase (COL_EXTRA) primero.
    - Clave vacía → error.
    - Si actualizar y clave no existe en existing_short_names → error "Clave no existe".
    - Si actualizar y clave existe → VALIDA_PARCIAL (solo validaciones comunes).
    - Si no actualizar o clave no existe → VALIDA_TODA (A y D obligatorios cuando aplique, luego comunes).
    Además se validan RFC (requerido max 30), longitudes NOMBRE/SHORT_NAME/CURP para compatibilidad.
    """
    errors: List[Dict[str, Any]] = []
    err = validate_row_desfase(row, line_num)
    if err:
        errors.append(err)

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

    short_name = (row.get("SHORT_NAME") or "").strip()
    existing = existing_short_names or set()
    use_partial = actualizar and short_name in existing

    if actualizar and short_name and short_name not in existing:
        errors.append({
            "line": line_num,
            "col": "SHORT_NAME",
            "msg": MSG_CLAVE_NO_EXISTE,
            "solution": MSG_CLAVE_NO_EXISTE_SOLUCION,
        })

    if use_partial:
        errors.extend(valida_parcial_cliente_o_prov(row, line_num))
    else:
        errors.extend(valida_toda_cliente_o_prov(row, line_num, actualizar=actualizar))

    # Identificador fiscal requerido: Nacional = RFC, Extranjero = TAX-ID (mismo campo "RFC" en layout).
    procedencia = (row.get("PROCEDENCIA") or "").strip().upper()[:1]

    err = check_required_max(row, "RFC", RFC_MAX, line_num)
    if err:
        if procedencia == "E":
            err = {
                "line": line_num,
                "col": "RFC",
                "msg": "Requerido (TAX-ID)",
                "solution": "Capturar el TAX-ID del cliente/proveedor extranjero en la columna E (RFC/TAX-ID).",
            }
        errors.append(err)
    else:
        # Validar formato según procedencia: RFC (N) o TAX-ID (E).
        if procedencia == "N":
            err = check_rfc_format(row, line_num)
        elif procedencia == "E":
            err = check_tax_id_format(row, line_num)
        if err:
            errors.append(err)

    for err in [
        check_max_length(row, "NOMBRE", NAME_MAX, line_num),
        check_max_length(row, "SHORT_NAME", 10, line_num),  # modelo permite 10
        check_max_length(row, "CURP", CURP_MAX, line_num),
    ]:
        if err:
            errors.append(err)

    return errors
