# Copyright 2025 Softwell S.r.l.
# Licensed under the Apache License, Version 2.0

"""Session store — protocol and in-memory implementation."""

from __future__ import annotations

import secrets
from typing import Any, Protocol, runtime_checkable

from .session import Session

__all__ = ["SessionStore", "MemorySessionStore"]


@runtime_checkable
class SessionStore(Protocol):
    """Protocol for session storage backends."""

    def get(self, session_id: str) -> Session | None:
        """Retrieve session by id, or None if not found/expired."""
        ...

    def create(self, auth: dict[str, Any] | None = None) -> Session:
        """Create a new session with unique token."""
        ...

    def delete(self, session_id: str) -> None:
        """Remove session from store."""
        ...

    def dump(self) -> dict[str, Any]:
        """Serialize all sessions for persistence."""
        ...

    def restore(self, data: dict[str, Any]) -> None:
        """Restore sessions from serialized data."""
        ...


class MemorySessionStore:
    """In-memory session store. Default implementation.

    Attributes:
        _sessions: Dict mapping session_id to Session.
        _default_ttl: Default TTL in seconds for new sessions.
    """

    __slots__ = ("_sessions", "_default_ttl")

    def __init__(self, default_ttl: int = 3600) -> None:
        """Initialize store.

        Args:
            default_ttl: Default time-to-live in seconds for new sessions.
        """
        self._sessions: dict[str, Session] = {}
        self._default_ttl = default_ttl

    def get(self, session_id: str) -> Session | None:
        """Retrieve session by id. Returns None if not found or expired."""
        session = self._sessions.get(session_id)
        if session is None:
            return None
        if session.is_expired():
            del self._sessions[session_id]
            return None
        session.touch()
        return session

    def create(self, auth: dict[str, Any] | None = None) -> Session:
        """Create a new session with unique token.

        Args:
            auth: Auth dict snapshot to store in session.

        Returns:
            New Session instance.
        """
        session_id = secrets.token_urlsafe(32)
        session = Session(session_id=session_id, auth=auth, ttl=self._default_ttl)
        self._sessions[session_id] = session
        return session

    def delete(self, session_id: str) -> None:
        """Remove session from store."""
        self._sessions.pop(session_id, None)

    def dump(self) -> dict[str, Any]:
        """Serialize all sessions for persistence."""
        result: dict[str, Any] = {}
        for session_id, session in self._sessions.items():
            result[session_id] = {
                "meta": dict(session.meta),
                "auth": session.auth,
            }
        return result

    def restore(self, data: dict[str, Any]) -> None:
        """Restore sessions from serialized data.

        Args:
            data: Dict from dump() output.
        """
        for session_id, session_data in data.items():
            meta = session_data["meta"]
            session = Session(
                session_id=session_id,
                auth=session_data.get("auth"),
                ttl=meta["ttl"],
            )
            session._meta["created_at"] = meta["created_at"]  # noqa: SLF001
            session._meta["last_access"] = meta["last_access"]  # noqa: SLF001
            if not session.is_expired():
                self._sessions[session_id] = session


if __name__ == "__main__":
    pass
