import copy

from glom import glom, assign, delete

from optimus.helpers.core import val_to_list
from optimus.infer import is_list_value

ACTIONS_PATH = "transformations.actions"


class Meta:

    @staticmethod
    def set(meta, spec=None, value=None, missing=dict) -> dict:
        """
        Set metadata in a dataframe columns
        :param meta: Meta data to be modified
        :param spec: path to the key to be modified
        :param value: dict value
        :param missing:
        :return:
        """
        if spec is not None:
            data = copy.deepcopy(meta)
            assign(data, spec, value, missing=missing)
        else:
            data = value

        return data

    @staticmethod
    def reset(meta, spec=None) -> dict:
        """
        Set metadata in a dataframe columns
        :param meta: Meta data to be modified
        :param spec: path to the key to be modified
        :param value: dict value
        :param missing:
        :return:
        """
        if spec is not None:
            data = copy.deepcopy(meta)
            delete(data, spec, ignore_missing=True)
        else:
            data = meta

        return data

    @staticmethod
    def get(meta, spec=None) -> dict:
        """
        Get metadata from a dataframe column
        :param meta:Meta data to be modified
        :param spec: path to the key to be modified
        :return: dict
        """
        if spec is not None:
            data = glom(meta, spec, skip_exc=KeyError)
        else:
            data = meta
        return copy.deepcopy(data)

    @staticmethod
    def reset_actions(meta):
        """
        Reset the data frame metadata
        :param meta: Meta data to be modified
        :return:
        """

        return Meta.set(meta, ACTIONS_PATH, [])


    @staticmethod
    def columns(meta, value) -> dict:
        """
        Shortcut to cache the columns in a dataframe
        :param meta: Meta data to be modified
        :param value:
        :return: dict (Meta)
        """
        value = val_to_list(value)
        for v in value:
            meta = Meta.update(meta, "transformations.columns", v, list)
        return meta

    @staticmethod
    def action(meta, name, value) -> dict:
        """
        Shortcut to add actions to a dataframe
        :param meta: Meta data to be modified
        :param name: Action name
        :param value: Value to be added
        :return: dict (Meta)
        """
        if not is_list_value(value):
            value = [value]
        for v in value:
            meta = Meta.update(meta, ACTIONS_PATH, {name: v}, list)
        return meta

    @staticmethod
    def update(meta, path, value, default=list) -> dict:
        """
        Update meta data in a key
        :param meta: Meta data to be modified
        :param path: Path indise the dict to be modified
        :param value: New key value
        :param default:
        :return: dict (Meta)
        """

        new_meta = copy.deepcopy(meta)

        if new_meta is None:
            new_meta = {}

        elements = path.split(".")
        _element = new_meta
        for i, ele in enumerate(elements):
            if ele not in _element and not len(elements) - 1 == i:
                _element[ele] = {}

            if len(elements) - 1 == i:
                if default is list:
                    _element[ele] = _element.get(ele, [])
                    _element[ele].append(value)
                elif default is dict:
                    _element[ele] = _element.get(ele, {})
                    _element[ele].update(value)
            else:
                _element = _element[ele]

        return new_meta