"""SQLAlchemy implementation of PreferencesRepository."""

from __future__ import annotations

from collections.abc import Sequence
from dataclasses import dataclass
from datetime import datetime

from sqlalchemy import func, select

from noteflow.infrastructure.persistence.models import UserPreferencesModel
from noteflow.infrastructure.persistence.repositories._base import BaseRepository


@dataclass(frozen=True)
class PreferenceWithMetadata:
    """Preference value with sync metadata."""

    key: str
    value: object
    updated_at: datetime


class SqlAlchemyPreferencesRepository(BaseRepository):
    """SQLAlchemy implementation of PreferencesRepository.

    Provides key-value storage for user preferences. Values are stored as JSONB
    for flexibility while maintaining type-safe retrieval.
    """

    async def _get_by_key(self, key: str) -> UserPreferencesModel | None:
        """Get preference model by key.

        Args:
            key: Preference key.

        Returns:
            UserPreferencesModel or None if not found.
        """
        stmt = select(UserPreferencesModel).where(UserPreferencesModel.key == key)
        return await self._execute_scalar(stmt)

    async def get(self, key: str) -> object | None:
        """Get a preference value by key.

        Args:
            key: Preference key.

        Returns:
            Preference value or None if not found.
        """
        model = await self._get_by_key(key)
        return None if model is None else model.value.get("value")

    async def get_bool(self, key: str, default: bool = False) -> bool:
        """Get a boolean preference.

        Args:
            key: Preference key.
            default: Default value if not found.

        Returns:
            Boolean preference value.
        """
        value = await self.get(key)
        return default if value is None else bool(value)

    async def set(self, key: str, value: object) -> None:
        """Set a preference value.

        Args:
            key: Preference key.
            value: Preference value (must be JSON-serializable).
        """
        model = await self._get_by_key(key)

        if model is None:
            model = UserPreferencesModel(key=key, value={"value": value})
            self._session.add(model)
        else:
            model.value = {"value": value}

        await self._session.flush()

    async def delete(self, key: str) -> bool:
        """Delete a preference.

        Args:
            key: Preference key.

        Returns:
            True if deleted, False if not found.
        """
        model = await self._get_by_key(key)

        if model is None:
            return False

        await self._delete_and_flush(model)
        return True

    async def get_all(self, keys: list[str] | None = None) -> dict[str, object]:
        """Get all preferences, optionally filtered by keys.

        Args:
            keys: Optional list of keys to filter by. If None, returns all.

        Returns:
            Dictionary of key-value pairs.
        """
        stmt = select(UserPreferencesModel)
        if keys:
            stmt = stmt.where(UserPreferencesModel.key.in_(keys))

        models = await self._execute_scalars(stmt)
        return {m.key: m.value.get("value") for m in models}

    async def get_all_with_metadata(
        self,
        keys: Sequence[str] | None = None,
    ) -> list[PreferenceWithMetadata]:
        """Get all preferences with sync metadata.

        Args:
            keys: Optional list of keys to filter by.

        Returns:
            List of PreferenceWithMetadata objects.
        """
        stmt = select(UserPreferencesModel)
        if keys:
            stmt = stmt.where(UserPreferencesModel.key.in_(keys))

        models = await self._execute_scalars(stmt)
        return [
            PreferenceWithMetadata(
                key=m.key,
                value=m.value.get("value"),
                updated_at=m.updated_at,
            )
            for m in models
        ]

    async def get_max_updated_at(self) -> datetime | None:
        """Get the maximum updated_at timestamp across all preferences.

        Returns:
            Maximum updated_at or None if no preferences exist.
        """
        stmt = select(func.max(UserPreferencesModel.updated_at))
        result = await self._session.execute(stmt)
        return result.scalar_one_or_none()

    async def set_bulk(self, preferences: dict[str, object]) -> None:
        """Set multiple preferences at once.

        Args:
            preferences: Dictionary of key-value pairs to set.
        """
        for key, value in preferences.items():
            await self.set(key, value)
