from uuid import UUID

from loguru import logger
from pydantic import BaseModel

from huesoporro import utils
from huesoporro.infra.authenticator import TwitchAuthenticator
from huesoporro.infra.repos import UserRepo
from huesoporro.models import TwitchAuth, User
from huesoporro.settings import Settings


class CreateUserSvc(BaseModel):
    user_repo: UserRepo

    async def run(self, user: User) -> User:
        """Create a new user in the system

        Args:
            user: User object to be created

        Returns:
            The created User with any system-generated fields populated
        """
        return await self.user_repo.create(user)


class UpdateUserSvc(BaseModel):
    user_repo: UserRepo

    async def run(self, user: User) -> User:
        """Update an existing user in the system

        Args:
            user: User object with updated fields

        Returns:
            The updated User

        Raises:
            ValueError: If the user doesn't exist
        """
        user.last_updated_at = utils.get_utc_now()
        return await self.user_repo.update(user)


class DeleteUserSvc(BaseModel):
    user_repo: UserRepo

    async def run(self, user: User) -> None:
        """Delete a user from the system

        Args:
            user: User object to be deleted
        """
        await self.user_repo.delete(user)


class GetUserByIdSvc(BaseModel):
    user_repo: UserRepo

    async def run(self, user_id: UUID) -> User | None:
        """Retrieve a user by their ID

        Args:
            user_id: UUID of the user to retrieve

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


class GetUserByUsernameSvc(BaseModel):
    user_repo: UserRepo

    async def run(self, username: str) -> User | None:
        """Retrieve a user by their username

        Args:
            username: Username of the user to retrieve

        Returns:
            User object if found, None otherwise
        """
        return await self.user_repo.get_by_username(username)


class IsValidTokenSvc(BaseModel):
    authenticator: TwitchAuthenticator

    async def run(self, user: User) -> bool:
        return await self.authenticator.token_is_valid(user.twitch_access_token)


class RefreshTokenSvc(BaseModel):
    twitch_authenticator: TwitchAuthenticator

    async def run(self, user: User) -> User:
        """Refresh a user's Twitch token

        Args:
            user: User object with the Twitch token to be refreshed

        Returns:
            The updated User with the refreshed Twitch token
        """

        logger.info(f"Refreshing token for user {user}")
        twitch_auth = await self.twitch_authenticator.refresh_token(
            user.twitch_refresh_token
        )
        user.twitch_access_token = twitch_auth.access_token  # type: ignore[misc]
        user.twitch_refresh_token = twitch_auth.refresh_token  # type: ignore[misc]
        return user


class GetTwitchAuthByAuthCodeSvc(BaseModel):
    authenticator: TwitchAuthenticator
    s: Settings

    async def run(self, auth_code: str) -> TwitchAuth:
        auth = await self.authenticator.get_token(auth_code)
        username = auth.userinfo["preferred_username"]
        if username not in self.s.allowed_users:
            raise ValueError(f"User {username} is not allowed to use this bot")
        return auth
