import unittest
from enum import Enum

from robot.running.arguments.argumentspec import ArgInfo, ArgumentSpec
from robot.utils.asserts import assert_equal


class TestStringRepr(unittest.TestCase):

    def test_empty(self):
        self._verify("")

    def test_normal(self):
        self._verify("a, b", ["a", "b"])

    def test_non_ascii_names(self):
        self._verify("nön, äscii", ["nön", "äscii"])

    def test_default(self):
        self._verify("a, b=c", ["a", "b"], defaults={"b": "c"})
        self._verify("nön=äscii", ["nön"], defaults={"nön": "äscii"})
        self._verify("i=42", ["i"], defaults={"i": 42})

    def test_default_as_bytes(self):
        self._verify("b=ytes", ["b"], defaults={"b": b"ytes"})
        self._verify("ä=\xe4", ["ä"], defaults={"ä": b"\xe4"})

    def test_type_as_class(self):
        self._verify("a: int, b: bool", ["a", "b"], types={"a": int, "b": bool})

    def test_type_as_string(self):
        self._verify(
            "a: Integer, b: Boolean",
            ["a", "b"],
            types={"a": "Integer", "b": "Boolean"},
        )

    def test_type_and_default(self):
        self._verify("arg: int = 1", ["arg"], types=[int], defaults={"arg": 1})

    def test_positional_only(self):
        self._verify("a, /", positional_only=["a"])
        self._verify("a, /, b", positional_only=["a"], positional_or_named=["b"])

    def test_positional_only_with_default(self):
        self._verify("a, b=2, /", positional_only=["a", "b"], defaults={"b": 2})

    def test_positional_only_with_type(self):
        self._verify("a: int, b, /", positional_only=["a", "b"], types=[int])
        self._verify(
            "a: int, b: float, /, c: bool, d",
            positional_only=["a", "b"],
            positional_or_named=["c", "d"],
            types=[int, float, bool],
        )

    def test_positional_only_with_type_and_default(self):
        self._verify(
            "a: int = 1, b=2, /",
            positional_only=["a", "b"],
            types={"a": int},
            defaults={"a": 1, "b": 2},
        )

    def test_varargs(self):
        self._verify("*varargs", var_positional="varargs")
        self._verify("a, *b", positional_or_named=["a"], var_positional="b")

    def test_varargs_with_type(self):
        self._verify(
            "*varargs: float",
            var_positional="varargs",
            types={"varargs": float},
        )
        self._verify(
            "a: int, *b: list[int]",
            positional_or_named=["a"],
            var_positional="b",
            types=[int, "list[int]"],
        )

    def test_named_only_without_varargs(self):
        self._verify("*, kwo", named_only=["kwo"])

    def test_named_only_with_varargs(self):
        self._verify(
            "*varargs, k1, k2",
            var_positional="varargs",
            named_only=["k1", "k2"],
        )

    def test_named_only_with_default(self):
        self._verify(
            "*, k=1, w, o=3",
            named_only=["k", "w", "o"],
            defaults={"k": 1, "o": 3},
        )

    def test_named_only_with_types(self):
        self._verify(
            "*, k: int, w: float, o",
            named_only=["k", "w", "o"],
            types=[int, float],
        )
        self._verify(
            "x: int, *y: float, z: bool",
            positional_or_named=["x"],
            var_positional="y",
            named_only=["z"],
            types=[int, float, bool],
        )

    def test_named_only_with_types_and_defaults(self):
        self._verify(
            "x: int = 1, *, y: float, z: bool = 3",
            positional_or_named=["x"],
            named_only=["y", "z"],
            types=[int, float, bool],
            defaults={"x": 1, "z": 3},
        )

    def test_kwargs(self):
        self._verify("**kws", var_named="kws")
        self._verify(
            "a, b=c, *d, e=f, g, **h",
            positional_or_named=["a", "b"],
            var_positional="d",
            named_only=["e", "g"],
            var_named="h",
            defaults={"b": "c", "e": "f"},
        )

    def test_kwargs_with_types(self):
        self._verify(
            "**kws: dict[str, int]",
            var_named="kws",
            types={"kws": "dict[str, int]"},
        )
        self._verify(
            "a: int, /, b: float, *c: list[int], d: bool, **e: dict[int, str]",
            positional_only=["a"],
            positional_or_named=["b"],
            var_positional="c",
            named_only=["d"],
            var_named="e",
            types=[int, float, "list[int]", bool, "dict[int, str]"],
        )

    def test_enum_with_few_members(self):
        class Small(Enum):
            ONLY_FEW_MEMBERS = 1
            SO_THEY_CAN = 2
            BE_PRETTY_LONG = 3

        self._verify("e: Small", ["e"], types=[Small])

    def test_enum_with_many_short_members(self):
        class ManyShort(Enum):
            ONE = 1
            TWO = 2
            THREE = 3
            FOUR = 4
            FIVE = 5
            SIX = 6

        self._verify("e: ManyShort", ["e"], types=[ManyShort])

    def test_enum_with_many_long_members(self):
        class Big(Enum):
            MANY_MEMBERS = 1
            THAT_ARE_LONGISH = 2
            MEANS_THEY_ALL_DO_NOT_FIT = 3
            AND_SOME_ARE_OMITTED = 4
            FROM_THE_END = 5

        self._verify("e: Big", ["e"], types=[Big])

    def _verify(self, expected, positional_or_named=(), **config):
        spec = ArgumentSpec(positional_or_named=positional_or_named, **config)
        assert_equal(str(spec), expected)
        assert_equal(bool(spec), bool(expected))


class TestName(unittest.TestCase):

    def test_static(self):
        assert_equal(ArgumentSpec("xxx").name, "xxx")

    def test_dynamic(self):
        assert_equal(ArgumentSpec(lambda: "xxx").name, "xxx")


class TestArgInfo(unittest.TestCase):

    def test_required_without_default(self):
        for kind in (
            ArgInfo.POSITIONAL_ONLY,
            ArgInfo.POSITIONAL_OR_NAMED,
            ArgInfo.NAMED_ONLY,
        ):
            assert_equal(ArgInfo(kind).required, True)
            assert_equal(ArgInfo(kind, default=None).required, False)

    def test_never_required(self):
        for kind in (
            ArgInfo.VAR_POSITIONAL,
            ArgInfo.VAR_NAMED,
            ArgInfo.POSITIONAL_ONLY_MARKER,
            ArgInfo.NAMED_ONLY_MARKER,
        ):
            assert_equal(ArgInfo(kind).required, False)


if __name__ == "__main__":
    unittest.main()
