//! Integration tests for terminal operations.

use dirigent_tools::config::TerminalConfig;
use dirigent_tools::terminal::{
    create_terminal, get_terminal_output, kill_terminal, release_terminal,
    wait_for_terminal_exit, CreateTerminalRequest, KillTerminalCommandRequest,
    ReleaseTerminalRequest, TerminalOutputRequest, WaitForTerminalExitRequest,
};

#[tokio::test]
async fn test_terminal_echo_command() {
    let config = TerminalConfig {
        enabled: true,
        default_cwd: Some(std::env::current_dir().unwrap()),
        env_allowlist: vec![],
        command_blocklist: vec![],
        output_byte_limit: 10_000,
        max_runtime_secs: 30,
    };

    // Create terminal with echo command
    #[cfg(windows)]
    let request = CreateTerminalRequest {
        command: "cmd".to_string(),
        args: vec!["/C".to_string(), "echo".to_string(), "Hello World".to_string()],
        cwd: None,
        env: None,
        output_byte_limit: None,
    };

    #[cfg(not(windows))]
    let request = CreateTerminalRequest {
        command: "echo".to_string(),
        args: vec!["Hello World".to_string()],
        cwd: None,
        env: None,
        output_byte_limit: None,
    };

    let create_response = create_terminal(request, &config).await.unwrap();
    let terminal_id = create_response.terminal_id;

    // Wait for command to complete
    tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;

    // Get output
    let output_response = get_terminal_output(
        TerminalOutputRequest {
            terminal_id: terminal_id.clone(),
        },
        &config,
    )
    .await
    .unwrap();

    assert!(output_response.output.contains("Hello World"));

    // Release terminal
    let _ = release_terminal(
        ReleaseTerminalRequest {
            terminal_id: terminal_id.clone(),
        },
        &config,
    )
    .await;
}

#[tokio::test]
async fn test_terminal_wait_for_exit() {
    let config = TerminalConfig {
        enabled: true,
        default_cwd: Some(std::env::current_dir().unwrap()),
        env_allowlist: vec![],
        command_blocklist: vec![],
        output_byte_limit: 10_000,
        max_runtime_secs: 30,
    };

    // Create terminal with a quick command
    #[cfg(windows)]
    let request = CreateTerminalRequest {
        command: "cmd".to_string(),
        args: vec!["/C".to_string(), "exit".to_string(), "0".to_string()],
        cwd: None,
        env: None,
        output_byte_limit: None,
    };

    #[cfg(not(windows))]
    let request = CreateTerminalRequest {
        command: "true".to_string(),
        args: vec![],
        cwd: None,
        env: None,
        output_byte_limit: None,
    };

    let create_response = create_terminal(request, &config).await.unwrap();
    let terminal_id = create_response.terminal_id;

    // Wait for exit
    let wait_response = wait_for_terminal_exit(
        WaitForTerminalExitRequest {
            terminal_id: terminal_id.clone(),
        },
        &config,
    )
    .await
    .unwrap();

    assert_eq!(wait_response.exit_status, 0);

    // Release terminal
    let _ = release_terminal(
        ReleaseTerminalRequest {
            terminal_id: terminal_id.clone(),
        },
        &config,
    )
    .await;
}

