# Copyright (c) Microsoft. All rights reserved.

import re
import uuid
from enum import Enum
from typing import ClassVar
from urllib.parse import urlsplit, urlunsplit

from azure.core.credentials import TokenCredential
from pydantic import Field, field_validator

from semantic_kernel.exceptions.function_exceptions import PluginInitializationError
from semantic_kernel.kernel_pydantic import HttpsUrl, KernelBaseModel, KernelBaseSettings
from semantic_kernel.utils.authentication.entra_id_authentication import get_entra_auth_token


class CodeInputType(str, Enum):
    """Code input type."""

    Inline = "inline"


class CodeExecutionType(str, Enum):
    """Code execution type."""

    Synchronous = "synchronous"
    # Asynchronous = "asynchronous" TODO: Enable when available


class SessionsPythonSettings(KernelBaseModel):
    """The Sessions Python code interpreter settings."""

    session_id: str | None = Field(default_factory=lambda: str(uuid.uuid4()), alias="identifier", exclude=True)
    code_input_type: CodeInputType | None = Field(default=CodeInputType.Inline, alias="codeInputType")
    execution_type: CodeExecutionType | None = Field(default=CodeExecutionType.Synchronous, alias="executionType")
    python_code: str | None = Field(alias="code", default=None)
    timeout_in_sec: int | None = Field(default=100, alias="timeoutInSeconds")
    sanitize_input: bool | None = Field(default=True, alias="sanitizeInput")


class ACASessionsSettings(KernelBaseSettings):
    """Azure Container Apps sessions settings.

    Required:
    - pool_management_endpoint: HttpsUrl - The URL of the Azure Container Apps pool management endpoint.
        (Env var ACA_POOL_MANAGEMENT_ENDPOINT)
    """

    env_prefix: ClassVar[str] = "ACA_"

    pool_management_endpoint: HttpsUrl
    token_endpoint: str = "https://acasessions.io/.default"

    @field_validator("pool_management_endpoint", mode="before")
    @classmethod
    def _validate_endpoint(cls, endpoint: str) -> str:
        """Validates the pool management endpoint."""
        if "python/execute" in endpoint:
            endpoint_parsed = urlsplit(endpoint.replace("python/execute", ""))._asdict()
        else:
            endpoint_parsed = urlsplit(endpoint)._asdict()
        if endpoint_parsed["path"]:
            endpoint_parsed["path"] = re.sub(r"/{2,}", "/", endpoint_parsed["path"])
        else:
            endpoint_parsed["path"] = "/"
        return str(urlunsplit(endpoint_parsed.values()))

    def get_sessions_auth_token(
        self, credential: TokenCredential | None = None, token_endpoint: str | None = None
    ) -> str | None:
        """Retrieve a Microsoft Entra Auth Token for a given token endpoint for the use with an Azure Container App.

        The required role for the token is `Azure ContainerApps Session Executor and Contributor`.
        The token endpoint may be specified as an environment variable, via the .env
        file or as an argument. If the token endpoint is not provided, the default is None.
        The `token_endpoint` argument takes precedence over the `token_endpoint` attribute.

        Args:
            credential: The credential to use for authentication.
            token_endpoint: The token endpoint to use. Defaults to `https://acasessions.io/.default`.

        Returns:
            The Azure token or None if the token could not be retrieved.

        Raises:
            ServiceInitializationError: If the token endpoint is not provided.
        """
        endpoint_to_use = token_endpoint or self.token_endpoint
        if endpoint_to_use is None:
            raise PluginInitializationError("Please provide a token endpoint to retrieve the authentication token.")
        if credential is None:
            raise PluginInitializationError("Please provide a credential to retrieve the authentication token.")
        return get_entra_auth_token(credential, endpoint_to_use)
