use serde::{Deserialize, Serialize};
use crate::classifier::{classify, SafetyLevel};
use crate::audit::{AuditEntry, AuditStatus};

/// Execution result for a single step.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct StepResult {
    pub step_index: usize,
    pub command: String,
    pub exit_code: i32,
    pub stdout: String,
    pub stderr: String,
    pub duration_ms: u64,
    pub status: StepStatus,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum StepStatus {
    Success,
    Failed,
    Skipped,
    AwaitingApproval,
    Rejected,
    TimedOut,
}

/// Execute a command after classification and approval check.
/// Destructive commands are NEVER auto-approved (BMad must-have).
pub async fn execute_step(
    command: &str,
    dry_run: bool,
    approval_callback: &dyn Fn(&str, SafetyLevel) -> bool,
) -> StepResult {
    let classification = classify(command);

    // Approval gate
    if classification.requires_approval {
        let approved = approval_callback(command, classification.safety);
        if !approved {
            return StepResult {
                step_index: 0,
                command: command.to_string(),
                exit_code: -1,
                stdout: String::new(),
                stderr: "Approval denied".into(),
                duration_ms: 0,
                status: StepStatus::Rejected,
            };
        }
    }

    if dry_run {
        return StepResult {
            step_index: 0,
            command: command.to_string(),
            exit_code: 0,
            stdout: format!("[DRY RUN] Would execute: {}", command),
            stderr: String::new(),
            duration_ms: 0,
            status: StepStatus::Skipped,
        };
    }

    // Execute via tokio::process
    let start = std::time::Instant::now();
    let output = tokio::process::Command::new("sh")
        .arg("-c")
        .arg(command)
        .output()
        .await;

    let duration = start.elapsed().as_millis() as u64;

    match output {
        Ok(out) => StepResult {
            step_index: 0,
            command: command.to_string(),
            exit_code: out.status.code().unwrap_or(-1),
            stdout: String::from_utf8_lossy(&out.stdout).to_string(),
            stderr: String::from_utf8_lossy(&out.stderr).to_string(),
            duration_ms: duration,
            status: if out.status.success() { StepStatus::Success } else { StepStatus::Failed },
        },
        Err(e) => StepResult {
            step_index: 0,
            command: command.to_string(),
            exit_code: -1,
            stdout: String::new(),
            stderr: e.to_string(),
            duration_ms: duration,
            status: StepStatus::Failed,
        },
    }
}
