"""
QuestraAuthentication - Simplified interface for Questra API authentication
"""

from .authentication import (
    OAuth2Authentication,
    OAuth2InteractiveUserCredential,
    OAuth2ServiceCredential,
    OidcConfig,
    OidcDiscoveryClient,
)


class QuestraAuthentication:
    """
    Main client for Questra API access with automatic OAuth2 authentication.

    Supports two authentication modes:
    1. Service Account (username/password)
    2. Interactive User (Device Code Flow)

    The access token is automatically renewed when it expires.
    """

    def __init__(
        self,
        url: str | list[str],
        client_id: str = "Questra",
        username: str | None = None,
        password: str | None = None,
        interactive: bool = False,
        scope: str | None = None,
        oidc_discovery_paths: list[str] | None = None,
    ):
        """
        Initialize the QuestraAuthentication.

        Args:
            url: Base URL of the Identity Provider or List of URLs
            client_id: OAuth2 Client ID (default: "Questra")
            username: Username for Service Account authentication
            password: Password for Service Account authentication
            interactive: True for interactive Device Code Flow,
                False for Service Account
            scope: Optional OAuth2 Scopes
            oidc_discovery_paths: Optional List of discovery paths
                (e.g. ['/application/o/techstack', '/application/o/questra'])

        Examples:
            Service Account:
            ```python
            client = QuestraAuthentication(
                url="https://authentik.dev.example.com",
                username="ServiceUser",
                password="secret_password",
                oidc_discovery_paths=[
                    "/application/o/techstack",
                    "/application/o/questra",
                ],
            )
            ```

            Interactive:
            ```python
            client = QuestraAuthentication(
                url="https://authentik.dev.example.com",
                interactive=True,
                oidc_discovery_paths=[
                    "/application/o/techstack",
                    "/application/o/questra",
                ],
            )
            ```
        """
        self.url = url
        self.client_id = client_id
        self.username = username
        self.password = password
        self.interactive = interactive
        self.scope = scope or "offline_access"
        self.oidc_discovery_paths = oidc_discovery_paths or ["/application/o/questra"]

        self._oauth_client: OAuth2Authentication | None = None
        self._oidc_config: OidcConfig | None = None

        # Initialize OIDC Discovery and OAuth Client
        self._initialize()

    def _initialize(self):
        """Initialize OIDC Discovery and OAuth2 Client."""
        # OIDC Discovery
        discovery_urls = self._build_discovery_urls()
        oidc_discovery_client = OidcDiscoveryClient(discovery_urls)
        self._oidc_config = oidc_discovery_client.discover()

        # Create OAuth2 Credentials
        if self.interactive:
            credentials = OAuth2InteractiveUserCredential()
        else:
            if not self.username or not self.password:
                raise ValueError(
                    "Username and Password are required for "
                    "Service Account authentication. "
                    "Use interactive=True for interactive login."
                )
            credentials = OAuth2ServiceCredential(
                username=self.username, password=self.password
            )

        # Create OAuth2 Client
        self._oauth_client = OAuth2Authentication(
            client_id=self.client_id,
            credentials=credentials,
            oidc_config=self._oidc_config,
            scope=self.scope,
        )

        # Perform authentication
        self._oauth_client.authenticate()

    def _build_discovery_urls(self) -> str | list[str]:
        """
        Creates Discovery URLs based on Base-URL and discovery paths.

        Returns:
            str | list[str]: String or List of Discovery URLs
        """
        if isinstance(self.url, list):
            # Multiple URLs were passed
            if self.oidc_discovery_paths:
                # Combine all URLs with all discovery paths
                urls = []
                for base_url in self.url:
                    for path in self.oidc_discovery_paths:
                        urls.append(f"{base_url.rstrip('/')}{path}")
                return urls
            else:
                return self.url
        else:
            # Single URL
            if self.oidc_discovery_paths:
                return [
                    f"{self.url.rstrip('/')}{path}"
                    for path in self.oidc_discovery_paths
                ]
            else:
                return self.url

    def get_access_token(self) -> str:
        """
        Returns a valid access token.

        The token is automatically renewed if it has expired.
        This method should be called before each API request.

        Returns:
            str: Valid access token

        Raises:
            Exception: If authentication has failed
        """
        if not self._oauth_client:
            raise Exception("OAuth Client not initialized")

        return self._oauth_client.get_access_token()

    def get_oidc_config(self) -> OidcConfig:
        """
        Returns the OIDC configuration.

        Returns:
            OidcConfig: Object with all endpoints
        """
        if not self._oidc_config:
            raise Exception("OIDC Config not initialized")

        return self._oidc_config

    def is_authenticated(self) -> bool:
        """
        Checks if the client is authenticated.

        Returns:
            True if authenticated, otherwise False
        """
        return self._oauth_client is not None and self._oauth_client.authenticated

    def reauthenticate(self):
        """
        Forces a re-authentication.

        This can be useful if the credentials have changed
        or in case of authentication problems.
        """
        if self._oauth_client:
            self._oauth_client.authenticate()
        else:
            self._initialize()
