"""Type stubs and typed wrappers for FoundationDB Python bindings.

The ``foundationdb`` package (v7.3.x) ships without ``py.typed`` or ``.pyi``
files, so type checkers cannot resolve its internal types.  This module
re-exports the concrete runtime classes under a single import and provides
``Protocol`` definitions for the handful of duck-typed objects (``Value``,
``KeyValue``) that the storage layer touches.

Usage::

    from __future__ import annotations
    from typing import TYPE_CHECKING
    if TYPE_CHECKING:
        from matyan_backend.fdb_types import Database, DirectorySubspace, Transaction

At runtime these are the real FDB classes; the protocols below exist only to
give type checkers something to work with when the runtime classes have no
stubs.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Any, Concatenate, Protocol, runtime_checkable

import fdb as _fdb

# ---------------------------------------------------------------------------
# Re-exports of concrete runtime types (usable in TYPE_CHECKING blocks)
# ---------------------------------------------------------------------------
# These imports will resolve at type-check time because the fdb package IS
# installed — the problem is that it lacks stubs, not that it's missing.
from fdb.directory_impl import DirectoryLayer, DirectorySubspace
from fdb.impl import Database, FDBError, Transaction

if TYPE_CHECKING:
    from collections.abc import Callable, Iterator

# ---------------------------------------------------------------------------
# Typed wrapper for fdb.transactional
# ---------------------------------------------------------------------------


def transactional[**P, R](
    func: Callable[Concatenate[Transaction, P], R],
) -> Callable[Concatenate[Database | Transaction, P], R]:
    """Type-preserving wrapper around ``fdb.transactional``.

    The real decorator replaces the first ``Transaction`` parameter so that
    callers may pass a ``Database`` (the decorator creates and commits the
    transaction automatically) or a ``Transaction`` (passed through as-is).
    """
    return _fdb.transactional(func)


# ---------------------------------------------------------------------------
# Protocols for duck-typed FDB objects
# ---------------------------------------------------------------------------


@runtime_checkable
class Value(Protocol):
    """Result of ``tr[key]`` — a lazy future that resolves to bytes."""

    def present(self) -> bool: ...
    def __bytes__(self) -> bytes: ...


@runtime_checkable
class KeyValue(Protocol):
    """Element yielded by ``tr.get_range()``."""

    key: bytes
    value: bytes


class FDBRange(Protocol):
    """Return type of ``subspace.range()`` — a ``slice``-like object."""

    start: bytes
    stop: bytes


class Subspace(Protocol):
    """Minimal protocol for FDB Subspace / DirectorySubspace key operations."""

    def pack(self, t: tuple = ()) -> bytes: ...
    def unpack(self, key: bytes) -> tuple: ...
    def range(self, t: tuple = ()) -> FDBRange: ...
    def key(self) -> bytes: ...
    def contains(self, key: bytes) -> bool: ...


class TransactionRead(Protocol):
    """Read-side of an FDB transaction (both Transaction and snapshot)."""

    def get(self, key: bytes) -> Value: ...
    def get_range(
        self,
        begin: bytes,
        end: bytes,
        limit: int = 0,
        reverse: bool = False,
        streaming_mode: int = -1,
    ) -> Iterator[KeyValue]: ...
    def __getitem__(self, key: bytes | slice) -> Any: ...  # noqa: ANN401


__all__ = [
    "Database",
    "DirectoryLayer",
    "DirectorySubspace",
    "FDBError",
    "FDBRange",
    "KeyValue",
    "Subspace",
    "Transaction",
    "TransactionRead",
    "Value",
    "transactional",
]
