from __future__ import annotations

import hashlib
from pathlib import Path

from app.soat_tariffs.domain.models import SoatCatalog, SoatCatalogEntry


DEFAULT_CATALOG_DIR = Path(__file__).resolve().parents[1] / "catalogs"


class JsonSoatTariffCatalog:
    def __init__(self, directory: str | Path = DEFAULT_CATALOG_DIR) -> None:
        self.directory = Path(directory)
        self._cache: dict[int, SoatCatalog] = {}
        self._indexes: dict[int, dict[str, SoatCatalogEntry]] = {}

    def _path(self, year: int) -> Path:
        return self.directory / f"soat-{year}.json"

    def load(self, year: int) -> SoatCatalog:
        if year in self._cache:
            return self._cache[year]
        path = self._path(year)
        if not path.is_file():
            raise FileNotFoundError(f"Catalogo SOAT {year} no disponible")
        try:
            catalog = SoatCatalog.model_validate_json(path.read_text(encoding="utf-8"))
        except Exception as exc:
            raise ValueError(f"Catalogo SOAT {year} corrupto") from exc
        if catalog.year != year:
            raise ValueError(f"La vigencia interna no coincide con {year}")
        if len(catalog.entries) != 1771:
            raise ValueError(f"El catalogo SOAT {year} no contiene 1.771 procedimientos")
        groups = {entry.surgical_group for entry in catalog.entries}
        if groups != {*range(2, 14), *range(20, 24)}:
            raise ValueError(f"El catalogo SOAT {year} no contiene los 16 grupos quirurgicos")
        self._validate_sources(catalog)
        self._cache[year] = catalog
        self._indexes[year] = {entry.code: entry for entry in catalog.entries}
        return catalog

    def _validate_sources(self, catalog: SoatCatalog) -> None:
        for source in catalog.generated_from:
            if not source.local_file:
                continue
            source_path = self.directory.parents[2] / source.local_file
            if not source_path.is_file():
                raise ValueError(f"Fuente local ausente: {source.local_file}")
            digest = hashlib.sha256(source_path.read_bytes()).hexdigest()
            if digest != source.sha256:
                raise ValueError(f"Hash invalido para fuente: {source.local_file}")

    def find_exact(self, year: int, code: str) -> SoatCatalogEntry | None:
        raw = str(code).strip()
        if not raw.isdigit() or len(raw) not in {4, 5}:
            return None
        normalized = raw.zfill(5)
        self.load(year)
        return self._indexes[year].get(normalized)

    def validate_all(self) -> dict[int, str]:
        expected = range(2022, 2027)
        results: dict[int, str] = {}
        for year in expected:
            self._cache.pop(year, None)
            self._indexes.pop(year, None)
            self.load(year)
            results[year] = "ready"
        return results
