use tauri::Manager;
use tokio::sync::oneshot;

use crate::agent::{
    AgentStreamEmitter, AgentToolRuntimeContext, CancelChatStreamRequest, ChatGateway,
    ChatPromptRequest, ChatStreamStart, ResolveAgentToolApprovalRequest,
};
use crate::state::AppState;
use crate::terminal::{
    Company, ExecuteTerminalCommandRequest, LookupCompanyRequest, 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)
}

/// Looks up a live company snapshot directly from the quote provider.
#[tauri::command]
pub async fn lookup_company(
    state: tauri::State<'_, AppState>,
    request: LookupCompanyRequest,
) -> Result<Company, String> {
    state.command_service.lookup_company(&request.symbol).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 = request.request_id.trim().to_string();
    if request_id.is_empty() {
        return Err("request id cannot be empty".to_string());
    }

    let (prepared_turn, gateway) = {
        let mut agent = state.agent.lock().await;

        let gateway = agent.gateway();
        let prepared_turn = agent
            .prepare_turn(request)
            .await
            .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();
    let active_chat_streams = state.active_chat_streams.clone();
    let command_executor = state.command_service.clone();
    let pending_approvals = state.pending_agent_tool_approvals.clone();
    let artifact_store = state.artifact_store.clone();
    let (cancel_sender, cancel_receiver) = oneshot::channel();
    active_chat_streams
        .register(request_id.clone(), cancel_sender)
        .map_err(|error| error.to_string())?;
    let stream_emitter = std::sync::Arc::new(AgentStreamEmitter::new(
        app_handle.clone(),
        prepared_turn.workspace_id.clone(),
        request_id.clone(),
        prepared_turn.session_id.clone(),
    ));
    tauri::async_runtime::spawn(async move {
        let request_id = request_id.clone();
        let reply = tokio::select! {
            reply = gateway.stream_chat(
                prepared_turn.runtime.clone(),
                prepared_turn.prompt.clone(),
                prepared_turn.context_messages.clone(),
                prepared_turn.history.clone(),
                AgentToolRuntimeContext {
                    stream_emitter: stream_emitter.clone(),
                    command_executor,
                    pending_approvals: pending_approvals.clone(),
                    artifact_store,
                    request_id: request_id.clone(),
                    workspace_id: prepared_turn.workspace_id.clone(),
                },
                prepared_turn.task_profile,
            ) => reply,
            _ = cancel_receiver => {
                pending_approvals.cancel_for_request(&request_id);
                let _ = active_chat_streams.remove(&request_id);
                let _ = stream_emitter.cancelled();
                return;
            }
        };

        let _ = active_chat_streams.remove(&request_id);

        let reply = match reply {
            Ok(reply) => reply,
            Err(error) => {
                let _ = stream_emitter.error(error.to_string());
                return;
            }
        };

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

    Ok(start)
}

/// Cancels an active streaming chat request if it is still running.
#[tauri::command]
pub async fn cancel_chat_stream(
    state: tauri::State<'_, AppState>,
    request: CancelChatStreamRequest,
) -> Result<(), String> {
    let workspace_id = request.workspace_id.trim();
    let request_id = request.request_id.trim();
    if workspace_id.is_empty() {
        return Err("workspace id cannot be empty".to_string());
    }
    if request_id.is_empty() {
        return Err("request id cannot be empty".to_string());
    }

    state
        .pending_agent_tool_approvals
        .cancel_for_request(request_id);
    state
        .active_chat_streams
        .cancel(request_id)
        .map_err(|error| error.to_string())?;
    Ok(())
}

/// Resolves a pending agent-triggered command approval.
#[tauri::command]
pub async fn resolve_agent_tool_approval(
    state: tauri::State<'_, AppState>,
    request: ResolveAgentToolApprovalRequest,
) -> Result<(), String> {
    state
        .pending_agent_tool_approvals
        .resolve(&request.approval_id, request.approved)
        .map_err(|error| error.to_string())
}
