import json
import urllib.parse
import base64
from enum import Enum
from typing import Dict, List, Union


class EncodingTypes(Enum):
    ascii = 1,
    utf8 = 2,
    urlencoded = 3,
    base64 = 4,
    json_string_escaping = 5


class Encoder:
    @staticmethod
    def encode_string(value: Union[str, bytes], encoding_type: EncodingTypes) -> bytes:

        # If value is already in bytes, I assume that is properly encoded
        if isinstance(value, bytes):
            return value

        if encoding_type == EncodingTypes.ascii:
            return value.encode('ascii', 'ignore')
        elif encoding_type == EncodingTypes.utf8:
            return value.encode('utf8', 'ignore')
        elif encoding_type == EncodingTypes.urlencoded:
            return urllib.parse.quote(value)
        elif encoding_type == EncodingTypes.base64:
            return base64.b64encode(bytes(value))
        elif encoding_type == EncodingTypes.json_string_escaping:
            return json.dumps(value)[1:][:-1].encode('utf8', 'ignore')
        else:
            raise NotImplementedError

    @staticmethod
    def encode_dict(dictionary, encoding_type: EncodingTypes) -> Union[Dict, List[Dict], bytes]:
        if isinstance(dictionary, dict):
            return {Encoder.encode_dict(key, encoding_type): Encoder.encode_dict(value, encoding_type)
                    for key, value in dictionary.items()}
        elif isinstance(dictionary, list):
            return [Encoder.encode_dict(element, encoding_type) for element in dictionary]
        elif isinstance(dictionary, str):
            return Encoder.encode_string(dictionary, encoding_type)
        else:
            return dictionary

    @staticmethod
    def get_ascii_encoded_quotation_mark():
        return Encoder.encode_string("\"", EncodingTypes.ascii)
