from uuid import UUID

from datalogue.errors import DtlError, _enum_parse_error, _invalid_parameter_error
from datalogue.dtl_utils import _parse_string_list, SerializableStringEnum
import abc
from typing import Union, List, Optional


class Transformation(abc.ABC):
    type_field = "type"

    def __init__(self, transformation_type: str):
        self.type = transformation_type
        super().__init__()

    def _base_payload(self) -> dict:
        return dict([(Transformation.type_field, self.type)])

    @abc.abstractmethod
    def _as_payload(self) -> dict:
        """
        Represents the transformation in its dictionary construction

        :return:
        """


class RegexTransformation(Transformation):
    def __init__(
        self,
        regex: Optional[str],
        regex_id: Optional[UUID],
        regex_transformation_type: str,
    ):
        Transformation.__init__(self, regex_transformation_type)
        if regex is None and regex_id is None:
            raise _invalid_parameter_error(
                "neither regex nor regex_id is/are not defined!",
                regex_transformation_type,
            )
        if regex is not None and regex_id is not None:
            raise _invalid_parameter_error(
                "both regex and regex_id are defined! Please provide only either.",
                regex_transformation_type,
            )


class DataType(SerializableStringEnum):
    """
    Data Types that can be specified in the pipeline
    """

    String = "String"
    Boolean = "Boolean"
    Integer = "Integer"
    DateTime = "DateTime"
    Double = "Double"

    @staticmethod
    def parse_error(s: str) -> str:
        return _enum_parse_error("data type", s)

    @staticmethod
    def from_str(string: str) -> Union[DtlError, "DataType"]:
        return SerializableStringEnum.from_str(DataType)(string)


def _array_from_dict(
    json: dict, transformation_type: str, array_field_key: str
) -> Union[DtlError, List[str]]:
    """
    Generic method to extract an array from a json transformation dictionary

    :param json: dictionary built from json
    :param transformation_type: type of the transformation
    :param array_field_key: key at which the array is
    :return:
    """
    if json.get(Transformation.type_field) != transformation_type:
        return DtlError("Dictionary input is not of type %s" % transformation_type)

    array_field = json.get(array_field_key)
    if array_field is None:
        return DtlError(
            "'%s' is missing from the json transformation" % array_field_key
        )

    array = _parse_string_list(array_field)

    if isinstance(array, DtlError):
        return array

    return array


class ByClass:
    def __init__(self, class_id: Union[str, UUID]):
        self.class_id = class_id
        """
        Targets data classified as the specified class
        :param class_id: id of the class with which to specify as a transformation target
        """

    def __repr__(self):
        return "ByClass(" f"class_id= {self.class_id!r})"


class ByFieldName:
    def __init__(self, field_name: List[str]):
        self.field_name = field_name
        """
        Targets data residing under the specified field name
    
        :param field_name: name of the field to specify as a transformation target
        """

    def __repr__(self):
        return "ByFieldName(" f"field_name= {self.field_name!r})"
