import httpx
from .ConfigurationLoader import ConfigurationLoader
from .AuthenticationHandler import AuthenticationHandler
from .APIRequestBuilder import APIRequestBuilder
from .ResponseHandler import ResponseHandler
import os
import json
import aiohttp
import importlib


class IntegrationManager:
    def __init__(self, config_loader: ConfigurationLoader, auth_handler: AuthenticationHandler, 
                 request_builder: APIRequestBuilder, response_handler: ResponseHandler):
        self.config_loader = config_loader
        self.auth_handler = auth_handler
        self.request_builder = request_builder
        self.response_handler = response_handler


    async def execute_integration(self, app_integration_id: str, flow_id: str, request_data: dict):

        app_integration = await self.config_loader.load_integration(app_integration_id)
        flow = await self.config_loader.load_flow(flow_id)
        app_details = await self.config_loader.load_app_details(app_integration['app_id'])
        api_schema = await self.load_api_schema_as_json(app_details)
        #function_name = request_data.get('f')
        function_name = flow['endpoint']

        if not function_name:
            raise KeyError("function is loaded from flow details")

        if app_details.get('call_type') == 'sdk':

            sdk_info = api_schema['servers'][0]
            sdk_module_name = sdk_info['url']
            sdk_class_name = sdk_info['class']

            path_config = api_schema['paths'].get(function_name, {})
            http_method = 'POST' if 'post' in path_config else 'GET'
            response_schema = path_config.get(http_method.lower(), {}).get("responses", {})

            # Dynamically load the SDK module and class
            sdk_module = self.load_sdk_module(sdk_module_name)

            sdk_class = getattr(sdk_module, sdk_class_name)

            # Instantiate the SDK class
            sdk_instance = sdk_class(app_integration, app_details)
         
            # Call the function specified by 'f' in the SDK instance
            if hasattr(sdk_instance, function_name):
                function_to_call = getattr(sdk_instance, function_name)
                return await function_to_call(request_data), response_schema
            else:
                raise ValueError(f"No such method '{function_name}' in class '{sdk_class_name}'")
        else:
            # For non-SDK call types, use the server URL and append the function name as the endpoint
            base_url = api_schema['servers'][0]['url']
            #endpoint = f"{base_url}/{function_name}"
            api_request, response_schema = await self.request_builder.build_request(app_details, request_data, app_integration, api_schema, flow)
            response = await self.send_request(api_request)
            return response, response_schema


    async def load_api_schema_as_json(self, app_details: dict) -> dict:
        """
        Load the API schema as JSON. If 'api_schema' in app_details is a dictionary containing a 'url' key,
        check if the URL starts with 'https'. If not, treat it as a relative path to the local_directory.
        If it starts with 'https', load JSON from the URL directly. Otherwise, return 'api_schema' directly 
        if it is a JSON object.
        """
        api_schema_str = app_details.get('api_schema', None)
        api_schema = json.loads(api_schema_str)

        if isinstance(api_schema, dict):
            if 'url' in api_schema:
                url = api_schema['url']
                if url.startswith('https://'):
                    # Load JSON from the absolute URL
                    api_schema =  await self.load_json_from_url(url)
                else:
                    current_directory = os.getcwd()
                    local_path = os.path.join(current_directory, 'app/api/v1/apps/sdk/json', url.lstrip('/'))
                    try:
                        with open(local_path, 'r', encoding='utf-8') as file:
                            api_schema = json.load(file)
                    except FileNotFoundError:
                        print(f"File not found: {local_path}")
                        return None
                    except json.JSONDecodeError:
                        print(f"Error decoding JSON from file: {local_path}")
                        return None

            return api_schema
        
        return None
    

    async def load_json_from_url(self, url: str):
        async with aiohttp.ClientSession() as session:
            async with session.get(url) as response:
                if response.status == 200:
                    return await response.json()
                else:
                    raise Exception(f"Failed to load JSON from URL: {url} - Status Code: {response.status}")

    def load_sdk_module(self, module_name: str):
        # Assuming SDKs are located in a 'sdk' directory relative to the current file
        current_directory = os.path.dirname(__file__)
        sdk_directory = os.path.join(current_directory, 'sdk')
        module_path = os.path.join(sdk_directory, module_name + ".py")
        spec = importlib.util.spec_from_file_location(module_name, module_path)
        sdk_module = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(sdk_module)
        return sdk_module


    async def test_integration(self, app_id: str, function_name: str, request_data: dict, configurations:dict, auth_config:dict ):

        app_details = await self.config_loader.load_app_details(app_id)
        api_schema = await self.load_api_schema_as_json(app_details)
        api_request, response_schema = await self.request_builder.test_request(app_details, request_data, api_schema, function_name, configurations, auth_config )
        response = await self.send_request(api_request)
        return response, response_schema

    async def send_request(self, api_request: httpx.Request) -> httpx.Response:
        """
        Send the API request and return the response.
        """
        async with httpx.AsyncClient(verify=False) as client:  # Disable SSL verification
            response = await client.send(api_request)
            return response

# Example usage
# Define the required objects and execute an integration
# integration_manager = IntegrationManager(config_loader, auth_handler, request_builder, response_handler)
# response = await integration_manager.execute_integration(app_integration_id, request_data)
