import httpx
from litestar.exceptions import HTTPException
from pydantic import BaseModel, ConfigDict, Field

from huesoporro.models import TwitchAuth
from huesoporro.settings import Settings


class TwitchAuthenticator(BaseModel):
    s: Settings = Field(default_factory=Settings.get)
    client: httpx.AsyncClient = Field(
        default_factory=lambda x: httpx.AsyncClient(base_url="https://id.twitch.tv/")
    )
    model_config = ConfigDict(arbitrary_types_allowed=True)

    async def get_token(self, code: str, auto_refresh: bool = True) -> TwitchAuth:
        response = await self.client.post(
            "/oauth2/token",
            data={
                "client_id": self.s.twitch_client_id,
                "client_secret": self.s.twitch_client_secret.get_secret_value(),
                "grant_type": "authorization_code",
                "code": code,
                "redirect_uri": f"{self.s.server_hostname}o/code",
            },
            headers={"Accept": "application/json"},
        )

        if auto_refresh and response.status_code == 401:  # noqa: PLR2004
            return await self.refresh_token(response.json()["refresh_token"])

        response.raise_for_status()
        profile = await self.get_userinfo(response.json()["access_token"])
        return TwitchAuth(**response.json(), userinfo=profile)

    async def get_userinfo(self, access_token):
        response = await self.client.get(
            "/oauth2/userinfo", headers={"Authorization": f"Bearer {access_token}"}
        )
        response.raise_for_status()
        return response.json()

    async def refresh_token(self, refresh_token: str) -> TwitchAuth:
        response = await self.client.post(
            "/oauth2/token",
            data={
                "client_id": Settings.get().twitch_client_id,
                "client_secret": Settings.get().twitch_client_secret.get_secret_value(),
                "grant_type": "refresh_token",
                "refresh_token": refresh_token,
            },
            headers={"Accept": "application/json"},
        )
        response.raise_for_status()
        profile = await self.get_userinfo(response.json()["access_token"])
        return TwitchAuth(**response.json(), userinfo=profile)

    async def validate_token(self, access_token: str) -> str:
        response = await self.client.get(
            "/oauth2/validate", headers={"Authorization": f"OAuth {access_token}"}
        )
        response.raise_for_status()
        user_data = response.json()

        if user_data.get("status"):
            raise HTTPException(status_code=401, detail="Unauthorized")

        if (user := user_data["login"]) not in self.s.allowed_users:
            raise HTTPException(status_code=403, detail="Forbidden")

        return user

    async def token_is_valid(self, access_token: str) -> bool:
        response = await self.client.get(
            "/oauth2/validate", headers={"Authorization": f"OAuth {access_token}"}
        )
        return response.status_code == 200  # noqa: PLR2004
