use std::path::Path;

use crate::agent_status::AgentStatus;
use crate::focus_queue::{FocusPriority, FocusQueue};
use crate::session::{SessionId, SessionManager};
use crate::ui::paint_keybind_hint;
use egui::{Color32, Pos2, Rect, Response, Sense, Vec2};

/// The RTS-style scene view for managing agents
pub struct AgentScene {
    /// Camera/view transform state managed by egui::Scene
    scene_rect: Rect,
    /// Currently selected agent IDs
    pub selected: Vec<SessionId>,
    /// Drag selection state
    drag_select: Option<DragSelect>,
    /// Target camera position for smooth animation
    camera_target: Option<Vec2>,
    /// Animation progress (0.0 to 1.0)
    animation_progress: f32,
}

/// State for box/marquee selection
struct DragSelect {
    start: Pos2,
    current: Pos2,
}

/// Action generated by the scene UI
#[derive(Debug, Clone)]
pub enum SceneAction {
    /// Selection changed
    SelectionChanged(Vec<SessionId>),
    /// Request to spawn a new agent
    SpawnAgent,
    /// Request to delete selected agents
    DeleteSelected,
    /// Agent was dragged to new position
    AgentMoved { id: SessionId, position: Vec2 },
}

/// Response from scene rendering
#[derive(Default)]
pub struct SceneResponse {
    pub action: Option<SceneAction>,
}

impl SceneResponse {
    pub fn new(action: SceneAction) -> Self {
        Self {
            action: Some(action),
        }
    }
}

impl Default for AgentScene {
    fn default() -> Self {
        Self::new()
    }
}

impl AgentScene {
    pub fn new() -> Self {
        Self {
            scene_rect: Rect::from_min_max(Pos2::new(-500.0, -500.0), Pos2::new(500.0, 500.0)),
            selected: Vec::new(),
            drag_select: None,
            camera_target: None,
            animation_progress: 1.0,
        }
    }

    /// Check if an agent is selected
    pub fn is_selected(&self, id: SessionId) -> bool {
        self.selected.contains(&id)
    }

    /// Set selection to a single agent
    pub fn select(&mut self, id: SessionId) {
        self.selected.clear();
        self.selected.push(id);
    }

    /// Add an agent to the selection
    pub fn add_to_selection(&mut self, id: SessionId) {
        if !self.selected.contains(&id) {
            self.selected.push(id);
        }
    }

    /// Clear all selection
    pub fn clear_selection(&mut self) {
        self.selected.clear();
    }

    /// Get the first selected agent (for chat panel)
    pub fn primary_selection(&self) -> Option<SessionId> {
        self.selected.first().copied()
    }

    /// Animate camera to focus on a position
    pub fn focus_on(&mut self, position: Vec2) {
        self.camera_target = Some(position);
        self.animation_progress = 0.0;
    }

