"""Taxonomy data from NASA Worldview's wv.json — disciplines, hazards, measurements.

Provides curated scientific categories for GIBS layers, extracted from
Worldview's ~51MB config and shipped as a compact static JSON file.
"""

import json
import logging
from importlib import resources

log = logging.getLogger(__name__)


class Taxonomy:
    """Lookup interface for Worldview's layer taxonomy.

    Organizes layers into science disciplines, hazard categories,
    measurements (with source instruments), and per-layer metadata
    (layergroup + search tags).
    """

    def __init__(self, data: dict) -> None:
        self._disciplines: dict[str, list[str]] = data.get("disciplines", {})
        self._hazards: dict[str, list[str]] = data.get("hazards", {})
        self._measurements: dict[str, dict] = data.get("measurements", {})
        self._layers: dict[str, dict] = data.get("layers", {})

        # Build reverse index: layer_id -> list of discipline names
        self._layer_to_disciplines: dict[str, list[str]] = {}
        for disc_name, meas_names in self._disciplines.items():
            for meas_name in meas_names:
                meas = self._measurements.get(meas_name, {})
                for source_layers in meas.get("sources", {}).values():
                    for layer_id in source_layers:
                        self._layer_to_disciplines.setdefault(layer_id, [])
                        if disc_name not in self._layer_to_disciplines[layer_id]:
                            self._layer_to_disciplines[layer_id].append(disc_name)

        # Build reverse index: layer_id -> list of hazard names
        self._layer_to_hazards: dict[str, list[str]] = {}
        for haz_name, meas_names in self._hazards.items():
            for meas_name in meas_names:
                meas = self._measurements.get(meas_name, {})
                for source_layers in meas.get("sources", {}).values():
                    for layer_id in source_layers:
                        self._layer_to_hazards.setdefault(layer_id, [])
                        if haz_name not in self._layer_to_hazards[layer_id]:
                            self._layer_to_hazards[layer_id].append(haz_name)

    @property
    def disciplines(self) -> dict[str, list[str]]:
        return self._disciplines

    @property
    def hazards(self) -> dict[str, list[str]]:
        return self._hazards

    @property
    def measurements(self) -> dict[str, dict]:
        return self._measurements

    def layergroup(self, layer_id: str) -> str | None:
        """Get the layergroup (scientific category) for a layer."""
        entry = self._layers.get(layer_id)
        return entry.get("g") if entry else None

    def tags(self, layer_id: str) -> str | None:
        """Get the search tags for a layer."""
        entry = self._layers.get(layer_id)
        return entry.get("t") if entry else None

    def layers_for_discipline(self, name: str) -> list[str]:
        """Get all layer IDs belonging to a science discipline (deduplicated)."""
        name_lower = name.lower()

        matched_disc = None
        for disc_name in self._disciplines:
            if disc_name.lower() == name_lower:
                matched_disc = disc_name
                break

        if matched_disc is None:
            return []

        # Use reverse index to avoid duplicates
        return [
            lid
            for lid, discs in self._layer_to_disciplines.items()
            if matched_disc in discs
        ]

    def layers_for_hazard(self, name: str) -> list[str]:
        """Get all layer IDs belonging to a hazard category (deduplicated)."""
        name_lower = name.lower()

        matched_haz = None
        for haz_name in self._hazards:
            if haz_name.lower() == name_lower:
                matched_haz = haz_name
                break

        if matched_haz is None:
            return []

        # Use reverse index to avoid duplicates
        return [
            lid
            for lid, hazs in self._layer_to_hazards.items()
            if matched_haz in hazs
        ]


def load_taxonomy() -> Taxonomy | None:
    """Load the bundled taxonomy.json.

    Returns None if the file is missing (e.g. hasn't been generated yet).
    """
    try:
        pkg = resources.files("mcgibs.data")
        taxonomy_file = pkg.joinpath("taxonomy.json")
        data = json.loads(taxonomy_file.read_text(encoding="utf-8"))
        tax = Taxonomy(data)
        log.info(
            "Loaded taxonomy: %d disciplines, %d hazards, %d measurements, %d layers",
            len(tax.disciplines),
            len(tax.hazards),
            len(tax.measurements),
            len(tax._layers),
        )
        return tax
    except (FileNotFoundError, ModuleNotFoundError) as exc:
        log.debug("Taxonomy not available: %s", exc)
        return None
    except json.JSONDecodeError as exc:
        log.warning("Taxonomy JSON is corrupted: %s", exc)
        return None
