"""SQLAlchemy implementation of UserRepository."""

from __future__ import annotations

from uuid import UUID

from sqlalchemy import select

from noteflow.domain.identity import User
from noteflow.infrastructure.persistence.models import DEFAULT_USER_ID, UserModel
from noteflow.infrastructure.persistence.repositories._base import (
    BaseRepository,
    DeleteByIdMixin,
    GetByIdMixin,
)


class SqlAlchemyUserRepository(
    BaseRepository,
    GetByIdMixin[UserModel, User],
    DeleteByIdMixin[UserModel],
):
    """SQLAlchemy implementation of UserRepository.

    Manage user accounts for local-first identity management.
    Uses mixins for standardized get and delete operations.
    """

    _model_class: type[UserModel] = UserModel

    def _to_domain(self, model: UserModel) -> User:
        """Convert ORM model to domain entity.

        Args:
            model: SQLAlchemy UserModel.

        Returns:
            Domain User entity.
        """
        return User(
            id=model.id,
            display_name=model.display_name,
            email=model.email,
            is_default=(str(model.id) == DEFAULT_USER_ID),
            created_at=model.created_at,
            updated_at=model.updated_at,
            metadata=dict(model.metadata_) if model.metadata_ else {},
        )

    @staticmethod
    def _to_model(entity: User) -> UserModel:
        """Convert domain entity to ORM model.

        Args:
            entity: Domain User entity.

        Returns:
            SQLAlchemy UserModel.
        """
        return UserModel(
            id=entity.id,
            display_name=entity.display_name,
            email=entity.email,
            created_at=entity.created_at,
            updated_at=entity.updated_at,
            metadata_=entity.metadata,
        )

    async def get(self, user_id: UUID) -> User | None:
        """Get user by ID.

        Args:
            user_id: User UUID.

        Returns:
            User if found, None otherwise.
        """
        return await self._mixin_get_by_id(user_id)

    async def get_by_email(self, email: str) -> User | None:
        """Get user by email address.

        Args:
            email: User email.

        Returns:
            User if found, None otherwise.
        """
        stmt = select(UserModel).where(UserModel.email == email)
        model = await self._execute_scalar(stmt)
        return self._to_domain(model) if model else None

    async def get_default(self) -> User | None:
        """Get the default local user.

        Returns:
            Default user if exists, None otherwise.
        """
        default_id = UUID(DEFAULT_USER_ID)
        return await self.get(default_id)

    async def create(self, user: User) -> User:
        """Create a new user.

        Args:
            user: User to create.

        Returns:
            Created user.
        """
        model = self._to_model(user)
        await self._add_and_flush(model)
        return self._to_domain(model)

    async def create_default(
        self,
        user_id: UUID,
        display_name: str,
        email: str | None = None,
    ) -> User:
        """Create the default local user.

        Args:
            user_id: UUID for the new user (should be DEFAULT_USER_ID).
            display_name: Display name for the user.
            email: Optional email address.

        Returns:
            Created default user.
        """
        model = UserModel(
            id=user_id,
            display_name=display_name,
            email=email,
            metadata_={},
        )
        await self._add_and_flush(model)
        return self._to_domain(model)

    async def update(self, user: User) -> User:
        """Update an existing user.

        Args:
            user: User with updated fields.

        Returns:
            Updated user.

        Raises:
            ValueError: If user does not exist.
        """
        stmt = select(UserModel).where(UserModel.id == user.id)
        model = await self._execute_scalar(stmt)

        if model is None:
            msg = f"User {user.id} not found"
            raise ValueError(msg)

        model.display_name = user.display_name
        model.email = user.email
        model.metadata_ = user.metadata

        await self._session.flush()
        return self._to_domain(model)

    async def delete(self, user_id: UUID) -> bool:
        """Delete a user.

        Args:
            user_id: User UUID.

        Returns:
            True if deleted, False if not found.
        """
        return await self._mixin_delete_by_id(user_id)
