"""
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
from ..common import csv_reader as common_csv_reader
from ..common.csv_hint_inject import inject_max_char_hints_into_columns, merge_enum_hint_before_max
from ..common.inject_csv_labels_en import inject_en_labels_into_template_columns
from ..common.template_locale import (
    download_header_cell,
    merge_download_header_into_lookup,
    merge_labels_into_lookup,
    normalize_locale,
)
from .csv_max_chars_for_headers import MATERIAL_CLASSES_CSV_MAX_CHARS


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

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:
        sample, _ = common_csv_reader.read_text_sample(
            file_path,
            requested_encoding=encoding,
            sample_chars=2048,
        )
    except Exception:
        try:
            sample, _ = common_csv_reader.read_text_sample(
                file_path,
                requested_encoding="auto",
                sample_chars=2048,
            )
        except Exception:
            return None, True

    if not sample:
        return None, True
    lines = sample.splitlines()
    if not lines:
        return None, True
    dialect = common_csv_reader.detect_csv_dialect(sample, delimiters=",;\t")
    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_DOWNLOAD_HEADERS_EN: List[str] = [
    "CLASS CODE",
    "DESCRIPTION (SPANISH)",
    "DESCRIPTION (ENGLISH)",
    "MATERIAL TYPE",
    "COMMERCIAL UOM",
    "MX FRACTION",
    "US FRACTION",
    "DEPRECIATION RATE",
    "PHYSICAL REVIEW (1/0)",
    "PRODUCT/SERVICE CP CODE",
]

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"]},
    ],
}

inject_en_labels_into_template_columns(TEMPLATE_COLUMNS)

# Mismos canónicos que parts (CLASE, FRACCION, …); restaurar etiquetas de catálogo de clases.
# Mismo texto que TEMPLATE_DOWNLOAD_HEADERS_EN (plantilla Clarion EN).
_MATERIAL_CLASS_LABEL_EN_OVERRIDES = {
    "CLASE": "CLASS CODE",
    "UNIMED": "COMMERCIAL UOM",
    "FRACCION": "MX FRACTION",
    "FRACCIONAME": "US FRACTION",
}
for col in TEMPLATE_COLUMNS.get("material_classes") or []:
    canon = col.get("canonical")
    if canon in _MATERIAL_CLASS_LABEL_EN_OVERRIDES:
        col.setdefault("labels", {})["en"] = _MATERIAL_CLASS_LABEL_EN_OVERRIDES[canon]

inject_max_char_hints_into_columns(
    TEMPLATE_COLUMNS.get("material_classes"), MATERIAL_CLASSES_CSV_MAX_CHARS
)
for _mc in TEMPLATE_COLUMNS.get("material_classes") or []:
    if _mc.get("canonical") == "REVFISICA":
        merge_enum_hint_before_max(_mc, ["1", "0"])

_MC_DL_ORDER = [
    "CLASE",
    "DESCRIPCIONE",
    "DESCRIPCIONI",
    "CLAVEMAT",
    "UNIMED",
    "FRACCION",
    "FRACCIONAME",
    "TASADEPRECIA",
    "REVFISICA",
    "FRACCIONEXENTAIVA",
]
_MC_BY_CANON = {c["canonical"]: c for c in (TEMPLATE_COLUMNS.get("material_classes") or [])}
_MATERIAL_CLASSES_DOWNLOAD_DEFS = [dict(_MC_BY_CANON[k]) for k in _MC_DL_ORDER]


def download_headers_for_locale(locale: str) -> List[str]:
    loc = normalize_locale(locale)
    return [download_header_cell(d, loc) for d in _MATERIAL_CLASSES_DOWNLOAD_DEFS]


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
        merge_labels_into_lookup(item, canonical, normalize_header_fn, lookup)
        merge_download_header_into_lookup(item, canonical, normalize_header_fn, lookup)
    for es_h, en_h in zip(TEMPLATE_DOWNLOAD_HEADERS, TEMPLATE_DOWNLOAD_HEADERS_EN):
        if not es_h or not en_h:
            continue
        es_key = normalize_header_fn(es_h)
        canon = lookup.get(es_key)
        if canon:
            lookup[normalize_header_fn(en_h)] = canon
    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 puede venir colapsada.
            if "CLASE" not in out and value:
                val_str = cell_to_str(value)
                first_val = val_str
                for delimiter in (",", ";", "\t"):
                    if delimiter in first_val:
                        first_val = first_val.split(delimiter)[0]
                        break
                first_val = first_val.lstrip("\ufeff").strip()
                if first_val:
                    out["CLASE"] = first_val
    return out
