//! Terminal wait-for-exit operation.
//!
//! **Status**: Not yet implemented (TOOLS-TERM-03)
//!
//! This module will implement:
//! - Blocking wait for process completion
//! - Runtime timeout enforcement
//! - Exit status return

use crate::config::TerminalConfig;
use crate::error::{ToolError, ToolResult};
use serde::{Deserialize, Serialize};

/// Request to wait for terminal to exit.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WaitForTerminalExitRequest {
    /// Terminal ID from create response.
    pub terminal_id: String,
}

/// Response with exit status.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct WaitForTerminalExitResponse {
    /// Process exit status code.
    pub exit_status: i32,
}

/// Wait for terminal process to complete.
///
/// **Status**: Implemented (TOOLS-TERM-03)
///
/// ## Implementation
///
/// This function:
/// 1. Looks up TerminalId in registry
/// 2. Gets process handle
/// 3. If already exited → returns cached exit status immediately
/// 4. If still running:
///    - Awaits process completion (tokio child.wait())
///    - Enforces `max_runtime_secs` timeout via tokio::time::timeout
///    - If timeout → kills process and returns error
/// 5. Caches exit status in registry
/// 6. Returns WaitForTerminalExitResponse
///
/// ## Timeout Behavior
///
/// - Uses `max_runtime_secs` from TerminalConfig
/// - On timeout:
///   - Process is killed (SIGKILL/TerminateProcess)
///   - Returns `ToolError::TerminalError` with timeout message
///   - Terminal remains in registry (can still get output)
///
/// ## Blocking vs Polling
///
/// - `wait_for_exit()` → Blocks until completion or timeout
/// - `get_output()` → Returns immediately with current output
///
/// Use `wait_for_exit()` when you want to ensure completion before proceeding.
///
/// ## Error Cases
///
/// - Invalid/unknown TerminalId → `ToolError::TerminalNotFound`
/// - Terminal released → `ToolError::TerminalNotFound`
/// - Timeout exceeded → `ToolError::TerminalError`
///
/// ## See Also
///
/// - Task spec: `docs/building/04_acp_client/04_tasks_02_tools_and_sandboxing.md#TOOLS-TERM-03`
/// - Get output: `output.rs`
pub async fn wait_for_terminal_exit(
    request: WaitForTerminalExitRequest,
    config: &TerminalConfig,
) -> ToolResult<WaitForTerminalExitResponse> {
    use crate::terminal::registry::global_registry;
    use std::time::Duration;

    let registry = global_registry();
    let terminal_id = request.terminal_id.clone();

    // Check if already exited
    let already_exited = registry.get_mut(&terminal_id, |state| {
        state.exit_status.as_ref().map(|status| {
            #[cfg(unix)]
            {
                use std::os::unix::process::ExitStatusExt;
                status.code().or_else(|| status.signal()).unwrap_or(-1)
            }
            #[cfg(not(unix))]
            {
                status.code().unwrap_or(-1)
            }
        })
    })?;

    if let Some(exit_status) = already_exited {
        return Ok(WaitForTerminalExitResponse { exit_status });
    }

    // Process is still running, need to wait for it
    // We need to take ownership of the process to wait on it
    // This is tricky with the registry design, so we'll use a different approach:
    // We'll poll the process status and check exit_status

    let timeout_duration = Duration::from_secs(config.max_runtime_secs);
    let start_time = std::time::Instant::now();

    loop {
        // Check if timeout exceeded
        if start_time.elapsed() >= timeout_duration {
            // Kill the process due to timeout
            registry.get_mut(&terminal_id, |state| {
                let _ = state.process.start_kill();
                state.killed = true;
            })?;

            return Err(ToolError::terminal_error(format!(
                "Terminal timed out after {} seconds",
                config.max_runtime_secs
            )));
        }

        // Try to get exit status (non-blocking check)
        let exit_status_result = registry.get_mut(&terminal_id, |state| {
            // Try to check if process has exited
            match state.process.try_wait() {
                Ok(Some(status)) => {
                    // Process has exited
                    state.exit_status = Some(status);
                    let exit_code = {
                        #[cfg(unix)]
                        {
                            use std::os::unix::process::ExitStatusExt;
                            status.code().or_else(|| status.signal()).unwrap_or(-1)
                        }
                        #[cfg(not(unix))]
                        {
                            status.code().unwrap_or(-1)
                        }
                    };
                    Some(exit_code)
                }
                Ok(None) => {
                    // Process is still running
                    None
                }
                Err(_e) => {
                    // Error checking status, treat as still running
                    None
                }
            }
        })?;

        if let Some(exit_status) = exit_status_result {
            return Ok(WaitForTerminalExitResponse { exit_status });
        }

        // Wait a bit before checking again
        tokio::time::sleep(Duration::from_millis(100)).await;
    }
}
