"""C6 tile cache config block (AZ-303).

Registered into ``config.components['c6_tile_cache']`` by the package
``__init__.py``. The composition-root factories
(``build_tile_store`` / ``build_tile_metadata_store`` /
``build_descriptor_index``) read this block to select the runtime and
locate the persistent root.
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Final

from gps_denied_onboard.config.schema import ConfigError

__all__ = [
    "C6TileCacheConfig",
    "KNOWN_DESCRIPTOR_INDEX_RUNTIMES",
    "KNOWN_METADATA_RUNTIMES",
    "KNOWN_TILE_STORE_RUNTIMES",
]

KNOWN_TILE_STORE_RUNTIMES: Final[frozenset[str]] = frozenset({"postgres_filesystem"})
KNOWN_METADATA_RUNTIMES: Final[frozenset[str]] = frozenset({"postgres_filesystem"})
KNOWN_DESCRIPTOR_INDEX_RUNTIMES: Final[frozenset[str]] = frozenset({"faiss_hnsw"})


@dataclass(frozen=True)
class C6TileCacheConfig:
    """Per-component config for C6 tile cache.

    ``store_runtime`` / ``metadata_runtime`` are currently both
    ``"postgres_filesystem"`` (single concrete impl); the field
    exists so a future SQLite Tier-0 dev runtime can be added
    without rippling the contract.

    ``descriptor_index_runtime`` selects the vector index strategy
    (today only ``"faiss_hnsw"``; gated by ``BUILD_FAISS_INDEX``).

    ``root_dir`` is the filesystem root for tile JPEGs +
    ``.index`` sidecars; layout is byte-identical to
    ``satellite-provider`` (Invariant I-1 of ``tile_store.md``).

    ``postgres_dsn`` is the PostgreSQL connection string the
    metadata-store impl uses (e.g. ``postgresql://...``); empty
    string means "use the runtime ``db_url`` from ``RuntimeConfig``"
    (the impl will resolve at build time).

    ``lru_eviction_threshold_bytes`` is the disk-budget high-water
    mark the cache-budget enforcer (separate task) uses to trigger
    LRU eviction; default 10 GiB per the C6 description.
    """

    store_runtime: str = "postgres_filesystem"
    metadata_runtime: str = "postgres_filesystem"
    descriptor_index_runtime: str = "faiss_hnsw"
    root_dir: str = "/var/lib/gps-denied/tiles"
    postgres_dsn: str = ""
    lru_eviction_threshold_bytes: int = 10 * 1024**3

    def __post_init__(self) -> None:
        if self.store_runtime not in KNOWN_TILE_STORE_RUNTIMES:
            raise ConfigError(
                f"C6TileCacheConfig.store_runtime={self.store_runtime!r} not in "
                f"{sorted(KNOWN_TILE_STORE_RUNTIMES)}"
            )
        if self.metadata_runtime not in KNOWN_METADATA_RUNTIMES:
            raise ConfigError(
                f"C6TileCacheConfig.metadata_runtime={self.metadata_runtime!r} not in "
                f"{sorted(KNOWN_METADATA_RUNTIMES)}"
            )
        if self.descriptor_index_runtime not in KNOWN_DESCRIPTOR_INDEX_RUNTIMES:
            raise ConfigError(
                f"C6TileCacheConfig.descriptor_index_runtime="
                f"{self.descriptor_index_runtime!r} not in "
                f"{sorted(KNOWN_DESCRIPTOR_INDEX_RUNTIMES)}"
            )
        if not self.root_dir:
            raise ConfigError("C6TileCacheConfig.root_dir must be non-empty")
        if self.lru_eviction_threshold_bytes <= 0:
            raise ConfigError(
                f"C6TileCacheConfig.lru_eviction_threshold_bytes must be > 0; "
                f"got {self.lru_eviction_threshold_bytes}"
            )