    /// Render the scene
    pub fn ui(
        &mut self,
        session_manager: &SessionManager,
        focus_queue: &FocusQueue,
        ui: &mut egui::Ui,
        ctrl_held: bool,
    ) -> SceneResponse {
        let mut response = SceneResponse::default();

        // Update camera animation towards target
        if let Some(target) = self.camera_target {
            if self.animation_progress < 1.0 {
                self.animation_progress += 0.08;
                self.animation_progress = self.animation_progress.min(1.0);

                // Smoothly interpolate scene_rect center towards target
                let current_center = self.scene_rect.center();
                let target_pos = Pos2::new(target.x, target.y);
                let t = ease_out_cubic(self.animation_progress);
                let new_center = current_center.lerp(target_pos, t);

                // Shift the scene_rect to center on new position
                let offset = new_center - current_center;
                self.scene_rect = self.scene_rect.translate(offset);

                ui.ctx().request_repaint();
            } else {
                // Animation complete
                self.camera_target = None;
            }
        }

        // Track interactions from inside the scene closure
        let mut clicked_agent: Option<(SessionId, bool, Vec2)> = None; // (id, shift_held, position)
        let mut dragged_agent: Option<(SessionId, Vec2)> = None; // (id, new_position)
        let mut bg_clicked = false;
        let mut bg_drag_started = false;

        // Use a local copy of scene_rect to avoid borrow conflict
        let mut scene_rect = self.scene_rect;
        let selected_ids = &self.selected;

        let scene_response =
            egui::Scene::new()
                .zoom_range(0.1..=1.0)
                .show(ui, &mut scene_rect, |ui| {
                    // Draw agents and collect interaction responses
                    // Use sessions_ordered() to match keybinding order (Ctrl+1 = first in order, etc.)
                    for (keybind_idx, session) in
                        session_manager.sessions_ordered().into_iter().enumerate()
                    {
                        // Scene view only makes sense for agentic sessions
                        let Some(agentic) = &session.agentic else {
                            continue;
                        };

                        let id = session.id;
                        let keybind_number = keybind_idx + 1; // 1-indexed for display
                        let position: Vec2 = agentic.scene_position.into();
                        let status = session.status();
                        let title = session.details.display_title();
                        let is_selected = selected_ids.contains(&id);
                        let queue_priority = focus_queue.get_session_priority(id);

                        let agent_response = Self::draw_agent(
                            ui,
                            id,
                            keybind_number,
                            position,
                            status,
                            title,
                            &agentic.cwd,
                            &session.details.home_dir,
                            is_selected,
                            ctrl_held,
                            queue_priority,
                        );

                        if agent_response.clicked() {
                            let shift = ui.input(|i| i.modifiers.shift);
                            clicked_agent = Some((id, shift, position));
                        }

                        if agent_response.dragged() && is_selected {
                            let delta = agent_response.drag_delta();
                            dragged_agent = Some((id, position + delta));
                        }
                    }

                    // Handle click on empty space to deselect
                    let bg_response = ui.interact(
                        ui.max_rect(),
                        ui.id().with("scene_bg"),
                        Sense::click_and_drag(),
                    );

                    if bg_response.clicked() && clicked_agent.is_none() {
                        bg_clicked = true;
                    }

                    if bg_response.drag_started() && clicked_agent.is_none() {
                        bg_drag_started = true;
                    }
                });

        // Get the viewport rect for coordinate transforms
        let viewport_rect = scene_response.response.rect;

        self.scene_rect = scene_rect;

        // Process agent click
        if let Some((id, shift, _position)) = clicked_agent {
            if shift {
                self.add_to_selection(id);
            } else {
                self.select(id);
            }
            response = SceneResponse::new(SceneAction::SelectionChanged(self.selected.clone()));
        }

        // Process agent drag
        if let Some((id, new_pos)) = dragged_agent {
            response = SceneResponse::new(SceneAction::AgentMoved {
                id,
                position: new_pos,
            });
        }

        // Process background click
        if bg_clicked && response.action.is_none() && !self.selected.is_empty() {
            self.selected.clear();
            response = SceneResponse::new(SceneAction::SelectionChanged(Vec::new()));
        }

        // Start drag selection
        if bg_drag_started {
            if let Some(pos) = ui.input(|i| i.pointer.interact_pos()) {
                self.drag_select = Some(DragSelect {
                    start: pos,
                    current: pos,
                });
            }
        }

        // Update drag selection position
        if let Some(drag) = &mut self.drag_select {
            if let Some(pos) = ui.input(|i| i.pointer.interact_pos()) {
                drag.current = pos;
            }
        }

        // Handle keyboard input (only when no text input has focus)
        // Note: N key for spawning agents is handled globally in keybindings.rs
        if !ui.ctx().wants_keyboard_input()
            && ui.input(|i| i.key_pressed(egui::Key::Delete))
            && !self.selected.is_empty()
        {
            response = SceneResponse::new(SceneAction::DeleteSelected);
        }

        // Handle box selection completion
        if let Some(drag) = &self.drag_select {
            if ui.input(|i| i.pointer.primary_released()) {
                // Convert screen-space drag coordinates to scene-space
                // Screen -> Scene: scene_pos = scene_rect.min + (screen_pos - viewport.min) / viewport.size() * scene_rect.size()
                let screen_to_scene = |screen_pos: Pos2| -> Pos2 {
                    let rel = (screen_pos - viewport_rect.min) / viewport_rect.size();
                    scene_rect.min + rel * scene_rect.size()
                };

                let scene_start = screen_to_scene(drag.start);
                let scene_current = screen_to_scene(drag.current);
                let selection_rect = Rect::from_two_pos(scene_start, scene_current);

                self.selected.clear();

                for session in session_manager.iter() {
                    if let Some(agentic) = &session.agentic {
                        let agent_pos =
                            Pos2::new(agentic.scene_position.0, agentic.scene_position.1);
                        if selection_rect.contains(agent_pos) {
                            self.selected.push(session.id);
                        }
                    }
                }

                if !self.selected.is_empty() {
                    response =
                        SceneResponse::new(SceneAction::SelectionChanged(self.selected.clone()));
                }

                self.drag_select = None;
            }
        }

        // Draw box selection overlay
        if let Some(drag) = &self.drag_select {
            let rect = Rect::from_two_pos(drag.start, drag.current);
            let painter = ui.painter();
            painter.rect_filled(
                rect,
                0.0,
                Color32::from_rgba_unmultiplied(100, 150, 255, 30),
            );
            painter.rect_stroke(
                rect,
                0.0,
                egui::Stroke::new(
                    notedeck::tokens::STROKE_THIN,
                    Color32::from_rgb(100, 150, 255),
                ),
                egui::StrokeKind::Outside,
            );
        }

        response
    }

