use crate::app::AppState;
/// Run Manager Screen
/// View, filter, and manage agent runs
use ratatui::prelude::*;
use ratatui::widgets::{Block, Borders, List, ListItem, Paragraph, Wrap};
use serde_json::Value;

// === 4RUNR BRAND COLORS (matching layout.rs) ===
const BRAND_PURPLE: Color = Color::Rgb(138, 43, 226);
const CYBER_CYAN: Color = Color::Rgb(0, 255, 255);
const NEON_GREEN: Color = Color::Rgb(57, 255, 20);
const AMBER_WARN: Color = Color::Rgb(255, 191, 0);
const TEXT_PRIMARY: Color = Color::Rgb(230, 230, 240);
const TEXT_DIM: Color = Color::Rgb(100, 100, 120);
const TEXT_MUTED: Color = Color::Rgb(60, 60, 80);
const BG_PANEL: Color = Color::Rgb(18, 18, 25);

/// Run Manager state
#[derive(Debug, Clone)]
pub struct RunManagerState {
    /// List of runs
    pub runs: Vec<RunInfo>,

    /// Selected run index
    pub selected_index: usize,

    /// Filter state
    pub filter: RunFilter,

    /// Sort state
    pub sort: RunSort,

    /// Loading state
    #[allow(dead_code)]
    pub loading: bool,

    /// Last refresh time
    #[allow(dead_code)]
    pub last_refresh: Option<std::time::Instant>,

    /// Full-screen detail view: locked to this run until ESC/Enter.
    pub detail_run_id: Option<String>,

    /// Log panel scroll offset in detail view.
    pub detail_log_scroll: u16,

    /// Poll Gateway for run list while Run Manager is open (no manual R).
    pub auto_refresh_enabled: bool,
    pub auto_refresh_interval: std::time::Duration,
}

#[derive(Debug, Clone)]
pub struct RunInfo {
    pub id: String,
    pub name: String,
    pub agent_name: String,
    pub status: RunStatus,
    /// ISO `createdAt` from gateway (for sorting)
    pub created_at_sort: String,
    pub started_at: String,
    pub duration: Option<String>,
    pub progress: Option<f32>, // 0.0-1.0

    // Extended details
    pub input_tokens: Option<u32>,
    pub output_tokens: Option<u32>,
    pub total_cost: Option<f64>,
    pub model: Option<String>,
    pub error_message: Option<String>,
    /// True when Gateway output indicates Shield blocked or masked on the run path.
    pub shield_blocked: bool,
    pub safety_reason: Option<String>,
    pub logs: Vec<String>,
}

#[derive(Debug, Clone, PartialEq)]
pub enum RunStatus {
    #[allow(dead_code)]
    Pending,
    Running,
    Completed,
    Failed,
    #[allow(dead_code)]
    Cancelled,
}

impl RunStatus {
    pub fn as_str(&self) -> &str {
        match self {
            RunStatus::Pending => "PENDING",
            RunStatus::Running => "RUNNING",
            RunStatus::Completed => "COMPLETED",
            RunStatus::Failed => "FAILED",
            RunStatus::Cancelled => "CANCELLED",
        }
    }

