# Copyright (c) 2023-2024 Arista Networks, Inc.
# Use of this source code is governed by the Apache License 2.0
# that can be found in the LICENSE file.
"""Tests for `anta.custom_types`.

The intention is only to test here what is not used already in other places.

TODO: Expand later.
"""

from __future__ import annotations

import re

import pytest

from anta.custom_types import (
    REGEX_BGP_IPV4_MPLS_VPN,
    REGEX_BGP_IPV4_UNICAST,
    REGEXP_BGP_IPV4_MPLS_LABELS,
    REGEXP_BGP_L2VPN_AFI,
    REGEXP_EOS_BLACKLIST_CMDS,
    REGEXP_INTERFACE_ID,
    REGEXP_PATH_MARKERS,
    REGEXP_TYPE_EOS_INTERFACE,
    REGEXP_TYPE_HOSTNAME,
    REGEXP_TYPE_VXLAN_SRC_INTERFACE,
    aaa_group_prefix,
    bgp_multiprotocol_capabilities_abbreviations,
    interface_autocomplete,
    interface_case_sensitivity,
)

# ------------------------------------------------------------------------------
# TEST custom_types.py regular expressions
# ------------------------------------------------------------------------------


def test_regexp_path_markers() -> None:
    """Test REGEXP_PATH_MARKERS."""
    # Test strings that should match the pattern
    assert re.search(REGEXP_PATH_MARKERS, "show/bgp/interfaces") is not None
    assert re.search(REGEXP_PATH_MARKERS, "show\\bgp") is not None
    assert re.search(REGEXP_PATH_MARKERS, "show bgp") is not None

    # Test strings that should not match the pattern
    assert re.search(REGEXP_PATH_MARKERS, "aaaa") is None
    assert re.search(REGEXP_PATH_MARKERS, "11111") is None
    assert re.search(REGEXP_PATH_MARKERS, ".[]?<>") is None


def test_regexp_bgp_l2vpn_afi() -> None:
    """Test REGEXP_BGP_L2VPN_AFI."""
    # Test strings that should match the pattern
    assert re.search(REGEXP_BGP_L2VPN_AFI, "l2vpn-evpn") is not None
    assert re.search(REGEXP_BGP_L2VPN_AFI, "l2 vpn evpn") is not None
    assert re.search(REGEXP_BGP_L2VPN_AFI, "l2-vpn evpn") is not None
    assert re.search(REGEXP_BGP_L2VPN_AFI, "l2vpn evpn") is not None
    assert re.search(REGEXP_BGP_L2VPN_AFI, "l2vpnevpn") is not None
    assert re.search(REGEXP_BGP_L2VPN_AFI, "l2 vpnevpn") is not None

    # Test strings that should not match the pattern
    assert re.search(REGEXP_BGP_L2VPN_AFI, "al2vpn evpn") is None
    assert re.search(REGEXP_BGP_L2VPN_AFI, "l2vpn-evpna") is None


def test_regexp_bgp_ipv4_mpls_labels() -> None:
    """Test REGEXP_BGP_IPV4_MPLS_LABELS."""
    assert re.search(REGEXP_BGP_IPV4_MPLS_LABELS, "ipv4-mpls-label") is not None
    assert re.search(REGEXP_BGP_IPV4_MPLS_LABELS, "ipv4 mpls labels") is not None
    assert re.search(REGEXP_BGP_IPV4_MPLS_LABELS, "ipv4Mplslabel") is None


def test_regex_bgp_ipv4_mpls_vpn() -> None:
    """Test REGEX_BGP_IPV4_MPLS_VPN."""
    assert re.search(REGEX_BGP_IPV4_MPLS_VPN, "ipv4-mpls-vpn") is not None
    assert re.search(REGEX_BGP_IPV4_MPLS_VPN, "ipv4_mplsvpn") is None


def test_regex_bgp_ipv4_unicast() -> None:
    """Test REGEX_BGP_IPV4_UNICAST."""
    assert re.search(REGEX_BGP_IPV4_UNICAST, "ipv4-uni-cast") is not None
    assert re.search(REGEX_BGP_IPV4_UNICAST, "ipv4+unicast") is None


