"""C6 tile cache runtime error taxonomy (AZ-303).

All read-side errors raised by ``TileStore`` / ``TileMetadataStore`` /
``DescriptorIndex`` are subclasses of :class:`TileCacheError`. Consumers
catch the family; implementations rewrap third-party exceptions
(psycopg, FAISS C++, OS errors) into one of these types.

:class:`IndexBuildError` is intentionally NOT in the :class:`TileCacheError`
family — it is raised only by the offline ``rebuild_from_descriptors``
path (C10 pre-flight provisioning) and has different fault semantics
than the in-flight read envelope.
"""

from __future__ import annotations

__all__ = [
    "ContentHashMismatchError",
    "FreshnessRejectionError",
    "IndexBuildError",
    "IndexUnavailableError",
    "TileCacheError",
    "TileFsError",
    "TileMetadataError",
    "TileNotFoundError",
]


class TileCacheError(Exception):
    """Base class for the C6 read-side error family.

    Consumers MUST be able to catch the family with a single
    ``except TileCacheError:`` and recover or surface to FDR.
    """


class TileNotFoundError(TileCacheError):
    """A tile lookup did not find the requested ``TileId`` on disk.

    Distinct from :class:`TileMetadataError`: the metadata row is also
    absent, so the cache is in a consistent (just-empty) state.
    """


class TileFsError(TileCacheError):
    """An OS-level filesystem error escaped from a Protocol method.

    Always wraps the originating ``OSError`` via ``__cause__``. The
    impl MUST rewrap; raw ``OSError`` MUST NOT escape the Protocol.
    """


class TileMetadataError(TileCacheError):
    """The metadata store violated a consistency invariant.

    Includes: row present but JPEG missing (or vice-versa), duplicate
    composite-key insert, backward voting-status transition. See
    ``tile_metadata_store.md`` Invariants I-1, I-8 for the full list.
    """


class ContentHashMismatchError(TileCacheError):
    """``sha256(tile_blob) != metadata.content_sha256_hex`` on ``write_tile``.

    Bound to the cache-poisoning safety budget (D-C10-3 / AC-NEW-7);
    the impl MUST NOT silently retry — surface to the caller (and FDR)
    so the operator can investigate the source feed.
    """


class FreshnessRejectionError(TileCacheError):
    """The C6 freshness gate rejected an insert.

    Raised when the tile's ``(lat, lon)`` falls in an ``ACTIVE_CONFLICT``
    sector AND ``capture_timestamp < now() - active_conflict_max_age``.
    See ``tile_metadata_store.md`` Invariant I-2.
    """


class IndexUnavailableError(TileCacheError):
    """The descriptor index could not satisfy a read.

    Includes: mmap handle invalid, ``.index`` file missing, sidecar
    ``.sha256`` mismatched, query vector dimension does not match
    ``descriptor_dim()``. See ``descriptor_index.md`` Invariants
    I-3 / I-6.
    """


class IndexBuildError(Exception):
    """Offline index build failed.

    Intentionally NOT a :class:`TileCacheError` subclass — the build
    path is the C10 pre-flight provisioning envelope, distinct from
    the in-flight read envelope. C2 catches :class:`TileCacheError`;
    C10 catches :class:`IndexBuildError`.
    """
