"""This script implements the action keywords matching to Misty's API
endpoints, sending action requests and data shortcuts.
"""
import json
import sys
from os import path
from typing import Dict, Union

import requests

from misty2py.utils.colours import construct_transition_dict

this_directory = path.abspath(path.dirname(__file__))
ACTIONS_JSON = str(path.join(this_directory, "allowed_actions.json"))
DATA_JSON = str(path.join(this_directory, "allowed_data.json"))


class Post:
    """A class representing the POST url request method.

    Attributes:
        ip (str): The IP address where the requests are sent.
        allowed_actions (dict): The dictionary of custom action keywords
        matching to the Misty's API endpoints.
        allowed_data (dict): The dictionary of custom data shortcuts matching
        to the json dictionaries required my Misty's API.
    """

    def __init__(
        self,
        ip: str,
        custom_allowed_actions: Dict = {},
        custom_allowed_data: Dict = {}
    ) -> None:
        """Initialises a Post object.

        Args:
            ip (str): The IP address where the requests are sent.
            custom_allowed_actions (Dict, optional): The dictionary of action
            keywords. Defaults to {}.
            custom_allowed_data (Dict, optional): The dictionary of data
            shortcuts. Defaults to {}.
        """

        self.ip = ip

        allowed_actions = custom_allowed_actions
        f = open(ACTIONS_JSON)
        allowed_actions.update(json.loads(f.read()))
        f.close()
        self.allowed_actions = allowed_actions

        allowed_data = custom_allowed_data
        f = open(DATA_JSON)
        allowed_data.update(json.loads(f.read()))
        f.close()
        self.allowed_data = allowed_data

    def perform_action(
        self, endpoint: str, data: Dict, request_method: str = "post"
    ) -> Dict:
        """Sends a POST request.

        Args:
            endpoint (str): The API endpoint to which the request is sent.
            data (Dict): The json data supplied in the body of the request.
            request_method (str, optional): The request method.
            Defaults to "post".

        Returns:
            Dict: The API response.
        """

        if request_method == "post":
            response = requests.post(
                "http://%s/%s" % (self.ip, endpoint),
                json=data
            )
        else:
            response = requests.delete(
                "http://%s/%s" % (self.ip, endpoint),
                json=data
            )
        try:
            return response.json()
        except Exception as e:
            return {
                "status": "Success",
                "content": response.content,
                "error_msg": e
            }


class Action(Post):
    """A class representing an action request for Misty.
    A subclass of Post()."""

    def perform_action(
        self, action_name: str, data: Union[str, Dict], data_method: str
    ) -> Dict:
        """Sends an action request to Misty.

        Args:
            action_name (str): The action keyword specifying which action
            is requested.
            data (Union[str, Dict]): The data shortcut representing the data
            supplied in the body of the request or the json dictionary
            to be supplied in the body of the request.
            data_method (str): "dict" if the data is supplied as a json
            dictionary, "string" if the data is supplied as a data shortcut.

        Returns:
            Dict: the response from the API.
        """

        if action_name not in self.allowed_actions.keys():
            return {
                "status": "Failed",
                "message": "Command `%s` not supported." % action_name,
            }

        else:
            if data_method == "dict":
                try:
                    return super().perform_action(
                        self.allowed_actions[action_name]["endpoint"],
                        data,
                        request_method=(
                            self.allowed_actions[action_name]["method"]
                        )
                    )

                except Exception as e:
                    return {
                        "status": "Failed",
                        "message": "Error occured. Error message: `%s`." % e,
                    }

            elif data_method == "string" and data in self.allowed_data:
                try:
                    return super().perform_action(
                        self.allowed_actions[action_name]["endpoint"],
                        self.allowed_data[data],
                        request_method=(
                            self.allowed_actions[action_name]
                                                ["method"]
                        ),
                    )

                except Exception as e:
                    return {
                        "status": "Failed",
                        "message": "Error occured. Error message: `%s`" % e,
                    }

            else:
                return {
                    "status": "Failed",
                    "message": "Data shortcut `%s` not supported." % data,
                }

    def action_handler(self, action_name: str, data: Union[Dict, str]) -> Dict:
        """Sends Misty a request to perform an action.

        Args:
            action_name (str): The keyword specifying the action to perform.
            data (Union[Dict, str]): The data to send in the request body
            in the form of a data shortcut or a json dictionary.

        Returns:
            Dict: response from the API
        """

        if (
            action_name == "led_trans" and
            isinstance(data, Dict) and
            len(data) >= 2 and
            len(data) <= 4
        ):

            try:
                data = construct_transition_dict(data, self.allowed_data)

            except ValueError as e:
                return {
                    "status": "Failed",
                    "message": "The data is not in correct format.",
                    "details": e,
                }

        data_method = ""

        if isinstance(data, Dict):
            data_method = "dict"

        else:
            data_method = "string"

        return self.perform_action(action_name, data, data_method)
