import os
import unicodedata
from collections import defaultdict
from typing import Dict, List, Optional

from .exceptions import ComuneNotFoundError
from .loader import DEFAULT_CSV_PATH, load_comuni_data


def _normalize(value: str) -> str:
    """Rende i confronti case/accents insensitive."""
    if not isinstance(value, str):
        return ""
    normalized = unicodedata.normalize("NFKD", value).encode("ascii", "ignore").decode("ascii")
    return normalized.lower().strip()


class Comuni:
    def __init__(self, csv_file: Optional[str] = None, *, allow_download: bool = True, timeout: int = 10):
        """
        :param csv_file: Percorso del CSV da utilizzare (di default quello distribuito con il pacchetto).
        :param allow_download: Se True aggiorna/scarica il CSV ISTAT quando necessario.
        :param timeout: Timeout per le richieste HTTP durante l'eventuale download.
        """
        self.csv_file = csv_file or DEFAULT_CSV_PATH
        records = load_comuni_data(self.csv_file, allow_download=allow_download, timeout=timeout)

        self._records = records
        self._by_name: Dict[str, List[Dict[str, str]]] = defaultdict(list)
        self._by_codice: Dict[str, Dict[str, str]] = {}
        self._by_provincia: Dict[str, List[Dict[str, str]]] = defaultdict(list)
        self._build_indexes()

    def _build_indexes(self) -> None:
        """Pre-calcola indici per ricerche rapide."""
        for record in self._records:
            normalized_name = _normalize(record.get("denominazione_italiana", ""))
            if normalized_name:
                self._by_name[normalized_name].append(record)

            codice_catastale = record.get("codice_catastale", "").upper()
            if codice_catastale:
                self._by_codice[codice_catastale] = record

            sigla = record.get("sigla_provincia", "").upper()
            if sigla:
                self._by_provincia[sigla].append(record)

        # Mantiene un ordinamento prevedibile nelle liste
        for records in self._by_provincia.values():
            records.sort(key=lambda item: _normalize(item.get("denominazione_italiana", "")))

    @staticmethod
    def _essential_data(comune: Dict[str, str]) -> Dict[str, str]:
        return {
            "nome": comune.get("denominazione_italiana", ""),
            "provincia": comune.get("provincia", ""),
            "sigla_provincia": comune.get("sigla_provincia", ""),
            "regione": comune.get("regione", ""),
            "codice_catastale": comune.get("codice_catastale", ""),
        }

    def cerca_comune(self, nome_comune: str) -> Dict[str, str]:
        """Cerca un comune per nome e restituisce solo i dati essenziali."""
        normalized = _normalize(nome_comune)
        risultati = self._by_name.get(normalized, [])
        if not risultati:
            raise ComuneNotFoundError(f"Il comune '{nome_comune}' non è stato trovato.")

        return self._essential_data(risultati[0])

    def comuni_per_provincia(self, sigla_provincia: str) -> List[Dict[str, str]]:
        """Restituisce tutti i comuni di una provincia con dati essenziali."""
        risultati = self._by_provincia.get(sigla_provincia.upper(), [])
        return [self._essential_data(record) for record in risultati]

    def cerca_per_codice_catastale(self, codice_catastale: str) -> Dict[str, str]:
        """Cerca un comune per codice catastale e restituisce solo i dati essenziali."""
        comune = self._by_codice.get(codice_catastale.upper())
        if not comune:
            raise ComuneNotFoundError(f"Nessun comune trovato con il codice catastale '{codice_catastale}'.")

        return self._essential_data(comune)