def test_regexp_type_interface_id() -> None:
    """Test REGEXP_INTERFACE_ID."""
    intf_id_re = re.compile(f"{REGEXP_INTERFACE_ID}")

    # Test strings that should match the pattern
    assert intf_id_re.search("123") is not None
    assert intf_id_re.search("123/456") is not None
    assert intf_id_re.search("123.456") is not None
    assert intf_id_re.search("123/456.789") is not None


def test_regexp_type_eos_interface() -> None:
    """Test REGEXP_TYPE_EOS_INTERFACE."""
    # Test strings that should match the pattern
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Ethernet0") is not None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Vlan100") is not None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Port-Channel1/0") is not None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Loopback0.1") is not None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Management0/0/0") is not None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Tunnel1") is not None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Vxlan1") is not None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Fabric1") is not None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Dps1") is not None

    # Test strings that should not match the pattern
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Ethernet") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Vlan") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Port-Channel") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Loopback.") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Management/") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Tunnel") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Vxlan") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Fabric") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Dps") is None

    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Ethernet1/a") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Port-Channel-100") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Loopback.10") is None
    assert re.match(REGEXP_TYPE_EOS_INTERFACE, "Management/10") is None


def test_regexp_type_vxlan_src_interface() -> None:
    """Test REGEXP_TYPE_VXLAN_SRC_INTERFACE."""
    # Test strings that should match the pattern
    assert re.match(REGEXP_TYPE_VXLAN_SRC_INTERFACE, "Loopback0") is not None
    assert re.match(REGEXP_TYPE_VXLAN_SRC_INTERFACE, "Loopback1") is not None
    assert re.match(REGEXP_TYPE_VXLAN_SRC_INTERFACE, "Loopback99") is not None
    assert re.match(REGEXP_TYPE_VXLAN_SRC_INTERFACE, "Loopback100") is not None
    assert re.match(REGEXP_TYPE_VXLAN_SRC_INTERFACE, "Loopback8190") is not None
    assert re.match(REGEXP_TYPE_VXLAN_SRC_INTERFACE, "Loopback8199") is not None

    # Test strings that should not match the pattern
    assert re.match(REGEXP_TYPE_VXLAN_SRC_INTERFACE, "Loopback") is None
    assert re.match(REGEXP_TYPE_VXLAN_SRC_INTERFACE, "Loopback9001") is None
    assert re.match(REGEXP_TYPE_VXLAN_SRC_INTERFACE, "Loopback9000") is None


def test_regexp_type_hostname() -> None:
    """Test REGEXP_TYPE_HOSTNAME."""
    # Test strings that should match the pattern
    assert re.match(REGEXP_TYPE_HOSTNAME, "hostname") is not None
    assert re.match(REGEXP_TYPE_HOSTNAME, "hostname.com") is not None
    assert re.match(REGEXP_TYPE_HOSTNAME, "host-name.com") is not None
    assert re.match(REGEXP_TYPE_HOSTNAME, "host.name.com") is not None
    assert re.match(REGEXP_TYPE_HOSTNAME, "host-name1.com") is not None

    # Test strings that should not match the pattern
    assert re.match(REGEXP_TYPE_HOSTNAME, "-hostname.com") is None
    assert re.match(REGEXP_TYPE_HOSTNAME, ".hostname.com") is None
    assert re.match(REGEXP_TYPE_HOSTNAME, "hostname-.com") is None
    assert re.match(REGEXP_TYPE_HOSTNAME, "hostname..com") is None


@pytest.mark.parametrize(
    ("test_string", "expected"),
    [
        ("reload", True),  # matches "^reload.*"
        ("reload now", True),  # matches "^reload.*"
        ("configure terminal", True),  # matches "^conf\w*\s*(terminal|session)*"
        ("conf t", True),  # matches "^conf\w*\s*(terminal|session)*"
        ("write memory", True),  # matches "^wr\w*\s*\w+"
        ("wr mem", True),  # matches "^wr\w*\s*\w+"
        ("show running-config", False),  # does not match any regex
        ("no shutdown", False),  # does not match any regex
        ("", False),  # empty string does not match any regex
    ],
)
def test_regexp_eos_blacklist_cmds(test_string: str, expected: bool) -> None:
    """Test REGEXP_EOS_BLACKLIST_CMDS."""

    def matches_any_regex(string: str, regex_list: list[str]) -> bool:
        """
        Check if a string matches at least one regular expression in a list.

        :param string: The string to check.
        :param regex_list: A list of regular expressions.
        :return: True if the string matches at least one regular expression, False otherwise.
        """
        return any(re.match(regex, string) for regex in regex_list)

    assert matches_any_regex(test_string, REGEXP_EOS_BLACKLIST_CMDS) == expected


