use std::sync::atomic::{AtomicU64, Ordering};
use std::sync::Arc;

use tauri::{AppHandle, Emitter, Runtime};

use crate::agent::{AgentStreamItemEvent, AgentStreamItemKind};
use crate::error::AppError;
use crate::terminal::TerminalCommandResponse;

pub struct AgentStreamEmitter<R: Runtime> {
    app_handle: AppHandle<R>,
    workspace_id: Arc<str>,
    request_id: Arc<str>,
    session_id: Arc<str>,
    next_sequence: AtomicU64,
}

impl<R: Runtime> AgentStreamEmitter<R> {
    pub fn new(
        app_handle: AppHandle<R>,
        workspace_id: String,
        request_id: String,
        session_id: String,
    ) -> Self {
        Self {
            app_handle,
            workspace_id: Arc::from(workspace_id),
            request_id: Arc::from(request_id),
            session_id: Arc::from(session_id),
            next_sequence: AtomicU64::new(1),
        }
    }

    pub fn emit(&self, event: AgentStreamItemEvent) -> Result<(), AppError> {
        self.app_handle
            .emit("agent_stream_item", event)
            .map_err(|error| AppError::ProviderRequest(error.to_string()))
    }

    pub fn reasoning_delta(&self, delta: String) -> Result<(), AppError> {
        if delta.is_empty() {
            return Ok(());
        }

        self.emit(
            self.event(AgentStreamItemKind::ReasoningDelta)
                .with_delta(delta),
        )
    }

    pub fn text_delta(&self, delta: String) -> Result<(), AppError> {
        if delta.is_empty() {
            return Ok(());
        }

        self.emit(self.event(AgentStreamItemKind::TextDelta).with_delta(delta))
    }

    pub fn tool_command(&self, command: String) -> Result<(), AppError> {
        self.emit(
            self.event(AgentStreamItemKind::ToolCommand)
                .with_command(command),
        )
    }

    pub fn tool_result(
        &self,
        command: String,
        response: TerminalCommandResponse,
    ) -> Result<(), AppError> {
        self.emit(
            self.event(AgentStreamItemKind::ToolResult)
                .with_command(command)
                .with_response(response),
        )
    }

    pub fn approval_required(
        &self,
        approval_id: String,
        command: String,
        title: String,
        message: String,
    ) -> Result<(), AppError> {
        self.emit(
            self.event(AgentStreamItemKind::ApprovalRequired)
                .with_approval(approval_id, title, message)
                .with_command(command),
        )
    }

    pub fn stream_complete(&self) -> Result<(), AppError> {
        self.emit(self.event(AgentStreamItemKind::StreamComplete))
    }

    pub fn cancelled(&self) -> Result<(), AppError> {
        self.emit(self.event(AgentStreamItemKind::Cancelled))
    }

    pub fn error(&self, message: String) -> Result<(), AppError> {
        self.emit(self.event(AgentStreamItemKind::Error).with_error(message))
    }

    fn event(&self, kind: AgentStreamItemKind) -> AgentStreamItemEvent {
        AgentStreamItemEvent {
            workspace_id: self.workspace_id.clone(),
            request_id: self.request_id.clone(),
            session_id: self.session_id.clone(),
            sequence: self.next_sequence.fetch_add(1, Ordering::Relaxed),
            kind,
            delta: None,
            command: None,
            response: None,
            approval_id: None,
            title: None,
            message: None,
            error_message: None,
        }
    }
}

impl AgentStreamItemEvent {
    fn with_delta(mut self, delta: String) -> Self {
        self.delta = Some(delta);
        self
    }

    fn with_command(mut self, command: String) -> Self {
        self.command = Some(command);
        self
    }

    fn with_response(mut self, response: TerminalCommandResponse) -> Self {
        self.response = Some(response);
        self
    }

    fn with_approval(mut self, approval_id: String, title: String, message: String) -> Self {
        self.approval_id = Some(approval_id);
        self.title = Some(title);
        self.message = Some(message);
        self
    }

    fn with_error(mut self, error_message: String) -> Self {
        self.error_message = Some(error_message);
        self
    }
}