    pub fn color(&self) -> Color {
        match self {
            RunStatus::Pending => AMBER_WARN,
            RunStatus::Running => CYBER_CYAN,
            RunStatus::Completed => NEON_GREEN,
            RunStatus::Failed => Color::Rgb(255, 69, 69), // Bright red
            RunStatus::Cancelled => TEXT_DIM,
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum RunFilter {
    All,
    Active, // Pending + Running
    Completed,
    Failed,
}

impl RunFilter {
    pub fn as_str(&self) -> &str {
        match self {
            RunFilter::All => "All",
            RunFilter::Active => "Active",
            RunFilter::Completed => "Completed",
            RunFilter::Failed => "Failed",
        }
    }

    pub fn matches(&self, status: &RunStatus) -> bool {
        match self {
            RunFilter::All => true,
            RunFilter::Active => matches!(status, RunStatus::Pending | RunStatus::Running),
            RunFilter::Completed => matches!(status, RunStatus::Completed),
            RunFilter::Failed => matches!(status, RunStatus::Failed),
        }
    }
}

#[derive(Debug, Clone, PartialEq)]
pub enum RunSort {
    DateDesc, // Newest first
    DateAsc,  // Oldest first
    Status,
    Agent,
}

impl RunSort {
    pub fn as_str(&self) -> &str {
        match self {
            RunSort::DateDesc => "Date ↓",
            RunSort::DateAsc => "Date ↑",
            RunSort::Status => "Status",
            RunSort::Agent => "Agent",
        }
    }
}

impl Default for RunManagerState {
    fn default() -> Self {
        Self {
            runs: Vec::new(),
            selected_index: 0,
            filter: RunFilter::All,
            sort: RunSort::DateDesc,
            loading: false,
            last_refresh: None,
            detail_run_id: None,
            detail_log_scroll: 0,
            auto_refresh_enabled: true,
            auto_refresh_interval: std::time::Duration::from_secs(3),
        }
    }
}

/// Map a Gateway `/api/runs` JSON object into UI state.
pub fn run_info_from_gateway_value(v: &Value) -> Option<RunInfo> {
    let o = v.as_object()?;
    let id = o.get("id")?.as_str()?.to_string();
    let name = o
        .get("name")
        .and_then(|n| n.as_str())
        .unwrap_or("Run")
        .to_string();
    let status_str = o
        .get("status")
        .and_then(|s| s.as_str())
        .unwrap_or("created");
    let status = match status_str {
        "running" => RunStatus::Running,
        "completed" => RunStatus::Completed,
        "failed" => RunStatus::Failed,
        "killed" => RunStatus::Cancelled,
        _ => RunStatus::Pending,
    };
    let created_at_sort = o
        .get("createdAt")
        .and_then(|c| c.as_str())
        .unwrap_or("")
        .to_string();
    let started_at = o
        .get("startedAt")
        .and_then(|s| s.as_str())
        .filter(|s| !s.is_empty())
        .map(|s| s.chars().take(19).collect())
        .unwrap_or_else(|| created_at_sort.chars().take(19).collect::<String>());
    let agent_name = extract_agent_name(o);
    let logs: Vec<String> = o
        .get("logs")
        .and_then(|l| l.as_array())
        .map(|arr| {
            arr.iter()
                .filter_map(|e| {
                    let lo = e.as_object()?;
                    let ts = lo.get("timestamp").and_then(|t| t.as_str()).unwrap_or("");
                    let msg = lo.get("message").and_then(|m| m.as_str()).unwrap_or("");
                    Some(format!("[{}] {}", ts, msg))
                })
                .collect()
        })
        .unwrap_or_default();
    let output_obj = o.get("output").and_then(|out| out.as_object());
    let error_message = output_obj
        .and_then(|oo| oo.get("error"))
        .and_then(|e| e.as_str())
        .map(|s| s.to_string());
    let safety_reason = output_obj
        .and_then(|oo| oo.get("reason"))
        .and_then(|r| r.as_str())
        .map(|s| s.to_string());
    let shield_blocked = error_message
        .as_deref()
        .map(|e| e.to_lowercase().contains("shield"))
        .unwrap_or(false)
        || logs.iter().any(|l| l.to_lowercase().contains("shield blocked"));
    let model = o
        .get("input")
        .and_then(|i| i.as_object())
        .and_then(|io| io.get("_agent"))
        .and_then(|a| a.as_object())
        .and_then(|ao| ao.get("model"))
        .and_then(|m| m.as_str())
        .map(|s| s.to_string());
    let progress = match status {
        RunStatus::Completed => Some(1.0),
        RunStatus::Failed | RunStatus::Cancelled => None,
        RunStatus::Running => Some(0.5),
        RunStatus::Pending => Some(0.05),
    };

    Some(RunInfo {
        id,
        name,
        agent_name,
        status,
        created_at_sort,
        started_at,
        duration: None,
        progress,
        input_tokens: None,
        output_tokens: None,
        total_cost: None,
        model,
        error_message,
        shield_blocked,
        safety_reason,
        logs,
    })
}

fn extract_agent_name(o: &serde_json::Map<String, Value>) -> String {
    if let Some(tags) = o.get("tags").and_then(|t| t.as_array()) {
        for t in tags {
            if let Some(s) = t.as_str() {
                if let Some(rest) = s.strip_prefix("agent:") {
                    return rest.to_string();
                }
            }
        }
    }
    if let Some(agent) = o
        .get("input")
        .and_then(|i| i.as_object())
        .and_then(|io| io.get("_agent"))
        .and_then(|a| a.as_object())
    {
        if let Some(n) = agent.get("name").and_then(|v| v.as_str()) {
            return n.to_string();
        }
    }
    if let Some(aid) = o
        .get("input")
        .and_then(|i| i.as_object())
        .and_then(|io| io.get("agent_id"))
        .and_then(|v| v.as_str())
    {
        return aid.to_string();
    }
    "—".to_string()
}

impl RunManagerState {
    pub fn replace_runs(&mut self, runs: Vec<RunInfo>) {
        self.runs = runs;
        self.selected_index = 0;
        self.detail_run_id = None;
        self.detail_log_scroll = 0;
        self.loading = false;
        self.last_refresh = Some(std::time::Instant::now());
    }

    /// Auto-refresh: keep selection and detail view when the selected run still exists.
    pub fn replace_runs_preserve(&mut self, runs: Vec<RunInfo>) {
        let selected_id = self.selected_run().map(|r| r.id.clone());
        let pinned_detail_id = self.detail_run_id.clone();
        self.runs = runs;
        self.loading = false;
        self.last_refresh = Some(std::time::Instant::now());

        let filtered = self.filtered_runs();
        if filtered.is_empty() {
            self.selected_index = 0;
            self.detail_run_id = None;
            self.detail_log_scroll = 0;
            return;
        }

        if let Some(id) = selected_id {
            if let Some(idx) = filtered.iter().position(|r| r.id == id) {
                self.selected_index = idx;
            } else if self.selected_index >= filtered.len() {
                self.selected_index = filtered.len() - 1;
            }
        } else if self.selected_index >= filtered.len() {
            self.selected_index = filtered.len() - 1;
        }

        if let Some(detail_id) = pinned_detail_id {
            if self.runs.iter().any(|r| r.id == detail_id) {
                self.detail_run_id = Some(detail_id);
            } else {
                self.detail_run_id = None;
                self.detail_log_scroll = 0;
            }
        }
    }

    pub fn clear_runs(&mut self) {
        self.runs.clear();
        self.selected_index = 0;
        self.detail_run_id = None;
        self.detail_log_scroll = 0;
        self.loading = false;
        self.last_refresh = None;
    }

    pub fn merge_run_detail(&mut self, info: RunInfo) {
        if let Some(i) = self.runs.iter().position(|r| r.id == info.id) {
            self.runs[i] = info;
        } else {
            self.runs.push(info);
        }
        self.loading = false;
    }

    /// Get filtered and sorted runs
    pub fn filtered_runs(&self) -> Vec<&RunInfo> {
        let mut runs: Vec<&RunInfo> = self
            .runs
            .iter()
            .filter(|r| self.filter.matches(&r.status))
            .collect();

        // Sort
        match self.sort {
            RunSort::DateDesc => {
                runs.sort_by(|a, b| b.created_at_sort.cmp(&a.created_at_sort));
            }
            RunSort::DateAsc => {
                runs.sort_by(|a, b| a.created_at_sort.cmp(&b.created_at_sort));
            }
            RunSort::Status => {
                runs.sort_by_key(|r| r.status.as_str());
            }
            RunSort::Agent => {
                runs.sort_by_key(|r| &r.agent_name);
            }
        }

        runs
    }

    /// Get currently selected run
    pub fn selected_run(&self) -> Option<&RunInfo> {
        let filtered = self.filtered_runs();
        filtered.get(self.selected_index).copied()
    }

    /// Run pinned in full-screen detail view (independent of list selection).
    pub fn detail_run(&self) -> Option<&RunInfo> {
        let id = self.detail_run_id.as_ref()?;
        self.runs.iter().find(|r| &r.id == id)
    }

    /// Open full-screen detail for the currently selected run.
    pub fn open_detail_view(&mut self) -> Option<String> {
        let run_id = self.selected_run()?.id.clone();
        self.detail_run_id = Some(run_id.clone());
        self.detail_log_scroll = 0;
        Some(run_id)
    }

    /// Close detail view and return to list (selection unchanged).
    pub fn close_detail_view(&mut self) {
        self.detail_run_id = None;
        self.detail_log_scroll = 0;
    }

    pub fn is_detail_view(&self) -> bool {
        self.detail_run_id.is_some()
    }

    pub fn detail_log_scroll_up(&mut self) {
        self.detail_log_scroll = self.detail_log_scroll.saturating_sub(1);
    }

    pub fn detail_log_scroll_down(&mut self) {
        self.detail_log_scroll = self.detail_log_scroll.saturating_add(1);
    }

    /// Move selection up (list view only).
    pub fn select_previous(&mut self) {
        if self.selected_index > 0 {
            self.selected_index -= 1;
        }
    }

    /// Move selection down
    pub fn select_next(&mut self) {
        let filtered_count = self.filtered_runs().len();
        if self.selected_index + 1 < filtered_count {
            self.selected_index += 1;
        }
    }

    /// Cycle to next filter
    pub fn next_filter(&mut self) {
        self.filter = match self.filter {
            RunFilter::All => RunFilter::Active,
            RunFilter::Active => RunFilter::Completed,
            RunFilter::Completed => RunFilter::Failed,
            RunFilter::Failed => RunFilter::All,
        };
        self.selected_index = 0; // Reset selection
    }

    /// Cycle to next sort
    pub fn next_sort(&mut self) {
        self.sort = match self.sort {
            RunSort::DateDesc => RunSort::DateAsc,
            RunSort::DateAsc => RunSort::Status,
            RunSort::Status => RunSort::Agent,
            RunSort::Agent => RunSort::DateDesc,
        };
    }
}

/// Render the Run Manager screen
pub fn render(f: &mut Frame, state: &AppState) {
    let area = f.size();

    // Get run manager state from AppState
    let run_state = &state.run_manager;

    // Check if in detail view
    if run_state.is_detail_view() {
        render_detail_view(f, area, run_state);
    } else {
        // Split screen into sections
        use ratatui::layout::{Constraint, Direction, Layout};

        let chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints([
                Constraint::Length(3), // Header
                Constraint::Min(10),   // Run list
                Constraint::Length(8), // Details panel
                Constraint::Length(3), // Actions
            ])
            .split(area);

        render_header(f, chunks[0], run_state);
        render_run_list(f, chunks[1], run_state);
        render_details_panel(f, chunks[2], run_state);
        render_actions(f, chunks[3]);
    }
}

fn render_header(f: &mut Frame, area: Rect, state: &RunManagerState) {
    let filtered_count = state.filtered_runs().len();
    let total_count = state.runs.len();

    let live = if state.auto_refresh_enabled {
        " · LIVE"
    } else {
        ""
    };

    let block = Block::default()
        .title(format!(
            " ⚡ Run Manager - {} ({}/{} runs){} ",
            state.filter.as_str(),
            filtered_count,
            total_count,
            live
        ))
        .borders(Borders::ALL)
        .border_style(Style::default().fg(BRAND_PURPLE))
        .style(Style::default().bg(BG_PANEL));

    f.render_widget(block, area);
}

fn render_run_list(f: &mut Frame, area: Rect, state: &RunManagerState) {
    let block = Block::default()
        .title(format!(" 📋 Runs (Sort: {}) ", state.sort.as_str()))
        .borders(Borders::ALL)
        .border_style(Style::default().fg(CYBER_CYAN))
        .style(Style::default().bg(BG_PANEL));

    let inner = block.inner(area);
    f.render_widget(block, area);

    let filtered_runs = state.filtered_runs();

    if filtered_runs.is_empty() {
        let text = Paragraph::new("No runs found")
            .style(Style::default().fg(Color::DarkGray))
            .alignment(Alignment::Center);
        f.render_widget(text, inner);
        return;
    }

    let items: Vec<ListItem> = filtered_runs
        .iter()
        .enumerate()
        .map(|(i, run)| {
            let is_selected = i == state.selected_index;

            let progress_bar = if let Some(progress) = run.progress {
                let filled = (progress * 20.0) as usize;
                let empty = 20 - filled;
                format!("[{}{}]", "█".repeat(filled), "░".repeat(empty))
            } else {
                "[░░░░░░░░░░░░░░░░░░░░]".to_string()
            };

            let duration_str = run.duration.as_deref().unwrap_or("--");

            // Build line with colored spans
            let mut spans = vec![
                Span::styled(
                    if is_selected { "▶ " } else { "  " },
                    Style::default().fg(BRAND_PURPLE).bold(),
                ),
                Span::styled(
                    format!("{:<10}", run.status.as_str()),
                    Style::default().fg(run.status.color()).bold(),
                ),
            ];
            if run.shield_blocked {
                spans.push(Span::styled(
                    " SHIELD ",
                    Style::default()
                        .fg(Color::Rgb(255, 140, 0))
                        .add_modifier(Modifier::BOLD),
                ));
            }
            spans.extend([
                Span::styled(" │ ", Style::default().fg(TEXT_DIM)),
                Span::styled(
                    format!("{:<20}", run.name),
                    Style::default()
                        .fg(if is_selected {
                            CYBER_CYAN
                        } else {
                            TEXT_PRIMARY
                        })
                        .bold(),
                ),
                Span::styled(" │ ", Style::default().fg(TEXT_DIM)),
                Span::styled(
                    format!("{:<15}", run.agent_name),
                    Style::default().fg(TEXT_DIM),
                ),
                Span::styled(" │ ", Style::default().fg(TEXT_DIM)),
                Span::styled(
                    format!("{:>8}", duration_str),
                    Style::default().fg(TEXT_MUTED),
                ),
                Span::raw(" "),
                Span::styled(
                    progress_bar,
                    Style::default().fg(if run.progress.unwrap_or(0.0) > 0.5 {
                        NEON_GREEN
                    } else {
                        AMBER_WARN
                    }),
                ),
            ]);

            let line = Line::from(spans);

            ListItem::new(line)
        })
        .collect();

    let list = List::new(items);
    f.render_widget(list, inner);
}

fn render_details_panel(f: &mut Frame, area: Rect, state: &RunManagerState) {
    let block = Block::default()
        .title(" 🔍 Run Details ")
        .borders(Borders::ALL)
        .border_style(Style::default().fg(NEON_GREEN))
        .style(Style::default().bg(BG_PANEL));

    let inner = block.inner(area);
    f.render_widget(block, area);

    if let Some(run) = state.selected_run() {
        let text = vec![
            Line::from(vec![
                Span::styled("ID: ", Style::default().fg(TEXT_DIM)),
                Span::styled(&run.id, Style::default().fg(AMBER_WARN)),
            ]),
            Line::from(vec![
                Span::styled("Name: ", Style::default().fg(TEXT_DIM)),
                Span::styled(&run.name, Style::default().fg(TEXT_PRIMARY).bold()),
            ]),
            Line::from(vec![
                Span::styled("Agent: ", Style::default().fg(TEXT_DIM)),
                Span::styled(&run.agent_name, Style::default().fg(CYBER_CYAN)),
            ]),
            Line::from(vec![
                Span::styled("Status: ", Style::default().fg(TEXT_DIM)),
                Span::styled(
                    run.status.as_str(),
                    Style::default().fg(run.status.color()).bold(),
                ),
            ]),
            Line::from(vec![
                Span::styled("Started: ", Style::default().fg(TEXT_DIM)),
                Span::styled(&run.started_at, Style::default().fg(TEXT_MUTED)),
            ]),
        ];

        let paragraph = Paragraph::new(text).wrap(Wrap { trim: false });
        f.render_widget(paragraph, inner);
    } else {
        let text = Paragraph::new("No run selected")
            .style(Style::default().fg(TEXT_DIM))
            .alignment(Alignment::Center);
        f.render_widget(text, inner);
    }
}

fn render_detail_view(f: &mut Frame, area: Rect, state: &RunManagerState) {
    use ratatui::layout::{Constraint, Direction, Layout};

    // Split into header, content, and footer
    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Length(3), // Header
            Constraint::Min(10),   // Content
            Constraint::Length(3), // Footer
        ])
        .split(area);

    // Get the pinned run (not list selection — stays fixed until exit)
    let run = match state.detail_run() {
        Some(r) => r,
        None => {
            let block = Block::default()
                .title(" Run Details ")
                .borders(Borders::ALL)
                .border_style(Style::default().fg(TEXT_DIM));
            f.render_widget(block, area);
            return;
        }
    };

    // Header
    let header_block = Block::default()
        .title(format!(" 📊 Run Details - {} ", run.name))
        .borders(Borders::ALL)
        .border_style(Style::default().fg(BRAND_PURPLE))
        .style(Style::default().bg(BG_PANEL));
    f.render_widget(header_block, chunks[0]);

    // Content - split into left (info) and right (logs)
    let content_chunks = Layout::default()
        .direction(Direction::Horizontal)
        .constraints([
            Constraint::Percentage(40), // Info
            Constraint::Percentage(60), // Logs
        ])
        .split(chunks[1]);

    // Left: Run Info
    let info_block = Block::default()
        .title(" ℹ️  Information ")
        .borders(Borders::ALL)
        .border_style(Style::default().fg(CYBER_CYAN))
        .style(Style::default().bg(BG_PANEL));

    let info_inner = info_block.inner(content_chunks[0]);
    f.render_widget(info_block, content_chunks[0]);

    let mut info_lines = vec![
        Line::from(""),
        Line::from(vec![
            Span::styled("ID: ", Style::default().fg(TEXT_DIM)),
            Span::styled(&run.id, Style::default().fg(TEXT_PRIMARY)),
        ]),
        Line::from(vec![
            Span::styled("Agent: ", Style::default().fg(TEXT_DIM)),
            Span::styled(&run.agent_name, Style::default().fg(CYBER_CYAN).bold()),
        ]),
        Line::from(vec![
            Span::styled("Status: ", Style::default().fg(TEXT_DIM)),
            Span::styled(
                run.status.as_str(),
                Style::default().fg(run.status.color()).bold(),
            ),
        ]),
        Line::from(""),
        Line::from(vec![
            Span::styled("Started: ", Style::default().fg(TEXT_DIM)),
            Span::styled(&run.started_at, Style::default().fg(TEXT_PRIMARY)),
        ]),
        Line::from(vec![
            Span::styled("Duration: ", Style::default().fg(TEXT_DIM)),
            Span::styled(
                run.duration.as_deref().unwrap_or("--"),
                Style::default().fg(TEXT_PRIMARY),
            ),
        ]),
        Line::from(""),
    ];

    // Add model info if available
    if let Some(model) = &run.model {
        info_lines.push(Line::from(vec![
            Span::styled("Model: ", Style::default().fg(TEXT_DIM)),
            Span::styled(model, Style::default().fg(TEXT_PRIMARY)),
        ]));
    }

    // Add token usage if available
    if let (Some(input), Some(output)) = (run.input_tokens, run.output_tokens) {
        info_lines.push(Line::from(""));
        info_lines.push(Line::from("Token Usage:").style(Style::default().fg(NEON_GREEN).bold()));
        info_lines.push(Line::from(vec![
            Span::styled("  Input: ", Style::default().fg(TEXT_DIM)),
            Span::styled(format!("{}", input), Style::default().fg(TEXT_PRIMARY)),
        ]));
        info_lines.push(Line::from(vec![
            Span::styled("  Output: ", Style::default().fg(TEXT_DIM)),
            Span::styled(format!("{}", output), Style::default().fg(TEXT_PRIMARY)),
        ]));
        info_lines.push(Line::from(vec![
            Span::styled("  Total: ", Style::default().fg(TEXT_DIM)),
            Span::styled(
                format!("{}", input + output),
                Style::default().fg(NEON_GREEN).bold(),
            ),
        ]));
    }

    // Add cost if available
    if let Some(cost) = run.total_cost {
        info_lines.push(Line::from(""));
        info_lines.push(Line::from(vec![
            Span::styled("Cost: ", Style::default().fg(TEXT_DIM)),
            Span::styled(
                format!("${:.4}", cost),
                Style::default().fg(AMBER_WARN).bold(),
            ),
        ]));
    }

    if run.shield_blocked {
        info_lines.push(Line::from(""));
        info_lines.push(
            Line::from("Safety (Shield):").style(Style::default().fg(Color::Rgb(255, 140, 0)).bold()),
        );
        info_lines.push(Line::from(vec![
            Span::styled("  Action: ", Style::default().fg(TEXT_DIM)),
            Span::styled("BLOCKED", Style::default().fg(Color::Rgb(255, 140, 0)).bold()),
        ]));
        if let Some(reason) = &run.safety_reason {
            info_lines.push(Line::from(vec![
                Span::styled("  Reason: ", Style::default().fg(TEXT_DIM)),
                Span::styled(reason, Style::default().fg(TEXT_PRIMARY)),
            ]));
        }
        if let Some(error) = &run.error_message {
            info_lines.push(Line::from(vec![
                Span::styled("  Error: ", Style::default().fg(TEXT_DIM)),
                Span::styled(error, Style::default().fg(Color::Rgb(255, 69, 69))),
            ]));
        }
    } else if let Some(error) = &run.error_message {
        info_lines.push(Line::from(""));
        info_lines
            .push(Line::from("Error:").style(Style::default().fg(Color::Rgb(255, 69, 69)).bold()));
        info_lines.push(
            Line::from(format!("  {}", error)).style(Style::default().fg(Color::Rgb(255, 69, 69))),
        );
    }

    let info_paragraph = Paragraph::new(info_lines).wrap(Wrap { trim: false });
    f.render_widget(info_paragraph, info_inner);

    // Right: Logs
    let logs_block = Block::default()
        .title(" 📜 Execution Logs ")
        .borders(Borders::ALL)
        .border_style(Style::default().fg(NEON_GREEN))
        .style(Style::default().bg(BG_PANEL));

    let logs_inner = logs_block.inner(content_chunks[1]);
    f.render_widget(logs_block, content_chunks[1]);

    let log_lines: Vec<Line> = if run.logs.is_empty() {
        vec![
            Line::from(""),
            Line::from("No logs available").style(Style::default().fg(TEXT_DIM)),
        ]
    } else {
        run.logs
            .iter()
            .map(|log| Line::from(log.as_str()).style(Style::default().fg(TEXT_PRIMARY)))
            .collect()
    };

    let logs_paragraph = Paragraph::new(log_lines)
        .wrap(Wrap { trim: false })
        .scroll((state.detail_log_scroll, 0));
    f.render_widget(logs_paragraph, logs_inner);

    // Footer
    let footer_block = Block::default()
        .title(" ⌨️  Actions ")
        .borders(Borders::ALL)
        .border_style(Style::default().fg(TEXT_DIM))
        .style(Style::default().bg(BG_PANEL));

    let footer_inner = footer_block.inner(chunks[2]);
    f.render_widget(footer_block, chunks[2]);

    let footer_text = Line::from(vec![
        Span::styled("ESC/Enter", Style::default().fg(BRAND_PURPLE).bold()),
        Span::styled(" Back to List  │  ", Style::default().fg(TEXT_DIM)),
        Span::styled("↑/↓", Style::default().fg(CYBER_CYAN).bold()),
        Span::styled(" Scroll logs  │  ", Style::default().fg(TEXT_DIM)),
        Span::styled("R", Style::default().fg(NEON_GREEN).bold()),
        Span::styled(" Refresh  │  ", Style::default().fg(TEXT_DIM)),
        Span::styled("C", Style::default().fg(Color::Rgb(255, 69, 69)).bold()),
        Span::styled(" Cancel run", Style::default().fg(TEXT_DIM)),
    ]);

    let footer_paragraph = Paragraph::new(footer_text).alignment(Alignment::Center);
    f.render_widget(footer_paragraph, footer_inner);
}

fn render_actions(f: &mut Frame, area: Rect) {
    let block = Block::default()
        .title(" ⌨️  Actions ")
        .borders(Borders::ALL)
        .border_style(Style::default().fg(TEXT_DIM))
        .style(Style::default().bg(BG_PANEL));

    let inner = block.inner(area);
    f.render_widget(block, area);

    let text = Line::from(vec![
        Span::styled("↑/↓", Style::default().fg(CYBER_CYAN).bold()),
        Span::styled(" Select  │  ", Style::default().fg(TEXT_DIM)),
        Span::styled("Enter", Style::default().fg(BRAND_PURPLE).bold()),
        Span::styled(" Details  │  ", Style::default().fg(TEXT_DIM)),
        Span::styled("F", Style::default().fg(AMBER_WARN).bold()),
        Span::styled(" Filter  │  ", Style::default().fg(TEXT_DIM)),
        Span::styled("S", Style::default().fg(AMBER_WARN).bold()),
        Span::styled(" Sort  │  ", Style::default().fg(TEXT_DIM)),
        Span::styled("R", Style::default().fg(NEON_GREEN).bold()),
        Span::styled(" Refresh  │  ", Style::default().fg(TEXT_DIM)),
        Span::styled("A", Style::default().fg(CYBER_CYAN).bold()),
        Span::styled(" Live  │  ", Style::default().fg(TEXT_DIM)),
        Span::styled("C", Style::default().fg(Color::Rgb(255, 69, 69)).bold()),
        Span::styled(" Cancel  │  ", Style::default().fg(TEXT_DIM)),
        Span::styled("D", Style::default().fg(Color::Rgb(255, 69, 69)).bold()),
        Span::styled(" Delete  │  ", Style::default().fg(TEXT_DIM)),
        Span::styled("ESC", Style::default().fg(BRAND_PURPLE).bold()),
        Span::styled(" Close", Style::default().fg(TEXT_DIM)),
    ]);

    let paragraph = Paragraph::new(text)
        .style(Style::default().fg(TEXT_PRIMARY))
        .alignment(Alignment::Center);

    f.render_widget(paragraph, inner);
}
