SEP = b'\x00'


class Namespace:
    def __init__(self, db, name: str):
        if not name:
            raise ValueError("Namespace name cannot be empty.")
        self._db     = db
        self._name   = name
        self._prefix = name.encode('utf-8') + SEP

    def _k(self, key) -> bytes:
        if isinstance(key, str):
            key = key.encode('utf-8')
        return self._prefix + key

    def _strip(self, raw_key: bytes) -> str:
        """Strip namespace prefix and return key as string."""
        return raw_key[len(self._prefix):].decode('utf-8')

    # ------------------------------------------------------------------
    # Typed API (serialized)
    # ------------------------------------------------------------------

    def set(self, key, value):
        self._db.set(self._k(key), value)

    def get(self, key):
        return self._db.get(self._k(key))

    # ------------------------------------------------------------------
    # Raw API (bytes in, bytes out — no serialization)
    # ------------------------------------------------------------------

    def create(self, key, value):
        self._db.create(self._k(key), value)

    def write(self, key, value):
        self._db.write(self._k(key), value)

    def read(self, key):
        return self._db.read(self._k(key))

    def update(self, key, value):
        self._db.update(self._k(key), value)

    def delete(self, key) -> bool:
        return self._db.delete(self._k(key))

    def exists(self, key) -> bool:
        return self._db.exists(self._k(key))

    # ------------------------------------------------------------------
    # Iteration (scoped to this namespace only)
    # ------------------------------------------------------------------

    def keys(self) -> list:
        return [
            self._strip(k)
            for k in self._db._engine._index
            if k.startswith(self._prefix)
        ]

    def items(self) -> list:
        result = []
        for k in self._db._engine._index:
            if k.startswith(self._prefix):
                val = self._db.get(k)
                result.append((self._strip(k), val))
        return result

    def values(self) -> list:
        return [v for _, v in self.items()]

    def count(self) -> int:
        return sum(1 for k in self._db._engine._index if k.startswith(self._prefix))

    def clear(self):
        keys = [k for k in self._db._engine._index if k.startswith(self._prefix)]
        for k in keys:
            self._db._engine.tombstone(k)

    def batch_set(self, items: dict):
        mapped = {self._k(k): v for k, v in items.items()}
        self._db.batch_set(mapped)

    def batch_delete(self, keys: list) -> int:
        return self._db.batch_delete([self._k(k) for k in keys])

    def __repr__(self):
        return f"Namespace(name={self._name!r}, keys={self.count()})"