"""
Integration manager for high-level business logic.

This module provides the IntegrationManager class for managing integrations
with error handling, logging, and model conversions.
"""

from typing import Optional, List

from pygeai import logger
from pygeai.core.base.mappers import ResponseMapper
from pygeai.core.base.responses import EmptyResponse
from pygeai.core.common.exceptions import APIError, MissingRequirementException
from pygeai.core.handlers import ErrorHandler
from pygeai.lab.integrations.clients import IntegrationClient
from pygeai.lab.integrations.mappers import IntegrationMapper
from pygeai.lab.models import ToolPlugin, ToolPluginList, ToolParameter, FilterSettings


class IntegrationManager:
    """
    Manager for Integration operations with error handling and model mapping.

    This manager provides high-level methods for working with integrations,
    handling errors, logging, and model conversions.
    """

    def __init__(self, api_key: str = None, base_url: str = None, alias: str = None, project_id: str = None):
        """
        Initialize the IntegrationManager.

        :param api_key: str - API key for authentication. Optional if using alias.
        :param base_url: str - Base URL of the AI Lab instance. Optional if using alias.
        :param alias: str - Alias for a pre-configured connection. Optional if using api_key and base_url.
        :param project_id: str - Project ID to operate within. Optional, can be set later.
        """
        self.__client = IntegrationClient(
            api_key=api_key,
            base_url=base_url,
            alias=alias,
            project_id=project_id
        )

    def list_integrations(
            self,
            filter_settings: Optional[FilterSettings] = None
    ) -> ToolPluginList:
        """
        Retrieves a list of integrations associated with the specified project.

        This method queries the integration client to fetch a list of integrations
        for the given project ID, applying the specified filter settings.

        :param filter_settings: Optional[FilterSettings] - Settings to filter the integration list query.
            Supports the following fields:
            - name: str - Filter by integration name (supports partial matching with '%' prefix)
            - id: str - Filter by integration ID
            - access_scope: str - Filter by access scope ('private', 'public', 'project')
            - count: int - Number of integrations to retrieve (default: 100)
            - start: int - Pagination offset (default: 0)
            - detail_level: str - Response detail level ('normal' or 'expanded', default: 'normal')
            - allow_drafts: bool - Include draft integrations (default: True)
            - allow_external: bool - Include external integrations (default: True)
        :return: ToolPluginList - A ToolPluginList containing the retrieved integrations.
        :raises APIError: If the API returns errors.

        Example:
            >>> from pygeai.lab.integrations.managers import IntegrationManager
            >>> from pygeai.lab.models import FilterSettings
            >>> manager = IntegrationManager(api_key="key", base_url="url", project_id="proj")
            >>> filters = FilterSettings(count=10, access_scope="private")
            >>> integrations = manager.list_integrations(filters)
        """
        if filter_settings is None:
            filter_settings = FilterSettings(
                count=100,
                start=0,
                detail_level="normal",
                allow_drafts=True,
                allow_external=True
            )

        logger.debug(f"Listing integrations with filters: {filter_settings}")

        response_data = self.__client.list_integrations(
            name=getattr(filter_settings, 'name', None),
            id=getattr(filter_settings, 'id', None),
            access_scope=getattr(filter_settings, 'access_scope', None),
            count=getattr(filter_settings, 'count', 100),
            start=getattr(filter_settings, 'start', 0),
            detail_level=getattr(filter_settings, 'detail_level', 'normal'),
            allow_drafts=getattr(filter_settings, 'allow_drafts', True),
            allow_external=getattr(filter_settings, 'allow_external', True)
        )

        if ErrorHandler.has_errors(response_data):
            error = ErrorHandler.extract_error(response_data)
            logger.error(f"Error received while listing integrations: {error}")
            raise APIError(f"Error received while listing integrations: {error}")

        result = IntegrationMapper.map_to_integration_list(response_data)
        logger.info(f"Successfully retrieved {len(result)} integration(s)")
        return result

    def get_integration(
            self,
            integration_id_or_name: str,
            filter_settings: Optional[FilterSettings] = None
    ) -> ToolPlugin:
        """
        Retrieves details of a specific integration from the specified project.

        This method queries the integration client to fetch details of a single integration
        identified by either ID or name.

        :param integration_id_or_name: str - Unique identifier or name of the integration to retrieve.
        :param filter_settings: Optional[FilterSettings] - Settings to filter the integration retrieval.
            Supports the following fields:
            - detail_level: str - Response detail level ('normal' or 'expanded', default: 'normal')
            - allow_drafts: bool - Include draft integration (default: True)
        :return: ToolPlugin - A ToolPlugin object representing the retrieved integration.
        :raises APIError: If the API returns errors.

        Example:
            >>> from pygeai.lab.integrations.managers import IntegrationManager
            >>> manager = IntegrationManager(api_key="key", base_url="url", project_id="proj")
            >>> integration = manager.get_integration("my-integration")
        """
        if filter_settings is None:
            filter_settings = FilterSettings(
                detail_level="normal",
                allow_drafts=True
            )

        logger.debug(f"Retrieving integration {integration_id_or_name}")

        response_data = self.__client.get_integration(
            integration_id_or_name=integration_id_or_name,
            detail_level=getattr(filter_settings, 'detail_level', 'normal'),
            allow_drafts=getattr(filter_settings, 'allow_drafts', True)
        )

        if ErrorHandler.has_errors(response_data):
            error = ErrorHandler.extract_error(response_data)
            logger.error(f"Error received while retrieving integration: {error}")
            raise APIError(f"Error received while retrieving integration: {error}")

        result = IntegrationMapper.map_to_integration(response_data)
        logger.info(f"Successfully retrieved integration {integration_id_or_name}")
        return result

    def delete_integration(
            self,
            integration_id_or_name: str
    ) -> EmptyResponse:
        """
        Deletes a specific integration from the specified project.

        This method sends a request to delete the integration identified by either ID or name.
        Deleting an integration may also delete all tools within it, depending on server configuration.

        :param integration_id_or_name: str - Unique identifier or name of the integration to delete.
        :return: EmptyResponse - An EmptyResponse object if the integration was deleted successfully.
        :raises APIError: If the API returns errors.

        Example:
            >>> from pygeai.lab.integrations.managers import IntegrationManager
            >>> manager = IntegrationManager(api_key="key", base_url="url", project_id="proj")
            >>> result = manager.delete_integration("my-integration")
        """
        logger.debug(f"Deleting integration {integration_id_or_name}")

        response_data = self.__client.delete_integration(
            integration_id_or_name=integration_id_or_name
        )

        if ErrorHandler.has_errors(response_data):
            error = ErrorHandler.extract_error(response_data)
            logger.error(f"Error received while deleting integration: {error}")
            raise APIError(f"Error received while deleting integration: {error}")

        result = ResponseMapper.map_to_empty_response(response_data or "Integration deleted successfully")
        logger.info(f"Successfully deleted integration {integration_id_or_name}")
        return result

    def get_config(
            self,
            integration_id_or_name: str,
            allow_drafts: bool = True
    ) -> List[ToolParameter]:
        """
        Retrieves the configuration (shared parameters) for a specific integration.

        This method returns the parameters defined at the integration level, which can be inherited
        by tools belonging to the integration.

        :param integration_id_or_name: str - Unique identifier or name of the integration.
        :param allow_drafts: bool - Whether to include draft configuration. Defaults to True.
        :return: List[ToolParameter] - A list of ToolParameter objects representing shared parameters.
        :raises APIError: If the API returns errors.

        Example:
            >>> from pygeai.lab.integrations.managers import IntegrationManager
            >>> manager = IntegrationManager(api_key="key", base_url="url", project_id="proj")
            >>> parameters = manager.get_config("my-integration")
        """
        logger.debug(f"Retrieving config for integration {integration_id_or_name}")

        response_data = self.__client.get_config(
            integration_id_or_name=integration_id_or_name,
            allow_drafts=allow_drafts
        )

        if ErrorHandler.has_errors(response_data):
            error = ErrorHandler.extract_error(response_data)
            logger.error(f"Error received while retrieving integration config: {error}")
            raise APIError(f"Error received while retrieving integration config: {error}")

        result = IntegrationMapper.map_to_config(response_data)
        logger.info(f"Successfully retrieved config for integration {integration_id_or_name}")
        return result

    def set_config(
            self,
            integration_id_or_name: str,
            parameters: List[ToolParameter]
    ) -> EmptyResponse:
        """
        Sets or updates the configuration (shared parameters) for a specific integration.

        This method allows setting parameter values at the integration level, which can then be inherited
        by tools belonging to the integration.

        :param integration_id_or_name: str - Unique identifier or name of the integration.
        :param parameters: List[ToolParameter] - List of ToolParameter objects defining the integration's
            shared parameters. Each parameter should have at minimum 'key', 'type', and 'value' fields.
        :return: EmptyResponse - An EmptyResponse object if the configuration was set successfully.
        :raises MissingRequirementException: If parameters list is None or empty.
        :raises APIError: If the API returns errors.

        Example:
            >>> from pygeai.lab.integrations.managers import IntegrationManager
            >>> from pygeai.lab.models import ToolParameter
            >>> manager = IntegrationManager(api_key="key", base_url="url", project_id="proj")
            >>> param = ToolParameter(key="api_key", type="config", value="secret-name", fromSecret=True)
            >>> result = manager.set_config("my-integration", [param])
        """
        if not parameters:
            raise MissingRequirementException("Parameters list must be provided and non-empty.")

        logger.debug(
            f"Setting config for integration {integration_id_or_name} with {len(parameters)} parameter(s)"
        )

        # Convert ToolParameter objects to dictionaries
        params_data = [param.to_dict() if hasattr(param, 'to_dict') else param for param in parameters]

        response_data = self.__client.set_config(
            integration_id_or_name=integration_id_or_name,
            parameters=params_data
        )

        if ErrorHandler.has_errors(response_data):
            error = ErrorHandler.extract_error(response_data)
            logger.error(f"Error received while setting integration config: {error}")
            raise APIError(f"Error received while setting integration config: {error}")

        result = ResponseMapper.map_to_empty_response(response_data or "Configuration set successfully")
        logger.info(f"Successfully set config for integration {integration_id_or_name}")
        return result

    def export_integration(
            self,
            integration_id_or_name: str
    ) -> dict:
        """
        Exports an integration with all its tools and configuration.

        This method retrieves the complete integration definition including all tools,
        shared parameters, and metadata, suitable for importing into another project.

        :param integration_id_or_name: str - Unique identifier or name of the integration to export.
        :return: dict - A dictionary containing the complete integration export data.
        :raises APIError: If the API returns errors.
        :raises InvalidAPIResponseException: If the export endpoint is not available (404).

        Note: This endpoint may not be available yet in all API versions.

        Example:
            >>> from pygeai.lab.integrations.managers import IntegrationManager
            >>> manager = IntegrationManager(api_key="key", base_url="url", project_id="proj")
            >>> export_data = manager.export_integration("my-integration")
        """
        logger.debug(f"Exporting integration {integration_id_or_name}")

        try:
            response_data = self.__client.export_integration(
                integration_id_or_name=integration_id_or_name
            )

            if ErrorHandler.has_errors(response_data):
                error = ErrorHandler.extract_error(response_data)
                logger.error(f"Error received while exporting integration: {error}")
                raise APIError(f"Error received while exporting integration: {error}")

            logger.info(f"Successfully exported integration {integration_id_or_name}")
            return response_data
        except Exception as e:
            logger.warning(f"Export integration endpoint may not be available: {e}")
            raise

    def create_integration(
            self,
            name: str,
            description: str,
            manifest: str,
            access_scope: str = "private",
            automatic_publish: bool = False
    ) -> ToolPlugin:
        """
        Creates a new integration from an OpenAPI manifest.

        This method creates a new integration in the project using an OpenAPI specification
        manifest that defines the tools and their parameters.

        :param name: str - Name of the integration.
        :param description: str - Description of what the integration does.
        :param manifest: str - OpenAPI 3.0 specification as a JSON string defining the API.
        :param access_scope: str - Access scope: "private" (default) or "public".
        :param automatic_publish: bool - Whether to automatically publish the tools after creation.
        :return: ToolPlugin - A ToolPlugin object representing the created integration.
        :raises MissingRequirementException: If required parameters are missing.
        :raises APIError: If the API returns errors.

        Example:
            >>> from pygeai.lab.integrations.managers import IntegrationManager
            >>> manager = IntegrationManager(api_key="key", base_url="url", project_id="proj")
            >>> manifest = '{"openapi": "3.0.0", ...}'
            >>> integration = manager.create_integration(
            ...     name="My Integration",
            ...     description="My description",
            ...     manifest=manifest
            ... )
        """
        if not all([name, description, manifest]):
            raise MissingRequirementException("Name, description, and manifest are required.")

        logger.debug(f"Creating integration '{name}'")

        response_data = self.__client.create_integration(
            name=name,
            description=description,
            manifest=manifest,
            access_scope=access_scope,
            automatic_publish=automatic_publish
        )

        if ErrorHandler.has_errors(response_data):
            error = ErrorHandler.extract_error(response_data)
            logger.error(f"Error received while creating integration: {error}")
            raise APIError(f"Error received while creating integration: {error}")

        result = IntegrationMapper.map_to_integration(response_data)
        logger.info(f"Successfully created integration '{name}'")
        return result

    def import_integration(
            self,
            integration_data: dict
    ) -> ToolPlugin:
        """
        Imports an integration from exported data.

        This method creates a new integration in the project using previously exported
        integration data, including all tools, shared parameters, and configuration.

        :param integration_data: dict - The integration export data to import.
            Should be the output from export_integration() method.
        :return: ToolPlugin - A ToolPlugin object representing the imported integration.
        :raises MissingRequirementException: If integration_data is None or empty.
        :raises APIError: If the API returns errors.

        Example:
            >>> from pygeai.lab.integrations.managers import IntegrationManager
            >>> manager = IntegrationManager(api_key="key", base_url="url", project_id="proj")
            >>> export_data = {"plugin": {...}, "tools": [...]}
            >>> integration = manager.import_integration(export_data)
        """
        if not integration_data:
            raise MissingRequirementException("Integration data must be provided and non-empty.")

        logger.debug("Importing integration")

        response_data = self.__client.import_integration(
            integration_data=integration_data
        )

        if ErrorHandler.has_errors(response_data):
            error = ErrorHandler.extract_error(response_data)
            logger.error(f"Error received while importing integration: {error}")
            raise APIError(f"Error received while importing integration: {error}")

        result = IntegrationMapper.map_to_integration(response_data)
        logger.info(f"Successfully imported integration")
        return result

    def update_integration(
            self,
            integration_id_or_name: str,
            name: str,
            description: str,
            manifest: str,
            access_scope: str = "private",
            automatic_publish: bool = False
    ) -> ToolPlugin:
        """
        Updates an existing integration by re-importing it with a new manifest.

        This method replaces the integration's definition with the provided manifest,
        updating all tools defined in it.

        :param integration_id_or_name: str - Unique identifier or name of the integration to update.
        :param name: str - New name for the integration.
        :param description: str - New description for the integration.
        :param manifest: str - Updated OpenAPI 3.0 specification as a JSON string.
        :param access_scope: str - Access scope: "private" (default) or "public".
        :param automatic_publish: bool - Whether to automatically publish the tools after update.
        :return: ToolPlugin - A ToolPlugin object representing the updated integration.
        :raises MissingRequirementException: If required parameters are missing.
        :raises APIError: If the API returns errors.

        Example:
            >>> from pygeai.lab.integrations.managers import IntegrationManager
            >>> manager = IntegrationManager(api_key="key", base_url="url", project_id="proj")
            >>> updated_manifest = '{"openapi": "3.0.0", ...}'
            >>> integration = manager.update_integration(
            ...     integration_id_or_name="my-integration",
            ...     name="My Integration",
            ...     description="Updated",
            ...     manifest=updated_manifest
            ... )
        """
        if not all([integration_id_or_name, name, description, manifest]):
            raise MissingRequirementException(
                "integration_id_or_name, name, description, and manifest are required."
            )

        logger.debug(f"Updating integration '{integration_id_or_name}'")

        response_data = self.__client.update_integration(
            integration_id_or_name=integration_id_or_name,
            name=name,
            description=description,
            manifest=manifest,
            access_scope=access_scope,
            automatic_publish=automatic_publish
        )

        if ErrorHandler.has_errors(response_data):
            error = ErrorHandler.extract_error(response_data)
            logger.error(f"Error received while updating integration: {error}")
            raise APIError(f"Error received while updating integration: {error}")

        result = IntegrationMapper.map_to_integration(response_data)
        logger.info(f"Successfully updated integration '{integration_id_or_name}'")
        return result

    def publish_tools(
            self,
            integration_id_or_name: str
    ) -> dict:
        """
        Publishes all tools in an integration, making them available for use.

        After creating or updating an integration, tools are in draft/pending state.
        This method publishes them so they can be used by agents.

        :param integration_id_or_name: str - Unique identifier or name of the integration.
        :return: dict - Result of the publish operation (may be empty).
        :raises APIError: If the API returns errors.

        Example:
            >>> from pygeai.lab.integrations.managers import IntegrationManager
            >>> manager = IntegrationManager(api_key="key", base_url="url", project_id="proj")
            >>> manager.publish_tools("my-integration")
        """
        logger.debug(f"Publishing tools for integration '{integration_id_or_name}'")

        response_data = self.__client.publish_tools(
            integration_id_or_name=integration_id_or_name
        )

        if ErrorHandler.has_errors(response_data):
            error = ErrorHandler.extract_error(response_data)
            logger.error(f"Error received while publishing tools: {error}")
            raise APIError(f"Error received while publishing tools: {error}")

        logger.info(f"Successfully published tools for integration '{integration_id_or_name}'")
        return response_data
