"""
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_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)
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, print_output: bool = False) -> None:
    if output_text is None:
        output_text = get_output_text()

    # Clear output file
    with open(output_text, 'w') as f:
        pass

    # Append output redirection
    full_cmd = f'{cmd} >> {output_text}; echo END_OF_CMD >> {output_text}'

    if print_output:
        print(full_cmd)

    subprocess.call(full_cmd, shell=True)

    # Wait until END_OF_CMD appears
    while True:
        get_sleep(sleep_timer=0.5)
        with open(output_text, 'r') as f:
            lines = f.readlines()
            if lines and lines[-1].strip() == 'END_OF_CMD':
                break

    if print_output:
        with open(output_text, 'r') as f:
            print_cmd(full_cmd, f.read().strip())

    os.remove(output_text)

def pexpect_cmd_with_args(command: str, child_runs: list, output_text: str = os.getcwd(),print_output:bool=False) -> 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)
        if print_output:
            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)
    if print_output:
        print_cmd(command, output)

    return child.exitstatus
