"""
Sheet: Command Utilities

Module: command_utils.py
Author: putkoff
Date: 05/31/2023
Version: 0.1.2

This module provides a collection of utilities for executing commands with various functionalities, 
including handling of sudo, logging outputs, and interacting with commands that expect input.

Main Functions:
---------------
1. get_output_text(parent_dir) -> str:
    Provides the path to an 'output.txt' file in a given directory.

2. get_env_value(key, env_path) -> str:
    Fetches the environment value associated with a provided key from a specific .env file path.

3. print_cmd(input, output) -> None:
    Prints the executed command alongside its corresponding output.

4. get_sudo_password(key) -> str:
    Retrieves the sudo password stored in an environment file.

5. cmd_run_sudo(cmd, password, key, output_text) -> None:
    Executes a command with sudo privileges, either using a given password or retrieving it from an environment file.

6. cmd_run(cmd, output_text) -> None:
    Executes a command and captures its output in a specified file.

7. pexpect_cmd_with_args(command, child_runs, output_text) -> int:
    Interacts with a command's expected input using the pexpect library and logs the command's output.

Dependencies:
-------------
- os
- time
- pexpect
- subprocess
- abstract_security.envy_it: find_and_read_env_file, get_env_value
- abstract_utilities.time_utils: get_sleep

Notes:
------
- Ensure the necessary dependencies are installed.
- Make sure you have appropriate permissions to execute commands, especially sudo-based ones.
- Always keep environment files secure and away from public access to ensure sensitive information like passwords remains confidential.
"""
import os
import pexpect
import subprocess
from abstract_security.envy_it import find_and_read_env_file, get_env_value
from .time_utils import get_sleep
def get_output(p):
    """
    Get the output of a subprocess command.

    This function takes a subprocess Popen object as an argument and returns
    the output generated by the command's execution.

    Args:
        p (subprocess.Popen): A Popen object representing a subprocess command.

    Returns:
        tuple: A tuple containing the standard output and standard error streams
               of the executed command.
    """
    return p.communicate()
def get_stdout(result):
    """
    Capture and Store Standard Output from a Subprocess Result

    This line captures the standard output generated by a subprocess command and stores it in the variable 'stdout'.
    The variable 'result' should contain the result of a subprocess command, typically obtained by using functions
    from the 'subprocess' module. The underscore '_' is used as a placeholder to indicate that the standard error
    output is intentionally discarded in this context.

    Parameters:
    - result (subprocess.CompletedProcess): The result of a subprocess command execution.

    Returns:
    - stdout (bytes): The captured standard output from the subprocess.

    Note:
    The standard error output is ignored in this context and not stored.

    Example:
    result = subprocess.run(["ls", "-l"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)

    print("Captured Output:", stdout.decode())
    """
    stdout, _ = result.communicate()
    return stdout
def get_cmd_out(st):
    """
    Get the output of a shell command.

    This function executes a shell command using the 'cmd_input' function and
    retrieves the output generated by the command's execution.

    Args:
        st (str): The shell command to execute.

    Returns:
        tuple: A tuple containing the standard output and standard error streams
               of the executed shell command.
    """
    return get_output(cmd_input(st))
def cmd_input(st):
    """
    Execute a shell command using subprocess.

    Args:
    - st (str): Command to be executed.

    Returns:
    - subprocess.Popen: A Popen object for communication.
    """
    return subprocess.Popen(st, stdout=subprocess.PIPE, shell=True, text=True)
def get_output_text(parent_dir: str = os.getcwd()) -> str:
    """
    Get the path to the 'output.txt' file in the given directory.

    Args:
        parent_dir (str, optional): Directory path. Defaults to the current working directory.

    Returns:
        str: Path to the 'output.txt' file.
    """
    return os.path.join(parent_dir,'output.txt')
def get_env_value(key: str = None, env_path: str = None) -> str:
    """
    Retrieve environment value based on a key from a specified .env file.

    Args:
        key (str, optional): Environment key to search for. Defaults to None.
        env_path (str, optional): Path to start searching for the .env file. Defaults to None.

    Returns:
        str: Value corresponding to the given environment key.
    """
    args={}
    if key != None:
        args["key"]=key
    if env_path != None:
        args["start_path"]=env_path
    return find_and_read_env_file(**args)
