from typing import Optional

from core.extension.api_based_extension_requestor import APIBasedExtensionRequestor
from core.external_data_tool.base import ExternalDataTool
from core.helper import encrypter
from extensions.ext_database import db
from models.api_based_extension import APIBasedExtension, APIBasedExtensionPoint


class ApiExternalDataTool(ExternalDataTool):
    """
    The api external data tool.
    """

    name: str = "api"
    """the unique name of external data tool"""

    @classmethod
    def validate_config(cls, tenant_id: str, config: dict) -> None:
        """
        Validate the incoming form config data.

        :param tenant_id: the id of workspace
        :param config: the form config data
        :return:
        """
        # own validation logic
        api_based_extension_id = config.get("api_based_extension_id")
        if not api_based_extension_id:
            raise ValueError("api_based_extension_id is required")

        # get api_based_extension
        api_based_extension = (
            db.session.query(APIBasedExtension)
            .filter(APIBasedExtension.tenant_id == tenant_id, APIBasedExtension.id == api_based_extension_id)
            .first()
        )

        if not api_based_extension:
            raise ValueError("api_based_extension_id is invalid")

    def query(self, inputs: dict, query: Optional[str] = None) -> str:
        """
        Query the external data tool.

        :param inputs: user inputs
        :param query: the query of chat app
        :return: the tool query result
        """
        # get params from config
        if not self.config:
            raise ValueError("config is required, config: {}".format(self.config))
        api_based_extension_id = self.config.get("api_based_extension_id")
        assert api_based_extension_id is not None, "api_based_extension_id is required"

        # get api_based_extension
        api_based_extension = (
            db.session.query(APIBasedExtension)
            .filter(APIBasedExtension.tenant_id == self.tenant_id, APIBasedExtension.id == api_based_extension_id)
            .first()
        )

        if not api_based_extension:
            raise ValueError(
                "[External data tool] API query failed, variable: {}, error: api_based_extension_id is invalid".format(
                    self.variable
                )
            )

        # decrypt api_key
        api_key = encrypter.decrypt_token(tenant_id=self.tenant_id, token=api_based_extension.api_key)

        try:
            # request api
            requestor = APIBasedExtensionRequestor(api_endpoint=api_based_extension.api_endpoint, api_key=api_key)
        except Exception as e:
            raise ValueError("[External data tool] API query failed, variable: {}, error: {}".format(self.variable, e))

        response_json = requestor.request(
            point=APIBasedExtensionPoint.APP_EXTERNAL_DATA_TOOL_QUERY,
            params={"app_id": self.app_id, "tool_variable": self.variable, "inputs": inputs, "query": query},
        )

        if "result" not in response_json:
            raise ValueError(
                "[External data tool] API query failed, variable: {}, error: result not found in response".format(
                    self.variable
                )
            )

        if not isinstance(response_json["result"], str):
            raise ValueError(
                "[External data tool] API query failed, variable: {}, error: result is not string".format(self.variable)
            )

        return response_json["result"]
