// RLM Executor - Docker Tier Binary
// Executes commands in isolated Docker containers
// Input: JSON via args (command, args, stdin)
// Output: JSON via stdout (stdout, stderr, exit_code)

use std::io::{self, Read};
use std::process::{Command, Stdio};

use serde::{Deserialize, Serialize};

/// Executor input (from dispatcher)
#[derive(Debug, Deserialize)]
struct ExecutorInput {
    command: String,
    #[serde(default)]
    args: Vec<String>,
    #[serde(default)]
    stdin: Option<String>,
}

/// Executor output (to dispatcher)
#[derive(Debug, Serialize)]
struct ExecutorOutput {
    stdout: String,
    stderr: String,
    exit_code: i32,
}

fn main() -> anyhow::Result<()> {
    // Parse command from args
    let args: Vec<String> = std::env::args().collect();

    if args.len() < 2 {
        eprintln!("Usage: executor <command> [args...]");
        std::process::exit(1);
    }

    let command_name = &args[1];
    let command_args = if args.len() > 2 {
        &args[2..]
    } else {
        &[]
    };

    // Read stdin if available
    let mut stdin_content = String::new();
    let _ = io::stdin().read_to_string(&mut stdin_content);

    // Execute command based on type
    let result = match command_name.as_str() {
        "peek" => execute_peek(command_args, &stdin_content),
        "grep" => execute_grep(command_args, &stdin_content),
        "slice" => execute_slice(command_args, &stdin_content),
        _ => {
            // Generic command execution (for complex tasks)
            execute_generic(command_name, command_args, &stdin_content)
        }
    };

    // Output result as JSON
    let output = serde_json::to_string(&result)?;
    println!("{}", output);

    std::process::exit(result.exit_code);
}

/// Execute peek command (head functionality)
fn execute_peek(args: &[String], stdin: &str) -> ExecutorOutput {
    let lines = if args.is_empty() {
        10
    } else {
        args[0].parse().unwrap_or(10)
    };

    let output: String = stdin
        .lines()
        .take(lines)
        .map(|line| format!("{}\n", line))
        .collect();

    ExecutorOutput {
        stdout: output,
        stderr: String::new(),
        exit_code: 0,
    }
}

/// Execute grep command
fn execute_grep(args: &[String], stdin: &str) -> ExecutorOutput {
    if args.is_empty() {
        return ExecutorOutput {
            stdout: String::new(),
            stderr: "grep: missing pattern\n".to_string(),
            exit_code: 1,
        };
    }

    let pattern = &args[0];
    let mut output = String::new();

    for line in stdin.lines() {
        if line.contains(pattern) {
            output.push_str(line);
            output.push('\n');
        }
    }

    ExecutorOutput {
        stdout: output,
        stderr: String::new(),
        exit_code: 0,
    }
}

/// Execute slice command
fn execute_slice(args: &[String], stdin: &str) -> ExecutorOutput {
    if args.len() < 2 {
        return ExecutorOutput {
            stdout: String::new(),
            stderr: "slice: requires <start> <end> arguments\n".to_string(),
            exit_code: 1,
        };
    }

    let start = args[0].parse::<usize>().unwrap_or(0);
    let end = args[1].parse::<usize>().unwrap_or(0);

    let output = if end > start && end <= stdin.len() {
        stdin[start..end].to_string()
    } else if start < stdin.len() {
        stdin[start..].to_string()
    } else {
        String::new()
    };

    ExecutorOutput {
        stdout: output,
        stderr: String::new(),
        exit_code: 0,
    }
}

/// Execute generic command (for complex tasks)
fn execute_generic(command: &str, args: &[String], stdin: &str) -> ExecutorOutput {
    let mut cmd = Command::new(command);
    cmd.args(args);
    cmd.stdin(Stdio::piped());
    cmd.stdout(Stdio::piped());
    cmd.stderr(Stdio::piped());

    let mut child = match cmd.spawn() {
        Ok(child) => child,
        Err(e) => {
            return ExecutorOutput {
                stdout: String::new(),
                stderr: format!("Failed to spawn command: {}\n", e),
                exit_code: 127,
            }
        }
    };

    // Write stdin if provided
    if !stdin.is_empty() {
        if let Some(mut child_stdin) = child.stdin.take() {
            use std::io::Write;
            let _ = child_stdin.write_all(stdin.as_bytes());
        }
    }

    // Wait for completion and capture output
    let output = match child.wait_with_output() {
        Ok(output) => output,
        Err(e) => {
            return ExecutorOutput {
                stdout: String::new(),
                stderr: format!("Failed to wait for command: {}\n", e),
                exit_code: 127,
            }
        }
    };

    ExecutorOutput {
        stdout: String::from_utf8_lossy(&output.stdout).to_string(),
        stderr: String::from_utf8_lossy(&output.stderr).to_string(),
        exit_code: output.status.code().unwrap_or(1),
    }
}