def print_cmd(input: str, output: str) -> None:
    """
    Print the input command and its corresponding output.

    Args:
        input (str): The command that was run.
        output (str): Output produced by the command.
    """
    print(f"Command Line Arguments: {input}")
    print(f"Output:\n{output}")
def get_sudo_password(key: str = "SUDO_PASSWORD") -> str:
    """
    Retrieve the sudo password from an environment file.

    Args:
        key (str, optional): Environment key for the sudo password. Defaults to "SUDO_PASSWORD".

    Returns:
        str: The sudo password.
    """
    return find_and_read_env_file(key=key)
def cmd_run_sudo(cmd: str, password: str = None, key: str = None, output_text: str = None) -> None:
    """
    Execute a command with sudo privileges using either provided password or retrieving it from an environment file.

    Args:
        cmd (str): Command to be executed.
        password (str, optional): Password for sudo. Defaults to None.
        key (str, optional): Environment key to retrieve sudo password. Defaults to None.
        output_text (str, optional): Path to store the command output. Defaults to None.
    """
    if password !=None:
        cmd_run(f'echo "{password}" | sudo -S -k {cmd}',output_text)
    elif key != None:
        cmd_run(f'echo "{get_env_value(key)}" | sudo -S -k {cmd}',output_text)
    else:
        cmd_run(f'echo "{get_sudo_password()}" | sudo -S -k {cmd}',output_text)
def cmd_run(cmd: str, output_text: str = None) -> None:
    """
    Execute a command and store its output in the specified file.

    Args:
        cmd (str): Command to be executed.
        output_text (str, optional): Path to store the command output. Defaults to None.
    """
    if output_text == None:
        output_text=get_output_text()
    # Clear the output file before running the command
    with open(get_output_text(), 'w') as f:
        pass
    cmd += f' >> '+output_text+'; echo END_OF_CMD >> '+output_text  # Add the delimiter at the end of cmd
    print(cmd)
    output = subprocess.call(f'gnome-terminal -- bash -c "{cmd}"', shell=True)
    # Wait until the delimiter appears in the output file
    while True:
        get_sleep(sleep_timer=0.5)  # Sleep for a while to reduce CPU usage
        with open(output_text, 'r') as f:
            lines = f.readlines()
            if lines:  # Check if the file is not empty
                last_line = lines[-1].strip()  # Read the last line of the file
                if last_line == 'END_OF_CMD':
                    break  # Break the loop if the delimiter is found
    # Print the command and its output
    with open(output_text, 'r') as f:
        output = f.read().strip()  # Read the entire output
    print_cmd(cmd, output)
    print(output)
    # Delete the output file and the bash script
    os.remove(get_output_text())

def pexpect_cmd_with_args(command: str, child_runs: list, output_text: str = os.getcwd()) -> int:
    """
    Run a command using pexpect and handle its prompts with specified responses.

    Args:
        command (str): Command to be executed.
        child_runs (list): List of prompts and their respective responses.
        output_text (str, optional): Path to store the command output. Defaults to the current working directory.

    Returns:
        int: Exit status of the command.
    """
    child = pexpect.spawn(command)

    for each in child_runs:
        # Wait for the process to prompt for the input and respond with it
        child.expect(each["prompt"])

        # Respond with the corresponding input
        if each["pass"] is not None:
            pass_phrase = each["pass"]
        else:
            args = {}
            if "key" in each:
                if each["key"] is not None:
                    args["key"] = each["key"]
            if "env_path" in each:
                if each["env_path"] is not None:
                    args["start_path"] = each["env_path"]

            pass_phrase = get_env_value(**args)

        child.sendline(pass_phrase)

        print("Output after handling prompt:")
        print(each["prompt"])

    # Wait for the process to finish
    child.expect(pexpect.EOF)
    output = child.before.decode("utf-8")

    # Write output to the output file
    with open(get_output_text(), "w") as f:
        f.write(output)

    print_cmd(command, output)

    return child.exitstatus
