"""
Authorization Code grant, the standard user-facing OAuth2 flow.
"""

from __future__ import annotations

import httpx

from fortytwo.core.auth.providers.provider import AuthProvider, TokenRequest
from fortytwo.core.config import FORTYTWO_REQUEST_ENDPOINT_AUTHORIZE


class AuthorizationCodeProvider(AuthProvider):
    """
    Builds token request payloads for the OAuth2 **authorization_code** grant.

    This provider exchanges an authorization code (obtained after the user
    authorizes your application) for an access token / refresh token pair.

    The typical flow is:

    1. Redirect the user to :func:`authorize_url` so they can grant access.
    2. Receive the ``code`` query-parameter on your ``redirect_uri``.
    3. Pass the code to this provider and let the authenticator exchange it.

    Args:
        client_id: The 42 API application UID.
        client_secret: The 42 API application secret.
        code: The authorization code received from the redirect.
        redirect_uri: The redirect URI registered with the 42 API application.
        scopes: OAuth scopes to request.  Defaults to ``["public"]``.
    """

    def __init__(
        self,
        client_id: str,
        client_secret: str,
        code: str,
        redirect_uri: str,
        scopes: list[str] | None = None,
    ) -> None:
        self.__client_id = client_id
        self.__client_secret = client_secret
        self.__code = code
        self.__redirect_uri = redirect_uri
        self.__scopes = scopes or ["public"]

    @staticmethod
    def authorize_url(
        client_id: str,
        redirect_uri: str,
        scopes: list[str] | None = None,
        state: str | None = None,
        *,
        request_endpoint_authorize: str = FORTYTWO_REQUEST_ENDPOINT_AUTHORIZE,
    ) -> str:
        """
        Build the URL the user should visit to authorize your application.

        Args:
            client_id: The 42 API application UID.
            redirect_uri: The redirect URI registered with the 42 API application.
            scopes: OAuth scopes to request. Defaults to ``["public"]``.
            state: Optional state string to include in the authorization request.
            request_endpoint_authorize: Optional custom authorization endpoint URL.

        Returns:
            The full authorization URL as a string.
        """
        if not scopes:
            scopes = ["public"]

        params: dict[str, str] = {
            "client_id": client_id,
            "redirect_uri": redirect_uri,
            "response_type": "code",
            "scope": " ".join(scopes),
        }

        if state:
            params["state"] = state

        return str(httpx.URL(request_endpoint_authorize, params=params))

    def build_token_request(self) -> TokenRequest:
        return TokenRequest(
            data={
                "grant_type": "authorization_code",
                "client_id": self.__client_id,
                "client_secret": self.__client_secret,
                "code": self.__code,
                "redirect_uri": self.__redirect_uri,
                "scope": " ".join(self.__scopes),
            },
        )

    def build_refresh_request(self, refresh_token: str) -> TokenRequest:
        return TokenRequest(
            data={
                "grant_type": "refresh_token",
                "client_id": self.__client_id,
                "client_secret": self.__client_secret,
                "refresh_token": refresh_token,
            },
        )
