use std::time::Duration;

use futures::StreamExt;
use tauri::{Emitter, Manager};

use crate::agent::{
    AgentDeltaEvent, AgentErrorEvent, AgentResultEvent, ChatGateway, ChatPromptRequest,
    ChatStreamStart,
};
use crate::state::AppState;
use crate::terminal::{ExecuteTerminalCommandRequest, TerminalCommandResponse};

/// Executes a slash command and returns either terminal text or a structured panel payload.
#[tauri::command]
pub async fn execute_terminal_command(
    state: tauri::State<'_, AppState>,
    request: ExecuteTerminalCommandRequest,
) -> Result<TerminalCommandResponse, String> {
    Ok(state.command_service.execute(request).await)
}

/// Starts a streaming plain-text chat turn and emits progress over Tauri events.
#[tauri::command]
pub async fn start_chat_stream(
    app: tauri::AppHandle,
    state: tauri::State<'_, AppState>,
    request: ChatPromptRequest,
) -> Result<ChatStreamStart, String> {
    let request_id = state.next_request_id();
    let (prepared_turn, gateway) = {
        let mut agent = state
            .agent
            .lock()
            .map_err(|_| "agent state is unavailable".to_string())?;

        let gateway = agent.gateway();
        let prepared_turn = agent
            .prepare_turn(request)
            .map_err(|error| error.to_string())?;
        (prepared_turn, gateway)
    };

    let start = ChatStreamStart {
        request_id: request_id.clone(),
        session_id: prepared_turn.session_id.clone(),
    };

    let app_handle = app.clone();
    tauri::async_runtime::spawn(async move {
        tokio::time::sleep(Duration::from_millis(30)).await;

        // Resolve the upstream stream outside the mutex so long-running provider I/O
        // does not block other settings reads or chat requests.
        let mut stream = match gateway
            .stream_chat(
                prepared_turn.runtime.clone(),
                prepared_turn.prompt.clone(),
                prepared_turn.history.clone(),
            )
            .await
        {
            Ok(stream) => stream,
            Err(error) => {
                let _ = app_handle.emit(
                    "agent_error",
                    AgentErrorEvent {
                        workspace_id: prepared_turn.workspace_id,
                        request_id,
                        session_id: prepared_turn.session_id,
                        message: error.to_string(),
                    },
                );
                return;
            }
        };

        let mut reply = String::new();

        while let Some(chunk) = stream.next().await {
            match chunk {
                Ok(delta) => {
                    reply.push_str(&delta);
                    let _ = app_handle.emit(
                        "agent_delta",
                        AgentDeltaEvent {
                            workspace_id: prepared_turn.workspace_id.clone(),
                            request_id: request_id.clone(),
                            session_id: prepared_turn.session_id.clone(),
                            delta,
                        },
                    );
                }
                Err(error) => {
                    let _ = app_handle.emit(
                        "agent_error",
                        AgentErrorEvent {
                            workspace_id: prepared_turn.workspace_id,
                            request_id,
                            session_id: prepared_turn.session_id,
                            message: error.to_string(),
                        },
                    );
                    return;
                }
            }
        }

        // Store the final assistant message after the stream completes so the next
        // conversational turn reuses the full transcript.
        if let Ok(mut agent) = app_handle.state::<AppState>().agent.lock() {
            let _ = agent.record_assistant_reply(&prepared_turn.session_id, &reply);
        }

        let _ = app_handle.emit(
            "agent_result",
            AgentResultEvent {
                workspace_id: prepared_turn.workspace_id,
                request_id,
                session_id: prepared_turn.session_id,
                reply,
            },
        );
    });

    Ok(start)
}
