from collections import OrderedDict

from moto.stepfunctions.parser.asl.component.intrinsic.argument.argument import (
    ArgumentList,
)
from moto.stepfunctions.parser.asl.component.intrinsic.function.statesfunction.states_function import (
    StatesFunction,
)
from moto.stepfunctions.parser.asl.component.intrinsic.functionname.state_function_name_types import (
    StatesFunctionNameType,
)
from moto.stepfunctions.parser.asl.component.intrinsic.functionname.states_function_name import (
    StatesFunctionName,
)
from moto.stepfunctions.parser.asl.eval.environment import Environment


class ArrayUnique(StatesFunction):
    # Removes duplicate values from an array and returns an array containing only unique elements
    #
    # For example:
    # With input
    # {
    #   "inputArray": [1,2,3,3,3,3,3,3,4]
    # }
    #
    # The call
    # States.ArrayUnique($.inputArray)
    #
    # Returns
    # [1,2,3,4]
    def __init__(self, argument_list: ArgumentList):
        super().__init__(
            states_name=StatesFunctionName(
                function_type=StatesFunctionNameType.ArrayUnique
            ),
            argument_list=argument_list,
        )
        if argument_list.size != 1:
            raise ValueError(
                f"Expected 1 argument for function type '{type(self)}', but got: '{argument_list}'."
            )

    def _eval_body(self, env: Environment) -> None:
        self.argument_list.eval(env=env)
        args = env.stack.pop()

        array = args.pop()
        if not isinstance(array, list):
            raise TypeError(f"Expected an array type, but got '{array}'.")

        # Remove duplicates through an ordered set, in this
        # case we consider they key set of an ordered dict.
        items_odict = OrderedDict.fromkeys(array).keys()
        unique_array = list(items_odict)
        env.stack.append(unique_array)
