//! Bridge to agor-core: PtyManager, SidecarManager, EventSink.
//!
//! Implements the `EventSink` trait from agor-core so that PTY and sidecar
//! events flow into GPUI's entity system via channel-based async dispatch.

use agor_core::event::EventSink;
use agor_core::pty::{PtyManager, PtyOptions};
use agor_core::sandbox::SandboxConfig;
use agor_core::sidecar::{AgentQueryOptions, SidecarConfig, SidecarManager};
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::{mpsc, Arc};

// ── GPUI EventSink ──────────────────────────────────────────────────

/// Event payload sent from backend threads to the GPUI main thread.
#[derive(Debug, Clone)]
pub struct BackendEvent {
    pub event_name: String,
    pub payload: serde_json::Value,
}

/// An `EventSink` that queues events into an `mpsc` channel.
/// The GPUI main loop drains this channel each frame to update entity state.
pub struct GpuiEventSink {
    sender: mpsc::Sender<BackendEvent>,
}

impl GpuiEventSink {
    pub fn new(sender: mpsc::Sender<BackendEvent>) -> Self {
        Self { sender }
    }
}

impl EventSink for GpuiEventSink {
    fn emit(&self, event: &str, payload: serde_json::Value) {
        let _ = self.sender.send(BackendEvent {
            event_name: event.to_string(),
            payload,
        });
    }
}

// ── Backend Manager ─────────────────────────────────────────────────

/// Owns the agor-core managers and the event channel.
/// Created once at app startup; shared via `Arc` or `Entity<Backend>`.
pub struct Backend {
    pub pty_manager: PtyManager,
    pub sidecar_manager: SidecarManager,
    pub event_rx: mpsc::Receiver<BackendEvent>,
}

impl Backend {
    /// Create backend with default sidecar search paths.
    pub fn new() -> Self {
        let (tx, rx) = mpsc::channel();
        let sink: Arc<dyn EventSink> = Arc::new(GpuiEventSink::new(tx));

        let pty_manager = PtyManager::new(Arc::clone(&sink));

        // Sidecar search paths: look next to the binary, then common install locations
        let mut search_paths = vec![
            PathBuf::from("./sidecar/dist"),
            PathBuf::from("../sidecar/dist"),
        ];
        if let Ok(exe) = std::env::current_exe() {
            if let Some(dir) = exe.parent() {
                search_paths.push(dir.join("sidecar"));
            }
        }

        let sidecar_config = SidecarConfig {
            search_paths,
            env_overrides: HashMap::new(),
            sandbox: SandboxConfig::default(),
        };
        let sidecar_manager = SidecarManager::new(Arc::clone(&sink), sidecar_config);

        Self {
            pty_manager,
            sidecar_manager,
            event_rx: rx,
        }
    }

    /// Spawn a PTY for a project terminal.
    pub fn spawn_pty(&self, cwd: &str) -> Result<String, String> {
        self.pty_manager.spawn(PtyOptions {
            shell: None,
            cwd: Some(cwd.to_string()),
            args: None,
            cols: Some(120),
            rows: Some(30),
        })
    }

    /// Start an agent query (sends to sidecar, non-blocking).
    pub fn start_agent(&self, session_id: &str, prompt: &str, cwd: &str) {
        let options = AgentQueryOptions {
            provider: "claude".to_string(),
            session_id: session_id.to_string(),
            prompt: prompt.to_string(),
            cwd: Some(cwd.to_string()),
            max_turns: None,
            max_budget_usd: None,
            resume_session_id: None,
            permission_mode: Some("bypassPermissions".to_string()),
            setting_sources: Some(vec!["user".to_string(), "project".to_string()]),
            system_prompt: None,
            model: None,
            claude_config_dir: None,
            additional_directories: None,
            worktree_name: None,
            provider_config: serde_json::Value::Null,
            extra_env: HashMap::new(),
        };
        let _ = self.sidecar_manager.query(&options);
    }

    /// Drain all pending backend events (call once per frame / tick).
    pub fn drain_events(&self) -> Vec<BackendEvent> {
        let mut events = Vec::new();
        while let Ok(ev) = self.event_rx.try_recv() {
            events.push(ev);
        }
        events
    }
}