# ------------------------------------------------------------------------------
# TEST custom_types.py functions
# ------------------------------------------------------------------------------


def test_interface_autocomplete_success() -> None:
    """Test interface_autocomplete with valid inputs."""
    assert interface_autocomplete("et1") == "Ethernet1"
    assert interface_autocomplete("et1/1") == "Ethernet1/1"
    assert interface_autocomplete("et1.1") == "Ethernet1.1"
    assert interface_autocomplete("et1/1.1") == "Ethernet1/1.1"
    assert interface_autocomplete("eth2") == "Ethernet2"
    assert interface_autocomplete("po3") == "Port-Channel3"
    assert interface_autocomplete("lo4") == "Loopback4"


def test_interface_autocomplete_no_alias() -> None:
    """Test interface_autocomplete with inputs that don't have aliases."""
    assert interface_autocomplete("GigabitEthernet1") == "GigabitEthernet1"
    assert interface_autocomplete("Vlan10") == "Vlan10"
    assert interface_autocomplete("Tunnel100") == "Tunnel100"


def test_interface_autocomplete_failure() -> None:
    """Trigger ValueError for interface_autocomplete."""
    with pytest.raises(ValueError, match="Could not parse interface ID in interface"):
        interface_autocomplete("ThisIsNotAnInterface")


@pytest.mark.parametrize(
    ("str_input", "expected_output"),
    [
        pytest.param("L2VPNEVPN", "l2VpnEvpn", id="l2VpnEvpn"),
        pytest.param("ipv4-mplsLabels", "ipv4MplsLabels", id="ipv4MplsLabels"),
        pytest.param("ipv4-mpls-vpn", "ipv4MplsVpn", id="ipv4MplsVpn"),
        pytest.param("ipv4-unicast", "ipv4Unicast", id="ipv4Unicast"),
        pytest.param("BLAH", "BLAH", id="unmatched"),
    ],
)
def test_bgp_multiprotocol_capabilities_abbreviationsh(str_input: str, expected_output: str) -> None:
    """Test bgp_multiprotocol_capabilities_abbreviations."""
    assert bgp_multiprotocol_capabilities_abbreviations(str_input) == expected_output


def test_aaa_group_prefix_known_method() -> None:
    """Test aaa_group_prefix with a known method."""
    assert aaa_group_prefix("local") == "local"
    assert aaa_group_prefix("none") == "none"
    assert aaa_group_prefix("logging") == "logging"


def test_aaa_group_prefix_unknown_method() -> None:
    """Test aaa_group_prefix with an unknown method."""
    assert aaa_group_prefix("demo") == "group demo"
    assert aaa_group_prefix("group1") == "group group1"


def test_interface_case_sensitivity_lowercase() -> None:
    """Test interface_case_sensitivity with lowercase inputs."""
    assert interface_case_sensitivity("ethernet") == "Ethernet"
    assert interface_case_sensitivity("vlan") == "Vlan"
    assert interface_case_sensitivity("loopback") == "Loopback"


def test_interface_case_sensitivity_mixed_case() -> None:
    """Test interface_case_sensitivity with mixed case inputs."""
    assert interface_case_sensitivity("Ethernet") == "Ethernet"
    assert interface_case_sensitivity("Vlan") == "Vlan"
    assert interface_case_sensitivity("Loopback") == "Loopback"


def test_interface_case_sensitivity_uppercase() -> None:
    """Test interface_case_sensitivity with uppercase inputs."""
    assert interface_case_sensitivity("ETHERNET") == "ETHERNET"
    assert interface_case_sensitivity("VLAN") == "VLAN"
    assert interface_case_sensitivity("LOOPBACK") == "LOOPBACK"
