"""
Configuración de plantilla CSV para Clases de Materiales (EstructuraCatClasesAF.xls).
Cabeceras de descarga = Clarion: CLAVE CLASE, DESCRIPCION ESPAÑOL, DESCRIPCION INGLES, etc.
"""
import csv
import io
from typing import Dict, List, Any, Optional, Tuple

from ..common.cell_value import cell_to_str


# Valores que indican que la primera fila es cabecera (primera columna normalizada)
FIRST_COLUMN_HEADER_VALUES = ("CLAVE CLASE", "CLASE")

_ENCODING_FALLBACKS: Tuple[str, ...] = ("utf-8-sig", "utf-8", "cp1252", "latin-1")


def _read_text_sample(file_path: str, sample_bytes: int = 2048) -> str:
    with open(file_path, "rb") as f:
        raw = f.read(sample_bytes)
    last_err: Optional[Exception] = None
    for enc in _ENCODING_FALLBACKS:
        try:
            return raw.decode(enc)
        except Exception as e:
            last_err = e
    if last_err:
        raise last_err
    return ""


def detect_headers_or_data(
    file_path: str,
    normalize_header_fn,
    encoding: str = "utf-8-sig",
) -> Tuple[Optional[List[str]], bool]:
    """
    Lee la primera línea del CSV y decide si es cabecera o dato.
    Devuelve (fieldnames, has_header).
    - Si la primera celda normalizada está en FIRST_COLUMN_HEADER_VALUES -> has_header=True, fieldnames=None
      (la primera fila es cabecera; iter_csv_rows sin fieldnames).
    - Si no -> has_header=False, fieldnames=TEMPLATE_DOWNLOAD_HEADERS (la primera fila es dato).
    """
    try:
        # `encoding` se mantiene por compatibilidad; si falla, hacemos fallback para CSVs tipo Excel (cp1252/latin-1).
        if encoding and encoding.lower() not in ("auto", "detect"):
            try:
                with open(file_path, "r", encoding=encoding) as f:
                    sample = f.read(2048)
            except Exception:
                sample = _read_text_sample(file_path, sample_bytes=2048)
        else:
            sample = _read_text_sample(file_path, sample_bytes=2048)
    except Exception:
        return None, True
    lines = sample.splitlines()
    if not lines:
        return None, True
    try:
        dialect = csv.Sniffer().sniff(sample, delimiters=",;\t")
    except Exception:
        dialect = csv.excel
    reader = csv.reader(io.StringIO(lines[0]), dialect=dialect)
    first_row = next(reader, None)
    if not first_row:
        return None, True
    first_cell = (first_row[0] or "").strip()
    first_cell_norm = normalize_header_fn(first_cell)
    if first_cell_norm in FIRST_COLUMN_HEADER_VALUES:
        return None, True
    return list(TEMPLATE_DOWNLOAD_HEADERS), False

# Cabeceras que se escriben al descargar la plantilla CSV (igual que Clarion EstructuraCatClasesAF)
TEMPLATE_DOWNLOAD_HEADERS: List[str] = [
    "CLAVE CLASE",
    "DESCRIPCION ESPAÑOL",
    "DESCRIPCION INGLES",
    "TIPO DE MATERIAL",
    "U.M. COMERCIAL",
    "FRACCION ARANCELARIA",
    "FRACCION AMERICANA",
    "TASA DE DEPRECIACION",
    "REVISION FISICA (1/0)",
    "CODIGO DE PRODUCTO/SERVICIO CP",
]

TEMPLATE_COLUMNS: Dict[str, List[Dict[str, Any]]] = {
    "material_classes": [
        {"canonical": "CLASE", "aliases": ["CLAVE CLASE", "CLASS", "CODIGO", "CLASE CODIGO"]},
        {"canonical": "DESCRIPCIONE", "aliases": ["DESCRIPCION ESPAÑOL", "DESCRIPCION", "DESCRIPCION ES", "DESCRIPCION ESPAÑOL"]},
        {"canonical": "DESCRIPCIONI", "aliases": ["DESCRIPCION INGLES", "DESCRIPCION EN", "DESCRIPTION", "DESCRIPCION INGLES"]},
        {"canonical": "CLAVEMAT", "aliases": ["TIPO DE MATERIAL", "MATERIAL", "TIPOMAT", "CLAVE MATERIAL"]},
        {"canonical": "UNIMED", "aliases": ["U.M. COMERCIAL", "UNIDAD MEDIDA", "UNIT", "UOM", "TIPO DE MU.M.", "U.M. COMERCIAL"]},
        {"canonical": "FRACCION", "aliases": ["FRACCION ARANCELARIA", "FRACCION MEX", "COM FRACCION"]},
        {"canonical": "FRACCIONAME", "aliases": ["FRACCION AMERICANA", "FRACCION USA", "US FRACTION", "FRACCION"]},
        {"canonical": "TASADEPRECIA", "aliases": ["TASA DE DEPRECIACION", "TASA DEPRECIACIÓN", "TASA DEPRECIACION"]},
        {"canonical": "CLAVESUB", "aliases": ["SUB KEY", "CLAVE SUB"]},
        {"canonical": "REVFISICA", "aliases": ["REVISION FISICA (1/0)", "REV FISICA", "PHYSICAL REVIEW", "TASA DE REVISION", "REVISION FISICA"]},
        {"canonical": "FRACCIONEXENTAIVA", "aliases": ["CODIGO DE PRODUCTO/SERVICIO CP", "EXENTA IVA", "FRACCION EXENTA IVA", "CODIGO PRODUCTO SERVICIO CP"]},
    ],
}


def build_normalized_lookup(normalize_header_fn) -> Dict[str, str]:
    cols = TEMPLATE_COLUMNS.get("material_classes")
    if not cols:
        return {}
    lookup: Dict[str, str] = {}
    for item in cols:
        canonical = item["canonical"]
        lookup[normalize_header_fn(canonical)] = canonical
        for alias in item.get("aliases") or []:
            lookup[normalize_header_fn(alias)] = canonical
    return lookup


def row_from_template(row: Dict[str, Any], normalize_header_fn) -> Dict[str, Any]:
    lookup = build_normalized_lookup(normalize_header_fn)
    if not lookup:
        return {normalize_header_fn(k): cell_to_str(v) for k, v in row.items()}
    out: Dict[str, Any] = {}
    for csv_header, value in row.items():
        key_norm = normalize_header_fn(csv_header)
        if key_norm in lookup:
            out[lookup[key_norm]] = cell_to_str(value)
        elif key_norm.startswith("CLAVE CLASE"):
            # CSV leído con delimitador incorrecto: primera columna es "CLAVE CLASE,..." -> usar primer valor como CLASE
            if "CLASE" not in out and value:
                val_str = cell_to_str(value)
                first_val = (val_str.split(",")[0] if "," in val_str else val_str).strip()
                if first_val:
                    out["CLASE"] = first_val
    return out
