#!/usr/bin/env python3
"""Command execution utilities for sweepers.

This module contains the functions responsible for executing commands
with the parameter combinations generated by the sweepers tool.
It handles command formatting, execution, and error reporting.
"""

import subprocess
from typing import Any

from absl import logging
from beartype import beartype


@beartype
def run_command(cmd: list[str], params: dict[str, Any], dry_run: bool = False) -> bool:
    """Run a command with the given parameters.

    This function takes a base command and a dictionary of parameters,
    converts them into command-line flags, and executes the resulting command.
    In dry-run mode, it only prints the command without executing it.

    The parameters are added to the command as follows:
    - Each key in the params dictionary becomes a flag (--key)
    - Each value becomes the value for that flag
    - If the value is a list, it's joined with spaces and passed as a single argument

    Args:
        cmd: List of command parts (program and arguments)
        params: Dictionary of parameter names and values to add as flags
        dry_run: If True, only print the command without executing it

    Returns:
        bool: True if command succeeded or in dry-run mode, False if execution failed

    Examples:
        >>> run_command(["python", "train.py"], {"learning_rate": 0.01, "epochs": 10}, dry_run=True)
        # Prints: python train.py --learning_rate 0.01 --epochs 10
        # Returns: True

        >>> run_command(["echo", "test"], {"layers": [1, 2, 3]}, dry_run=False)
        # Executes: echo test --layers "1 2 3"
        # Returns: True if command succeeds, False otherwise
    """
    # Create command parts, starting with the command itself
    cmd_parts = list(cmd)

    # Add parameters as flags
    logging.debug("Adding parameters to command: %s", params)
    for key, value in params.items():
        if isinstance(value, list):
            # Handle list parameters by joining the elements with spaces
            cmd_parts.append(f"--{key}")
            cmd_parts.append(" ".join(str(item) for item in value))
            logging.debug("Added list parameter %s=%s", key, value)
        else:
            cmd_parts.extend([f"--{key}", str(value)])
            logging.debug("Added parameter %s=%s", key, value)

    # Create the command string
    command_str = " ".join(cmd_parts)

    # In dry-run mode, always output the command to info level
    # Otherwise, keep it at debug level
    if dry_run:
        logging.info(command_str)
        logging.debug("Dry run mode, not executing command")
        return True
    else:
        logging.debug("Executing command: %s", command_str)

    try:
        # Run the command and let output go to the console
        subprocess.run(cmd_parts, check=True, text=True)
        logging.debug("Command executed successfully with return code 0")
        return True
    except subprocess.CalledProcessError as e:
        logging.error("Command failed with error code: %d", e.returncode)
        logging.debug("Failed command: %s", command_str)
        return False
