# This file was auto-generated by Fern from our API Definition.

import typing
import copy
import datetime
import json
import datetime
import json


import volcenginesdkvefaas
import volcenginesdkcore

from .base import BaseProvider
from ..types.sandbox_response import SandboxResponse
from .sign import request

class VolcengineProvider(BaseProvider):
    """
    Volcengine cloud provider implementation for sandbox management.
    
    This provider uses the Volcengine VEFAAS (Volcengine Function as a Service)
    API to manage sandbox instances.
    """
    
    def __init__(
        self,
        access_key: str,
        secret_key: str,
        region: str = "cn-beijing",
        client_side_validation: bool = True
    ):
        """
        Initialize the Volcengine provider.
        
        Parameters
        ----------
        access_key : str
            Volcengine access key ID
        secret_key : str
            Volcengine secret access key
        region : str, optional
            Volcengine region, defaults to "cn-beijing"
        client_side_validation : bool, optional
            Enable client-side validation, defaults to True
        """
        
        self.access_key = access_key
        self.secret_key = secret_key
        self.region = region
        self.client_side_validation = client_side_validation
        
        # Initialize Volcengine configuration
        configuration = volcenginesdkcore.Configuration()
        configuration.ak = access_key
        configuration.sk = secret_key
        configuration.region = region
        configuration.client_side_validation = client_side_validation
        volcenginesdkcore.Configuration.set_default(configuration)

        # Initialize VEFAAS client
        self._client = volcenginesdkvefaas.VEFAASApi(volcenginesdkcore.ApiClient(configuration))
    
    def create_sandbox(
        self,
        function_id: str,
        timeout: int = 30,
        **kwargs
    ) -> SandboxResponse:
        """
        Create a new sandbox instance using Volcengine VEFAAS.
        
        Parameters
        ----------
        function_id : str
            The function ID for the sandbox
        timeout: int
            The timeout for the sandbox creation in minutes
        **kwargs
            Additional parameters for sandbox creation
            
        Returns
        -------
        str
            The ID of the created sandbox
        Exception
            The exception raised if the sandbox creation fails
        -------
        """
        try:
            request = volcenginesdkvefaas.CreateSandboxRequest(
                function_id=function_id,
                timeout=timeout,
                **kwargs
            )
            response = self._client.create_sandbox(request)
            return response.sandbox_id
            
        except Exception as e:
            return e
    
    def delete_sandbox(self, function_id: str, sandbox_id: str, **kwargs) -> SandboxResponse:
        """
        Delete an existing sandbox instance.
        
        Parameters
        ----------
        function_id : str
            The function ID of the sandbox
        sandbox_id : str
            The ID of the sandbox to delete
        **kwargs
            Additional parameters for sandbox deletion
            
        Returns
        -------
        SandboxResponse
            The response containing deletion status
        """
        try:
            request = volcenginesdkvefaas.KillSandboxRequest(
                function_id=function_id,
                sandbox_id=sandbox_id,
                **kwargs
            )
            response = self._client.kill_sandbox(request)

            return response
        
        except Exception as e:
            return e
    
    def _append_instance_query_struct(self, domains_info: typing.List[typing.Dict[str, typing.Any]], instance_name: str) -> typing.List[typing.Dict[str, typing.Any]]:
        """Append ?faasInstanceName= to domain field of structured domain objects, preserving type."""
        result: typing.List[typing.Dict[str, typing.Any]] = []
        for info in domains_info:
            domain_str = info.get("domain") or info.get("Domain") or ""
            if not domain_str:
                continue
            if "?" in domain_str:
                new_domain = f"{domain_str}&faasInstanceName={instance_name}"
            else:
                new_domain = f"{domain_str}?faasInstanceName={instance_name}"
            result.append({
                "domain": new_domain,
                "type": info.get("type") or info.get("Type"),
            })
        return result
    
    def get_sandbox(self, function_id: str, sandbox_id: str, **kwargs) -> SandboxResponse:
        """
        Get details of an existing sandbox instance.
        
        Parameters
        ----------
        function_id : str
            The function ID of the sandbox
        sandbox_id : str
            The ID of the sandbox to retrieve
        **kwargs
            Additional parameters for sandbox retrieval
            
        Returns
        -------
        SandboxResponse
            The response containing sandbox details
        """
        try:
            request = volcenginesdkvefaas.DescribeSandboxRequest(
                function_id=function_id,
                sandbox_id=sandbox_id,
                **kwargs
            )
            response = self._client.describe_sandbox(request)

            out = response.to_dict()
            base_domains = self.get_apig_domains(function_id)
            domains_struct = self._append_instance_query_struct(base_domains, sandbox_id)
            out["domains"] = domains_struct
            return out
            
        except Exception as e:
            return e
    
    def list_sandboxes(self, function_id: str, **kwargs) -> SandboxResponse:
        """
        List all sandbox instances for a function.
        
        Parameters
        ----------
        function_id : str
            The function ID to list sandboxes for
        **kwargs
            Additional parameters for listing sandboxes
            
        Returns
        -------
        SandboxResponse
            The response containing list of sandboxes
        """
        try:
            request = volcenginesdkvefaas.ListSandboxesRequest(
                function_id=function_id,
                **kwargs
            )
            response = self._client.list_sandboxes(request)
            
            # Attach domains with instanceName query to each sandbox item using instance_id
            base_domains = self.get_apig_domains(function_id)

            sandboxes = response.sandboxes
            normalized: typing.List[typing.Dict[str, typing.Any]] = []
            for sb in sandboxes:
                item = sb.to_dict()
                instance_id = item.get("id") or item.get("sandbox_id")
                domains_struct = self._append_instance_query_struct(base_domains, instance_id) if instance_id else base_domains
                item["domains"] = domains_struct
                normalized.append(item)

            return normalized
            
        except Exception as e:
            return e


    def _get_apig_trigger(self, function_id: str) -> str:
        """
        Get the UpstreamId from APIG triggers for a given function.
        
        Parameters
        ----------
        function_id : str
            The function ID to get triggers for
            
        Returns
        -------
        str
            The UpstreamId from the first APIG trigger found, or None if not found
        """
        body = {
            "FunctionId": function_id,
        }
        now = datetime.datetime.utcnow()
        response = request("POST", now, {}, {}, self.access_key, self.secret_key,"", "ListTriggers", json.dumps(body))
        
        # Parse the response to extract UpstreamId from APIG triggers
        if response and isinstance(response, dict):
            result = response.get("Result", {})
            items = result.get("Items", [])
            
            for item in items:
                if item.get("Type") == "apig":
                    detailed_config = item.get("DetailedConfig", "{}")
                    try:
                        config = json.loads(detailed_config)
                        upstream_id = config.get("UpstreamId")
                        if upstream_id:
                            return upstream_id
                    except json.JSONDecodeError:
                        print(f"Failed to parse DetailedConfig: {detailed_config}")
                        continue
        
        return None

    def _get_apig_domains(self, upstream_id: str) -> list:
        """
        Get structured domains from APIG routes using the upstream ID.
        
        Parameters
        ----------
        upstream_id : str
            The upstream ID to get routes for
            
        Returns
        -------
        list
            List of domains from the routes, or empty list if not found
        """
        body = {
            "UpstreamId": upstream_id,
            "PageSize": 100,
            "PageNumber": 1
        }
        
        now = datetime.datetime.utcnow()
        response = request("POST", now, {}, {}, self.access_key, self.secret_key, "", "ListRoutes", json.dumps(body))
                
        # Extract domains from the response and append MatchContent path, preserving type
        domains: typing.List[typing.Dict[str, typing.Any]] = []
        if response and isinstance(response, dict):
            result = response.get("Result", {})
            routes = result.get("Items", [])
            for route in routes:
                # derive path prefix from match rule
                path_prefix = ""
                try:
                    match_rule = route.get("MatchRule") or {}
                    path_rule = match_rule.get("Path") or {}
                    match_content = path_rule.get("MatchContent")
                    if isinstance(match_content, str):
                        path_prefix = match_content
                except Exception:
                    path_prefix = ""

                route_domains = route.get("Domains", [])
                for domain_info in route_domains:
                    base = domain_info.get("Domain")
                    if not base:
                        continue
                    # Append path prefix (e.g., "/v1") to the domain string, and preserve type
                    domains.append({
                        "domain": f"{base}{path_prefix}",
                        "type": domain_info.get("Type") or domain_info.get("type"),
                    })
        
        return domains

    def get_apig_domains(self, function_id: str) -> list:
        """
        Get domains for APIG triggers of a given function.
        
        Parameters
        ----------
        function_id : str
            The function ID to get domains for
            
        Returns
        -------
        list
            List of domains from APIG routes, or empty list if not found
        """
        upstream_id = self._get_apig_trigger(function_id)
        if upstream_id:
            return self._get_apig_domains(upstream_id)
        return []


