import re
from typing import Dict, List, Tuple

from pydantic import ValidationError

from ..core.config import T_Host_Style, settings
from ..services.identities import PasswordManager
from .ssh_config import SSH_Config, SSH_Host
from .ssh_parameters import SSHParams


def complete_ssh_host_names(ctx, param, incomplete) -> List[str]:
    ssh_config = SSH_Config().read().parse()
    all_hosts = ssh_config.get_all_host_names()
    return [k for k in all_hosts if k.startswith(incomplete)]


def complete_ssh_group_names(ctx, param, incomplete) -> List[str]:
    ssh_config = SSH_Config().read().parse()
    all_groups = ssh_config.get_all_group_names()
    return [k for k in all_groups if k.startswith(incomplete)]


def complete_params(ctx, param, incomplete) -> List[str]:
    return [k for k in SSHParams.__annotations__.keys() if k.startswith(incomplete)]


def complete_styles(ctx, param, incomplete) -> List[str]:
    return [k for k in T_Host_Style.__args__ if k.startswith(incomplete)]


def complete_identities(ctx, param, incomplete) -> List[str]:
    password_manager = PasswordManager()
    identities = password_manager.get_identities()
    return [k for k in identities if k.startswith(incomplete)]


# We use this functions to give a tuple of group/host names as input, where some names
# can be regexps (with "r:"" prefix), and we evaluate regex based on "all_names" list
# which we use to create a set of expanded host names; direct ones, and expanded ones from
# regex processing. Then return final list...
def expand_names(names: Tuple, all_names: list) -> List[str]:
    selected = set()

    for name in names:
        if name.startswith("r:"):
            name_re = name.split(":")[1]
            # print(f"Got regex type name def - '{name_re}'")
            for i_name in all_names:
                match = re.search(name_re, i_name)
                if match:
                    selected.add(i_name)
        else:
            selected.add(name)
    return list(selected)


# Function to trace connected hosts via "proxyjump" SSH parameter
# For target host, return a list of hosts in order of connections to reach the target host
# In case host is directly reachable (no "proxyjump" param), it will return list of single (target) host
def trace_jumphosts(name: str, config: SSH_Config, ctx, style: str) -> List[SSH_Host]:
    # Keep list of linked hosts via proxyjump option
    traced_hosts: List[SSH_Host] = []

    # we are first checking host that is asked, and then check if that host has a "proxy" defined
    # if proxy is defined, we add found host to traced list, and search attached proxy, it this way we
    # can trace full connection path for later graph processing
    while True:
        # Search for host in current configuration
        if not config.check_host_by_name(name):
            print(
                f"Cannot get info for used host '{name}' as it is not defined in configuration!"
            )
            ctx.exit(1)

        found_host = config.get_host_by_name(name)[0]

        # Set SSH host print style from user input
        found_host.print_style = style

        # Add current host, and its table to traced lists
        traced_hosts.append(found_host)

        # Find proxy info if it exists, if not, break the loop
        proxyjump = None
        host_params = {k.lower(): v for k, v in found_host.params.items()}
        if "proxyjump" in host_params:
            proxyjump = host_params["proxyjump"]
        else:
            for _, params in found_host.inherited_params:
                for key, value in params.items():
                    if key.lower() == "proxyjump":
                        proxyjump = value

        # If there is connected host, we switch "name" and continue the loop
        # Otherwise we exit the loop
        if proxyjump:
            name = proxyjump
        else:
            break

    return traced_hosts


def validate_ssh_params(parameters_input: Tuple[Tuple[str, str]]) -> Dict:
    parameters = {item[0]: item[1] for item in parameters_input}
    if not settings.ssh.SSH_VALIDATE_SSHCONFIG:
        return parameters
    try:
        ssh_params = SSHParams(**parameters)
    except ValidationError as e:
        print("Cannot validate parameters!")
        for error in e.errors():
            loc = error.get("loc")
            if len(loc) > 0:
                print(f"{loc[0]} - {error.get('msg')} ")
            else:
                print(error.get("msg"))
        exit(1)

    valid_ssh_params = ssh_params.model_dump(exclude_none=True)
    return valid_ssh_params


def validate_unique_param(
    host_params: Dict[str, str], new_params: Dict[str, str]
) -> None:
    """
    Remove duplicate Params on SSH config. If there is a keyword in lower case and same with case sensitive.
    Lower will be removed
    """
    host_params_to_delete = []
    for new_param in new_params:
        for host_param in host_params:
            if new_param.lower() == host_param.lower():
                host_params_to_delete.append(host_param)
    for param in host_params_to_delete:
        del host_params[param]
    {**host_params, **new_params}
