//! Command output types.

/// The output of an executed SSH command.
///
/// Contains the standard output, standard error, and exit status
/// of the executed command.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct CommandOutput {
    /// The standard output from the command.
    pub stdout: String,

    /// The standard error from the command.
    pub stderr: String,

    /// The exit status code of the command.
    /// 0 typically indicates success, while non-zero indicates an error.
    pub exit_status: i32,
}

impl CommandOutput {
    /// Creates a new `CommandOutput` instance.
    ///
    /// # Arguments
    ///
    /// * `stdout` - The standard output from the command
    /// * `stderr` - The standard error from the command
    /// * `exit_status` - The exit status code
    ///
    /// # Examples
    ///
    /// ```
    /// use lmrc_ssh::CommandOutput;
    ///
    /// let output = CommandOutput::new(
    ///     "Hello, World!".to_string(),
    ///     String::new(),
    ///     0
    /// );
    /// assert_eq!(output.stdout, "Hello, World!");
    /// assert!(output.is_success());
    /// ```
    pub fn new(stdout: String, stderr: String, exit_status: i32) -> Self {
        Self {
            stdout,
            stderr,
            exit_status,
        }
    }

    /// Returns `true` if the command executed successfully (exit status 0).
    ///
    /// # Examples
    ///
    /// ```
    /// use lmrc_ssh::CommandOutput;
    ///
    /// let success = CommandOutput::new("output".to_string(), String::new(), 0);
    /// assert!(success.is_success());
    ///
    /// let failure = CommandOutput::new(String::new(), "error".to_string(), 1);
    /// assert!(!failure.is_success());
    /// ```
    pub fn is_success(&self) -> bool {
        self.exit_status == 0
    }

    /// Returns `true` if the command failed (non-zero exit status).
    ///
    /// # Examples
    ///
    /// ```
    /// use lmrc_ssh::CommandOutput;
    ///
    /// let output = CommandOutput::new(String::new(), "error".to_string(), 1);
    /// assert!(output.is_failure());
    /// ```
    pub fn is_failure(&self) -> bool {
        !self.is_success()
    }

    /// Returns the combined output (stdout + stderr).
    ///
    /// # Examples
    ///
    /// ```
    /// use lmrc_ssh::CommandOutput;
    ///
    /// let output = CommandOutput::new(
    ///     "line1\n".to_string(),
    ///     "error1\n".to_string(),
    ///     0
    /// );
    /// assert_eq!(output.combined_output(), "line1\nerror1\n");
    /// ```
    pub fn combined_output(&self) -> String {
        format!("{}{}", self.stdout, self.stderr)
    }
}

impl std::fmt::Display for CommandOutput {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Exit Status: {}\nStdout:\n{}\nStderr:\n{}",
            self.exit_status, self.stdout, self.stderr
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_new_output() {
        let output = CommandOutput::new("test".to_string(), "err".to_string(), 0);
        assert_eq!(output.stdout, "test");
        assert_eq!(output.stderr, "err");
        assert_eq!(output.exit_status, 0);
    }

    #[test]
    fn test_is_success() {
        let success = CommandOutput::new(String::new(), String::new(), 0);
        assert!(success.is_success());
        assert!(!success.is_failure());

        let failure = CommandOutput::new(String::new(), String::new(), 1);
        assert!(failure.is_failure());
        assert!(!failure.is_success());
    }

    #[test]
    fn test_combined_output() {
        let output = CommandOutput::new("out\n".to_string(), "err\n".to_string(), 0);
        assert_eq!(output.combined_output(), "out\nerr\n");
    }

    #[test]
    fn test_display() {
        let output = CommandOutput::new("stdout".to_string(), "stderr".to_string(), 0);
        let display = format!("{}", output);
        assert!(display.contains("Exit Status: 0"));
        assert!(display.contains("stdout"));
        assert!(display.contains("stderr"));
    }
}
