"""
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the MIT License.
"""

from __future__ import annotations

import inspect
from typing import TYPE_CHECKING, Literal, Optional, Union

from microsoft_teams.api.auth.credentials import ClientCredentials
from microsoft_teams.common.http import Client, ClientOptions
from pydantic import BaseModel

from ...auth import Credentials, TokenCredentials
from ...auth.cloud_environment import PUBLIC
from ..api_client_settings import ApiClientSettings, merge_api_client_settings
from ..base_client import BaseClient

if TYPE_CHECKING:
    from ...auth.cloud_environment import CloudEnvironment


class GetBotTokenResponse(BaseModel):
    """Response model for bot token requests."""

    # Note: These fields use snake_case to match TypeScript exactly
    token_type: Literal["Bearer"]
    """
    The token type.
    """
    expires_in: int
    """
    The token expiration time in seconds.
    """
    ext_expires_in: Optional[int] = None
    """
    The extended token expiration time in seconds.
    """
    access_token: str
    """
    The access token.
    """


class BotTokenClient(BaseClient):
    """Client for managing bot tokens."""

    def __init__(
        self,
        options: Union[Client, ClientOptions, None] = None,
        api_client_settings: Optional[ApiClientSettings] = None,
        cloud: Optional[CloudEnvironment] = None,
    ) -> None:
        """Initialize the bot token client.

        Args:
            options: Optional Client or ClientOptions instance.
            api_client_settings: Optional API client settings.
            cloud: Optional cloud environment for sovereign cloud support.
        """
        super().__init__(options)
        self._cloud = cloud or PUBLIC
        self._api_client_settings = merge_api_client_settings(api_client_settings, self._cloud)

    async def get(self, credentials: Credentials) -> GetBotTokenResponse:
        """Get a bot token.

        Args:
            credentials: The credentials to use for authentication.

        Returns:
            The bot token response.
        """
        if isinstance(credentials, TokenCredentials):
            token = credentials.token(
                self._cloud.bot_scope,
                credentials.tenant_id,
            )
            if inspect.isawaitable(token):
                token = await token

            return GetBotTokenResponse(
                token_type="Bearer",
                expires_in=-1,
                access_token=token,
            )

        assert isinstance(credentials, ClientCredentials), (
            "Bot token client currently only supports Credentials with secrets."
        )

        tenant_id = credentials.tenant_id or self._cloud.login_tenant
        res = await self.http.post(
            f"{self._cloud.login_endpoint}/{tenant_id}/oauth2/v2.0/token",
            data={
                "grant_type": "client_credentials",
                "client_id": credentials.client_id,
                "client_secret": credentials.client_secret,
                "scope": self._cloud.bot_scope,
            },
            headers={"Content-Type": "application/x-www-form-urlencoded"},
        )

        return GetBotTokenResponse.model_validate(res.json())

    async def get_graph(self, credentials: Credentials) -> GetBotTokenResponse:
        """Get a bot token for Microsoft Graph.

        Args:
            credentials: The credentials to use for authentication.

        Returns:
            The bot token response.
        """
        if isinstance(credentials, TokenCredentials):
            token = credentials.token(
                self._cloud.graph_scope,
                credentials.tenant_id,
            )
            if inspect.isawaitable(token):
                token = await token

            return GetBotTokenResponse(
                token_type="Bearer",
                expires_in=-1,
                access_token=token,
            )

        assert isinstance(credentials, ClientCredentials), (
            "Bot token client currently only supports Credentials with secrets."
        )

        tenant_id = credentials.tenant_id or self._cloud.login_tenant
        res = await self.http.post(
            f"{self._cloud.login_endpoint}/{tenant_id}/oauth2/v2.0/token",
            data={
                "grant_type": "client_credentials",
                "client_id": credentials.client_id,
                "client_secret": credentials.client_secret,
                "scope": self._cloud.graph_scope,
            },
            headers={"Content-Type": "application/x-www-form-urlencoded"},
        )

        return GetBotTokenResponse.model_validate(res.json())
