from functools import lru_cache
from importlib import import_module

from colorama import Fore, Style

from prowler.lib.cli.sensitive import SENSITIVE_ARGUMENTS as COMMON_SENSITIVE_ARGUMENTS
from prowler.lib.logger import logger
from prowler.providers.common.provider import Provider, providers_path

REDACTED_VALUE = "REDACTED"


@lru_cache(maxsize=None)
def get_sensitive_arguments() -> frozenset:
    """Collect SENSITIVE_ARGUMENTS from all provider argument modules and the common parser."""
    sensitive: set[str] = set()

    # Common parser sensitive arguments (e.g., --shodan)
    sensitive.update(COMMON_SENSITIVE_ARGUMENTS)

    # Provider-specific sensitive arguments
    for provider in Provider.get_available_providers():
        try:
            module = import_module(
                f"{providers_path}.{provider}.lib.arguments.arguments"
            )
            sensitive.update(getattr(module, "SENSITIVE_ARGUMENTS", frozenset()))
        except Exception as error:
            logger.debug(f"Could not load SENSITIVE_ARGUMENTS from {provider}: {error}")

    return frozenset(sensitive)


def redact_argv(argv: list[str]) -> str:
    """Redact values of sensitive CLI flags from an argument list.

    Handles both ``--flag value`` and ``--flag=value`` syntax.
    Returns a single joined string suitable for display.
    """
    sensitive = get_sensitive_arguments()
    result: list[str] = []
    skip_next = False

    for i, arg in enumerate(argv):
        if skip_next:
            result.append(REDACTED_VALUE)
            skip_next = False
            continue

        # Handle --flag=value syntax
        if "=" in arg:
            flag = arg.split("=", 1)[0]
            if flag in sensitive:
                result.append(f"{flag}={REDACTED_VALUE}")
                continue

        # Handle --flag value syntax
        if arg in sensitive:
            result.append(arg)
            # Only redact the next token if it exists and is not another flag
            if i + 1 < len(argv) and not argv[i + 1].startswith("-"):
                skip_next = True
            continue

        result.append(arg)

    return " ".join(result)


def warn_sensitive_argument_values(argv: list[str]) -> None:
    """Log a warning for each sensitive CLI flag that was passed with an explicit value.

    Scans the raw argv list (not parsed args) to detect when users pass
    secret values directly on the command line instead of using environment
    variables. Handles both ``--flag value`` and ``--flag=value`` syntax.

    Args:
        argv: The argument list to check (typically ``sys.argv[1:]``).
    """
    sensitive = get_sensitive_arguments()
    if not sensitive:
        return

    use_color = "--no-color" not in argv
    flags_with_values: list[str] = []

    for i, arg in enumerate(argv):
        # --flag=value syntax
        if "=" in arg:
            flag = arg.split("=", 1)[0]
            if flag in sensitive:
                flags_with_values.append(flag)
            continue

        # --flag value syntax
        if arg in sensitive:
            if i + 1 < len(argv) and not argv[i + 1].startswith("-"):
                flags_with_values.append(arg)

    for flag in flags_with_values:
        if use_color:
            logger.warning(
                f"{Fore.YELLOW}{Style.BRIGHT}WARNING:{Style.RESET_ALL}{Fore.YELLOW} "
                f"Passing a value directly to {flag} is not recommended. "
                f"Use the corresponding environment variable instead to avoid "
                f"exposing secrets in process listings and shell history.{Style.RESET_ALL}"
            )
        else:
            logger.warning(
                f"Passing a value directly to {flag} is not recommended. "
                f"Use the corresponding environment variable instead to avoid "
                f"exposing secrets in process listings and shell history."
            )