#[tokio::test]
async fn test_terminal_kill() {
    let config = TerminalConfig {
        enabled: true,
        default_cwd: Some(std::env::current_dir().unwrap()),
        env_allowlist: vec![],
        command_blocklist: vec![],
        output_byte_limit: 10_000,
        max_runtime_secs: 30,
    };

    // Create terminal with a long-running command
    #[cfg(windows)]
    let request = CreateTerminalRequest {
        command: "cmd".to_string(),
        args: vec!["/C".to_string(), "timeout".to_string(), "/t".to_string(), "10".to_string()],
        cwd: None,
        env: None,
        output_byte_limit: None,
    };

    #[cfg(not(windows))]
    let request = CreateTerminalRequest {
        command: "sleep".to_string(),
        args: vec!["10".to_string()],
        cwd: None,
        env: None,
        output_byte_limit: None,
    };

    let create_response = create_terminal(request, &config).await.unwrap();
    let terminal_id = create_response.terminal_id;

    // Wait a bit to ensure process is running
    tokio::time::sleep(tokio::time::Duration::from_millis(100)).await;

    // Kill the terminal
    let kill_response = kill_terminal(
        KillTerminalCommandRequest {
            terminal_id: terminal_id.clone(),
        },
        &config,
    )
    .await;

    assert!(kill_response.is_ok());

    // Release terminal
    let _ = release_terminal(
        ReleaseTerminalRequest {
            terminal_id: terminal_id.clone(),
        },
        &config,
    )
    .await;
}

#[tokio::test]
async fn test_terminal_disabled() {
    let config = TerminalConfig {
        enabled: false,
        default_cwd: Some(std::env::current_dir().unwrap()),
        env_allowlist: vec![],
        command_blocklist: vec![],
        output_byte_limit: 10_000,
        max_runtime_secs: 30,
    };

    let request = CreateTerminalRequest {
        command: "echo".to_string(),
        args: vec!["test".to_string()],
        cwd: None,
        env: None,
        output_byte_limit: None,
    };

    let result = create_terminal(request, &config).await;
    assert!(result.is_err());
}

#[tokio::test]
async fn test_terminal_command_blocklist() {
    let config = TerminalConfig {
        enabled: true,
        default_cwd: Some(std::env::current_dir().unwrap()),
        env_allowlist: vec![],
        command_blocklist: vec!["rm".to_string(), "del".to_string()],
        output_byte_limit: 10_000,
        max_runtime_secs: 30,
    };

    let request = CreateTerminalRequest {
        command: "rm".to_string(),
        args: vec!["-rf".to_string(), "/".to_string()],
        cwd: None,
        env: None,
        output_byte_limit: None,
    };

    let result = create_terminal(request, &config).await;
    assert!(result.is_err());
}

#[tokio::test]
async fn test_terminal_output_truncation() {
    let config = TerminalConfig {
        enabled: true,
        default_cwd: Some(std::env::current_dir().unwrap()),
        env_allowlist: vec![],
        command_blocklist: vec![],
        output_byte_limit: 100, // Very small buffer
        max_runtime_secs: 30,
    };

    // Create terminal that generates lots of output
    #[cfg(windows)]
    let request = CreateTerminalRequest {
        command: "cmd".to_string(),
        args: vec![
            "/C".to_string(),
            "for".to_string(),
            "/L".to_string(),
            "%i".to_string(),
            "in".to_string(),
            "(1,1,100)".to_string(),
            "do".to_string(),
            "@echo".to_string(),
            "Line %i".to_string(),
        ],
        cwd: None,
        env: None,
        output_byte_limit: Some(100),
    };

    #[cfg(not(windows))]
    let request = CreateTerminalRequest {
        command: "sh".to_string(),
        args: vec![
            "-c".to_string(),
            "for i in $(seq 1 100); do echo Line $i; done".to_string(),
        ],
        cwd: None,
        env: None,
        output_byte_limit: Some(100),
    };

    let create_response = create_terminal(request, &config).await.unwrap();
    let terminal_id = create_response.terminal_id;

    // Wait for command to complete
    tokio::time::sleep(tokio::time::Duration::from_millis(1000)).await;

    // Get output
    let output_response = get_terminal_output(
        TerminalOutputRequest {
            terminal_id: terminal_id.clone(),
        },
        &config,
    )
    .await
    .unwrap();

    // Buffer should be truncated
    assert!(output_response.truncated || output_response.output.len() <= 100);

    // Release terminal
    let _ = release_terminal(
        ReleaseTerminalRequest {
            terminal_id: terminal_id.clone(),
        },
        &config,
    )
    .await;
}
