"""C2 VPR strategy config block (AZ-336).

Registered into ``config.components['c2_vpr']`` by the package
``__init__.py``. The composition-root factory
:func:`gps_denied_onboard.runtime_root.vpr_factory.build_vpr_strategy`
reads this block to select the strategy and locate the backbone
weights + FAISS index sidecar.

``backbone_weights_path`` and ``faiss_index_path`` are required (no
default — paths are deployment-specific). They are typed
:class:`pathlib.Path` so YAML loaders that emit strings get coerced
at construction; ``__post_init__`` validates that both are non-empty.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from pathlib import Path
from typing import Final

from gps_denied_onboard.config.schema import ConfigError

__all__ = [
    "C2VprConfig",
    "KNOWN_STRATEGIES",
]

KNOWN_STRATEGIES: Final[frozenset[str]] = frozenset(
    {
        "ultra_vpr",
        "net_vlad",
        "mega_loc",
        "mix_vpr",
        "sela_vpr",
        "eigen_places",
        "salad",
    }
)


@dataclass(frozen=True)
class C2VprConfig:
    """Per-component config for C2 VPR.

    ``strategy`` selects exactly one of the seven backbones
    (see :data:`KNOWN_STRATEGIES`); the composition-root factory
    respects compile-time ``BUILD_VPR_<variant>`` gating on top of
    this label.

    ``backbone_weights_path`` is the on-disk location of the
    backbone weights (TRT engine, ONNX model, PyTorch state dict —
    per strategy). ``faiss_index_path`` is the location of the
    pre-built FAISS HNSW index file (C6 ``DescriptorIndex`` reads
    its sidecar there).
    """

    strategy: str = "net_vlad"
    backbone_weights_path: Path = field(default_factory=lambda: Path("/models/vpr/weights"))
    faiss_index_path: Path = field(default_factory=lambda: Path("/cache/vpr/index.faiss"))

    def __post_init__(self) -> None:
        if self.strategy not in KNOWN_STRATEGIES:
            raise ConfigError(
                f"C2VprConfig.strategy={self.strategy!r} not in "
                f"{sorted(KNOWN_STRATEGIES)}"
            )
        if not isinstance(self.backbone_weights_path, Path):
            object.__setattr__(
                self, "backbone_weights_path", Path(self.backbone_weights_path)
            )
        if not isinstance(self.faiss_index_path, Path):
            object.__setattr__(
                self, "faiss_index_path", Path(self.faiss_index_path)
            )
        if not str(self.backbone_weights_path):
            raise ConfigError(
                "C2VprConfig.backbone_weights_path must be non-empty"
            )
        if not str(self.faiss_index_path):
            raise ConfigError(
                "C2VprConfig.faiss_index_path must be non-empty"
            )
