from anzar._models.anzar_config import AnzarConfig, AuthStrategy
from anzar._models.options import SdkOptions
from generated.openapi_client import (
    ApiClient,
    AuthApi,
    Configuration,
    EmailApi,
    UsersApi,
)
from generated.openapi_client.models import SessionTokens

from urllib.parse import urlparse


class RefreshingApiClient(ApiClient):
    def __init__(
        self, configuration=None, header_name=None, header_value=None, cookie=None
    ):
        super().__init__(configuration, header_name, header_value, cookie)
        self.refresh_callback = None
        self.access_callback = None

    def call_api(self, *args, **kwargs):
        path = args[1] if len(args) > 0 else None
        resource_path = urlparse(path).path
        is_refresh_endpoint = resource_path == "/auth/refresh-token"

        if not is_refresh_endpoint and self.access_callback:
            access_token = self.access_callback()
            args[2]["authorization"] = f"Bearer {access_token}"

        try:
            response = super().call_api(*args, **kwargs)
            response.read()

            if (
                response.status == 401
                and not is_refresh_endpoint
                and self.refresh_callback
            ):
                tokens = self.refresh_callback()
                # TODO use self.access_callback()

                # 2. Update the configuration with the new token
                self.configuration.access_token = tokens.access

                # 3. Update the headers for the current retry
                args[2]["authorization"] = f"Bearer {tokens.access}"

                # 4. Retry the call once
                response = super().call_api(*args, **kwargs)
                response.read()

            return response
        except Exception as _:
            return super().call_api(*args, **kwargs)


class Anzar:
    def __init__(self, anzar_config: AnzarConfig, options: SdkOptions | None = None):
        self._config = anzar_config
        self._options = options

        self._configuration = Configuration(host=anzar_config.api_url)
        # self._client = ApiClient(configuration=self._configuration)

        self._client = RefreshingApiClient(configuration=self._configuration)
        self._client.access_callback = (
            self._options.get_token if self._options else None
        )
        self._client.refresh_callback = self._handle_token_refresh

        self._auth_api = AuthApi(self._client)
        self._users_api = UsersApi(self._client)
        self._email_api = EmailApi(self._client)

        self.Auth = self._build_auth()
        self.User = self._users_api
        self.Email = self._email_api

    def _handle_token_refresh(self) -> SessionTokens | None:
        if not self._options:
            return None

        try:
            refresh_token = self._options.get_refresh_token()
            response = self._auth_api.refresh_token(
                _headers={"x-refresh-token": f"Bearer {refresh_token}"}
            )

            tokens: SessionTokens | None = None
            if response and response.tokens:
                tokens = response.tokens
                if self._options.on_token_refresh:
                    self._options.on_token_refresh(tokens)

            return tokens
        except Exception as _:
            if self._options.on_session_expired:
                self._options.on_session_expired()

    def _build_auth(self):
        import types

        async def is_authenticated():
            try:
                response = self._users_api.find_user()
                return response is not None
            except Exception:
                return False

        def _logout(self):
            self._auth_api.logout()
            if self._options and self._options.on_logout:
                self._options.on_logout()

        auth = types.SimpleNamespace(
            login=lambda body: self._auth_api.login(body),
            register=lambda body: self._auth_api.register(body),
            logout=lambda: _logout,
            reset_password=lambda body: self._auth_api.request_password_reset(body),
            is_authenticated=is_authenticated,
        )

        if self._config.auth and self._config.auth.strategy == AuthStrategy.Session:
            auth.session = lambda: self._auth_api.get_session()

        return auth


__all__ = ["Anzar"]