    /// Draw a single agent unit and return the interaction Response
    /// `keybind_number` is the 1-indexed number displayed when Ctrl is held (matches Ctrl+N keybindings)
    #[allow(clippy::too_many_arguments)]
    fn draw_agent(
        ui: &mut egui::Ui,
        id: SessionId,
        keybind_number: usize,
        position: Vec2,
        status: AgentStatus,
        title: &str,
        cwd: &Path,
        home_dir: &str,
        is_selected: bool,
        show_keybinding: bool,
        queue_priority: Option<FocusPriority>,
    ) -> Response {
        let agent_radius = 30.0;
        let center = Pos2::new(position.x, position.y);
        let agent_rect = Rect::from_center_size(center, Vec2::splat(agent_radius * 2.0));

        // Interact with the agent
        let response = ui.interact(
            agent_rect,
            ui.id().with(("agent", id)),
            Sense::click_and_drag(),
        );

        let painter = ui.painter();

        // Selection highlight (outer ring)
        if is_selected {
            painter.circle_stroke(
                center,
                agent_radius + 4.0,
                egui::Stroke::new(3.0_f32, Color32::from_rgb(255, 255, 100)),
            );
        }

        // Status ring
        let status_color = status.color();
        painter.circle_stroke(
            center,
            agent_radius,
            egui::Stroke::new(3.0_f32, status_color),
        );

        // Fill
        let fill_color = if response.hovered() {
            ui.visuals().widgets.hovered.bg_fill
        } else {
            ui.visuals().widgets.inactive.bg_fill
        };
        painter.circle_filled(center, agent_radius - 2.0, fill_color);

        // Focus queue indicator dot (top-right of the agent circle)
        if let Some(priority) = queue_priority {
            let dot_radius = 6.0;
            let dot_offset = Vec2::new(agent_radius * 0.7, -agent_radius * 0.7);
            let dot_center = center + dot_offset;
            painter.circle_filled(dot_center, dot_radius, priority.color());
        }

        // Agent icon in center: show keybind frame when Ctrl held, otherwise first letter
        if show_keybinding {
            paint_keybind_hint(ui, center, &keybind_number.to_string(), 24.0);
        } else {
            let icon_text: String = title.chars().next().unwrap_or('?').to_uppercase().collect();
            painter.text(
                center,
                egui::Align2::CENTER_CENTER,
                &icon_text,
                egui::FontId::proportional(20.0),
                ui.visuals().text_color(),
            );
        }

        // Title below
        let title_pos = center + Vec2::new(0.0, agent_radius + 10.0);
        painter.text(
            title_pos,
            egui::Align2::CENTER_TOP,
            title,
            egui::FontId::proportional(11.0),
            ui.visuals().text_color().gamma_multiply(0.8),
        );

        // Status label
        let status_pos = center + Vec2::new(0.0, agent_radius + 24.0);
        painter.text(
            status_pos,
            egui::Align2::CENTER_TOP,
            status.label(),
            egui::FontId::proportional(9.0),
            status_color.gamma_multiply(0.9),
        );

        // Cwd label (monospace, weak+small)
        let cwd_text = if home_dir.is_empty() {
            crate::path_utils::abbreviate_path(cwd)
        } else {
            crate::path_utils::abbreviate_with_home(cwd, home_dir)
        };
        let cwd_pos = center + Vec2::new(0.0, agent_radius + 38.0);
        painter.text(
            cwd_pos,
            egui::Align2::CENTER_TOP,
            &cwd_text,
            egui::FontId::monospace(8.0),
            ui.visuals().weak_text_color(),
        );

        response
    }
}

/// Easing function for smooth camera animation
fn ease_out_cubic(t: f32) -> f32 {
    1.0 - (1.0 - t).powi(3)
}
