/// Portal Connection Screen (formerly Gateway Connection)
/// Clean, professional interface for connecting to Gateway servers
use std::time::Instant;

use crate::app::{AppState, LogLevel, OperationMode, PortalField};
use ratatui::layout::Alignment;
use ratatui::prelude::*;
use ratatui::widgets::{Block, Borders, Clear, Paragraph, Wrap};

/// True when this OS session is linked to a Gateway (even if Connection Portal was reset).
fn session_linked(state: &AppState) -> bool {
    state.connection_portal.connection_success
        || (matches!(state.operation_mode, OperationMode::Connected) && state.gateway_url.is_some())
}

// === 4RUNR BRAND COLORS ===
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);
const ERROR_RED: Color = Color::Rgb(255, 69, 69);

/// First non-empty line only — avoids showing the same URL twice when state accidentally holds duplicate lines.
fn single_display_line_url(raw: &str) -> String {
    raw.lines()
        .map(str::trim)
        .find(|l| !l.is_empty())
        .map(str::to_string)
        .unwrap_or_else(|| raw.trim().to_string())
}

/// One row for the URL field: never multi-line wrap (centering + wrap caused a “double URL” look on some hosts).
fn truncate_visible(s: &str, max_cols: u16) -> String {
    let max_chars = (max_cols as usize).max(4);
    let n = s.chars().count();
    if n <= max_chars {
        return s.to_string();
    }
    let take = max_chars.saturating_sub(1);
    format!("{}…", s.chars().take(take).collect::<String>())
}

/// Break long strings for fixed-width panels. Prefers breaking at `\`, `/`, whitespace, or `-` so paths like `4runr-os` are not split mid-token when a separator exists in the window.
fn wrap_text_for_panel(text: &str, max_chars: usize) -> Vec<String> {
    let max_chars = max_chars.max(8);
    let mut out: Vec<String> = Vec::new();
    for segment in text.split('\n') {
        let mut line = segment.trim();
        if line.is_empty() {
            continue;
        }
        while !line.is_empty() {
            let char_count = line.chars().count();
            if char_count <= max_chars {
                out.push(line.to_string());
                break;
            }
            let byte_limit = line
                .char_indices()
                .nth(max_chars)
                .map(|(i, _)| i)
                .unwrap_or(line.len());
            let window = &line[..byte_limit];
            let mut split_byte = byte_limit;
            if let Some((pos, ch)) = window
                .char_indices()
                .rev()
                .find(|(_, c)| *c == '\\' || *c == '/' || c.is_whitespace())
            {
                let after = pos + ch.len_utf8();
                if after > 0 && after < byte_limit {
                    split_byte = after;
                }
            }
            if split_byte == byte_limit {
                if let Some((pos, ch)) = window.char_indices().rev().find(|(_, c)| *c == '-') {
                    let after = pos + ch.len_utf8();
                    if after > 0 && after < byte_limit {
                        split_byte = after;
                    }
                }
            }
            let (chunk, rest) = line.split_at(split_byte);
            let chunk_trim = chunk.trim_end();
            let rest_trim = rest.trim_start();
            if chunk_trim.is_empty() {
                let force = line
                    .char_indices()
                    .nth(max_chars)
                    .map(|(i, _)| i)
                    .unwrap_or(line.len())
                    .max(1);
                let (a, b) = line.split_at(force);
                out.push(a.to_string());
                line = b.trim_start();
            } else {
                out.push(chunk_trim.to_string());
                line = rest_trim;
            }
        }
    }
    out
}

/// Main render function - Full-screen standalone portal
pub fn render(f: &mut Frame, state: &AppState) {
    // DEBUG: Track render calls
    #[cfg(debug_assertions)]
    {
        static mut RENDER_COUNT: u64 = 0;
        unsafe {
            RENDER_COUNT += 1;
            if RENDER_COUNT % 10 == 1 {
                // Log every 10th render to avoid spam
                eprintln!("[DEBUG] Connection Portal render #{}", RENDER_COUNT);
            }
        }
    }

    let area = f.size();

    // CRITICAL: Validate terminal size before rendering
    // This prevents character corruption when terminal size is wrong on initial render
    if area.width == 0 || area.height == 0 {
        // Terminal not ready - skip render
        return;
    }

    // Ensure minimum size for portal (60x24 — URL row needs fixed height to avoid overlapping content)
    if area.width < 60 || area.height < 24 {
        // Render "too small" message
        render_too_small_connection(f, area);
        return;
    }

    // CRITICAL: Clear the ENTIRE screen first - this is a standalone screen, not an overlay
    f.render_widget(Clear, area);

    // Render full-screen background (no dimming - this is the only screen)
    let bg = Block::default().style(Style::default().bg(BG_PANEL));
    f.render_widget(bg, area);

    // Calculate centered portal dimensions (smaller dialog on full-screen background)
    let portal_width = area.width.min(80).max(60);
    let portal_height = area.height.min(32).max(24);

    let portal_x = (area.width.saturating_sub(portal_width)) / 2;
    let portal_y = (area.height.saturating_sub(portal_height)) / 2;

    let portal_area = Rect {
        x: portal_x,
        y: portal_y,
        width: portal_width,
        height: portal_height,
    };

    // Clear the portal area for clean rendering
    f.render_widget(Clear, portal_area);

    // Render portal background
    let bg = Block::default().style(Style::default().bg(BG_PANEL));
    f.render_widget(bg, portal_area);

    // Layout sections
    use ratatui::layout::{Constraint, Direction, Layout};

    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .constraints([
            Constraint::Length(3), // Header
            // Fixed height prevents chunk overlap / ghost “second” Gateway URL frame on small terminals
            Constraint::Length(8),
            Constraint::Min(8),
            Constraint::Length(3),
        ])
        .split(portal_area);

    // DEBUG: Log chunk positions to detect overlap
    #[cfg(debug_assertions)]
    {
        static mut LOG_COUNT: u64 = 0;
        unsafe {
            LOG_COUNT += 1;
            if LOG_COUNT % 50 == 1 {
                // Log every 50th render
                eprintln!("[DEBUG] Portal area: {:?}", portal_area);
                eprintln!("[DEBUG] chunks[0] (header): {:?}", chunks[0]);
                eprintln!("[DEBUG] chunks[1] (url section): {:?}", chunks[1]);
                eprintln!("[DEBUG] chunks[2] (content): {:?}", chunks[2]);
                eprintln!("[DEBUG] chunks[3] (actions): {:?}", chunks[3]);
                if chunks[1].y + chunks[1].height > chunks[2].y {
                    eprintln!("[DEBUG] ⚠️ OVERLAP DETECTED: chunks[1] overlaps chunks[2]!");
                }
            }
        }
    }

    // Render sections
    render_header(f, chunks[0], state);
    render_url_section(f, chunks[1], state);
    render_content_area(f, chunks[2], state);
    render_actions(f, chunks[3], state);
}

fn render_header(f: &mut Frame, area: Rect, state: &AppState) {
    // Fill background first for windowed terminals
    let bg_fill = Block::default().style(Style::default().bg(BG_PANEL));
    f.render_widget(bg_fill, area);
    f.render_widget(Clear, area);

    let portal = &state.connection_portal;

    let (title, border_color): (String, Color) =
        if portal.connection_success || session_linked(state) {
            let u = portal
                .last_successful_url
                .as_deref()
                .unwrap_or(portal.gateway_url.as_str());
            let stripped = u.replace("http://", "").replace("https://", "");
            let host = stripped.split('/').next().unwrap_or(u).to_string();
            (
                format!(" Portal Connection - Connected to {} ", host),
                NEON_GREEN,
            )
        } else if portal.error.is_some() {
            (" Portal Connection - Error ".to_string(), ERROR_RED)
        } else if portal.connecting {
            (
                " Portal Connection - Connecting... ".to_string(),
                AMBER_WARN,
            )
        } else {
            (" Portal Connection ".to_string(), BRAND_PURPLE)
        };

    let block = Block::default()
        .title(title)
        .borders(Borders::ALL)
        .border_style(Style::default().fg(border_color).bold())
        .style(Style::default().bg(BG_PANEL));

    f.render_widget(block, area);
}

/// One bordered “Gateway URL” region: hint text + URL value (no separate description row = no duplicate frames).
fn render_url_section(f: &mut Frame, area: Rect, state: &AppState) {
    f.render_widget(Clear, area);
    let outer_bg = Block::default().style(Style::default().bg(BG_PANEL));
    f.render_widget(outer_bg, area);

    #[cfg(debug_assertions)]
    {
        static mut LOG_COUNT: u64 = 0;
        unsafe {
            LOG_COUNT += 1;
            if LOG_COUNT % 50 == 1 {
                eprintln!("[DEBUG] render_url_section called - area: {:?}", area);
            }
        }
    }

    let portal = &state.connection_portal;

    if portal.connection_success || session_linked(state) {
        let url_raw = state
            .gateway_url
            .as_deref()
            .or(portal.last_successful_url.as_deref())
            .unwrap_or(portal.gateway_url.as_str());
        let url = single_display_line_url(url_raw);
        let stripped = url.replace("http://", "").replace("https://", "");
        let short = stripped.split('/').next().unwrap_or(url.as_str());
        let block = Block::default()
            .title(" Gateway endpoint ")
            .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);
        let text = Paragraph::new(format!("Connected to {}", short))
            .style(Style::default().fg(NEON_GREEN).bg(BG_PANEL))
            .alignment(Alignment::Center);
        f.render_widget(text, inner);
        return;
    }

    let url_one_line = single_display_line_url(&portal.gateway_url);
    let hint = if url_one_line.contains("localhost") || url_one_line.contains("127.0.0.1") {
        "Start Gateway first, then press Enter to connect"
    } else {
        "Enter Gateway server URL and press Enter to connect"
    };

    let url_focused = portal.focused_field == PortalField::GatewayUrl;
    let input_enabled = !portal.connecting;
    let url_style = if url_focused && input_enabled {
        Style::default().fg(CYBER_CYAN).bold().bg(BG_PANEL)
    } else if input_enabled {
        Style::default().fg(TEXT_DIM).bg(BG_PANEL)
    } else {
        Style::default().fg(TEXT_MUTED).bg(BG_PANEL)
    };

    let border_style = if portal.error.is_some() {
        Style::default().fg(ERROR_RED).bold()
    } else {
        url_style
    };

    let url_display = url_one_line;

    let block = Block::default()
        .title(" Gateway endpoint ")
        .borders(Borders::ALL)
        .border_style(border_style)
        .style(Style::default().bg(BG_PANEL));
    let inner = block.inner(area);
    f.render_widget(block, area);

    // URL = exactly one line (value), hint below (helper). Avoids duplicate URL rows from wrap + center alignment.
    let url_visible = truncate_visible(&url_display, inner.width.saturating_sub(2));
    let mut lines: Vec<Line> = vec![
        Line::from(Span::styled(url_visible, url_style)),
        Line::from(""),
        Line::from(Span::styled(
            hint,
            Style::default().fg(TEXT_DIM).bg(BG_PANEL),
        )),
    ];
    // Pad or trim to inner height so we do not leave stale cells or spill into the next panel (windowed hosts).
    let target_h = inner.height as usize;
    while lines.len() < target_h {
        lines.push(Line::from(Span::styled("", Style::default().bg(BG_PANEL))));
    }
    if lines.len() > target_h {
        lines.truncate(target_h);
    }
    let paragraph = Paragraph::new(lines)
        .style(Style::default().bg(BG_PANEL))
        .alignment(Alignment::Left);
    f.render_widget(paragraph, inner);
}

fn render_content_area(f: &mut Frame, area: Rect, state: &AppState) {
    let portal = &state.connection_portal;

    // CRITICAL FIX: Clear the content area before rendering to prevent artifacts
    f.render_widget(Clear, area);

    // Ensure minimum area size for bordered boxes
    if area.height < 8 || area.width < 20 {
        // Area too small - just show simple text
        let text = if portal.error.is_some() {
            "✗ Error"
        } else if session_linked(state) {
            "✓ Connected"
        } else if portal.connecting {
            "⏳ Connecting..."
        } else {
            "Enter URL, or keep localhost:3001 and start Gateway (see log); then press Enter"
        };

        let paragraph =
            Paragraph::new(text)
                .alignment(Alignment::Center)
                .style(Style::default().fg(if portal.error.is_some() {
                    ERROR_RED
                } else if session_linked(state) {
                    NEON_GREEN
                } else if portal.connecting {
                    AMBER_WARN
                } else {
                    TEXT_DIM
                }));
        f.render_widget(paragraph, area);
        return;
    }

    if portal.error.is_some() {
        let err_msg = portal.error.as_ref().unwrap();
        if !portal.activity_log.is_empty() {
            // Compact status/error strip; most vertical space for the activity log (CLI trace).
            use ratatui::layout::{Constraint, Direction, Layout};
            let total_h = area.height;
            let log_min: u16 = 8;
            let err_h = (total_h / 3).max(10).min(total_h.saturating_sub(log_min));
            let chunks = Layout::default()
                .direction(Direction::Vertical)
                .constraints([Constraint::Length(err_h), Constraint::Min(log_min)])
                .split(area);
            render_error(f, chunks[0], err_msg);
            render_activity_log(f, chunks[1], state);
        } else {
            render_error(f, area, err_msg);
        }
    } else if session_linked(state) {
        render_success(f, area, state);
    } else if portal.connecting {
        if portal.activity_log.is_empty() {
            render_connecting(f, area, state);
        } else {
            use ratatui::layout::{Constraint, Direction, Layout};
            let strip = area.height.min(9).max(6);
            let chunks = Layout::default()
                .direction(Direction::Vertical)
                .constraints([Constraint::Length(strip), Constraint::Min(4)])
                .split(area);
            render_connecting(f, chunks[0], state);
            render_activity_log(f, chunks[1], state);
        }
    } else if !portal.activity_log.is_empty() {
        // Show activity log with instructions when there's no error
        render_activity_log(f, area, state);
    } else {
        // Empty state - show instructions
        render_instructions(f, area);
    }
}

fn render_connecting(f: &mut Frame, area: Rect, state: &AppState) {
    let portal = &state.connection_portal;
    let url_one = single_display_line_url(&portal.gateway_url);
    let url = if url_one.is_empty() {
        "…"
    } else {
        url_one.as_str()
    };
    let elapsed_secs = portal
        .connecting_started
        .map(|t: Instant| t.elapsed().as_secs())
        .unwrap_or(0);
    let block = Block::default()
        .title(format!(" ⏳ CONNECTING — {}s ", elapsed_secs))
        .borders(Borders::ALL)
        .border_style(Style::default().fg(AMBER_WARN).bold())
        .style(Style::default().bg(BG_PANEL));
    let inner = block.inner(area);
    f.render_widget(block, area);

    // Simple “pulse” bar: fill grows and wraps so the UI keeps moving while we wait on Node.
    let bar_w = (inner.width.saturating_sub(4) as usize).clamp(8, 48);
    let fill = (elapsed_secs as usize) % (bar_w + 1);
    let bar: String = (0..bar_w)
        .map(|i| if i < fill { '█' } else { '░' })
        .collect();

    let mut lines: Vec<Line> = vec![
        Line::from(""),
        Line::from(vec![Span::styled(
            format!("Connecting to {} …", url),
            Style::default().fg(AMBER_WARN).bold(),
        )])
        .alignment(Alignment::Center),
        Line::from(vec![Span::styled(
            format!(
                "Elapsed {}s — waiting on CLI (gateway autostart can take a few minutes)",
                elapsed_secs
            ),
            Style::default().fg(CYBER_CYAN),
        )])
        .alignment(Alignment::Center),
    ];
    if inner.height > 5 {
        lines.push(
            Line::from(vec![Span::styled(bar, Style::default().fg(TEXT_DIM))])
                .alignment(Alignment::Center),
        );
        lines.push(Line::from(""));
        lines.push(
            Line::from(Span::styled(
                "See Connection Activity Log for live steps from the Gateway starter",
                Style::default().fg(TEXT_DIM),
            ))
            .alignment(Alignment::Center),
        );
    }

    let target_h = inner.height as usize;
    while lines.len() < target_h {
        lines.push(Line::from(Span::styled("", Style::default().bg(BG_PANEL))));
    }
    if lines.len() > target_h {
        lines.truncate(target_h);
    }

    let paragraph = Paragraph::new(lines)
        .alignment(Alignment::Center)
        .style(Style::default().bg(BG_PANEL));
    f.render_widget(paragraph, inner);
}

fn render_activity_log(f: &mut Frame, area: Rect, state: &AppState) {
    let portal = &state.connection_portal;

    let block = Block::default()
        .title(" Connection Activity Log ")
        .borders(Borders::ALL)
        .border_style(Style::default().fg(CYBER_CYAN).bold())
        .style(Style::default().bg(BG_PANEL));

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

    let max_lines = inner.height.saturating_sub(1) as usize;
    let inner_w = inner.width as usize;
    // ~22 cols for `[12:11:21] ⚠ ` + message start; continuation lines indent to match
    let msg_cols = inner_w.saturating_sub(22).max(10);
    let indent = "                      ";

    let mut built: Vec<Line> = Vec::new();
    for entry in portal.activity_log.iter() {
        let (color, icon) = match entry.level {
            LogLevel::Success => (NEON_GREEN, "✓"),
            LogLevel::Error => (ERROR_RED, "✗"),
            LogLevel::Warning => (AMBER_WARN, "⚠"),
            LogLevel::Info => (TEXT_DIM, "•"),
        };
        let chunks = wrap_text_for_panel(&entry.message, msg_cols);
        for (i, chunk) in chunks.into_iter().enumerate() {
            let sty = Style::default().fg(TEXT_PRIMARY).bg(BG_PANEL);
            if i == 0 {
                built.push(Line::from(vec![
                    Span::styled(
                        format!("[{}] ", entry.timestamp),
                        Style::default().fg(TEXT_MUTED).bg(BG_PANEL),
                    ),
                    Span::styled(
                        format!("{} ", icon),
                        Style::default().fg(color).bold().bg(BG_PANEL),
                    ),
                    Span::styled(chunk, sty),
                ]));
            } else {
                built.push(Line::from(Span::styled(
                    format!("{}{}", indent, chunk),
                    sty,
                )));
            }
        }
    }

    let start = if built.len() > max_lines {
        built.len() - max_lines
    } else {
        0
    };
    let visible: Vec<Line> = built.into_iter().skip(start).collect();

    let paragraph = Paragraph::new(visible).style(Style::default().bg(BG_PANEL));

    f.render_widget(paragraph, inner);
}

fn render_error(f: &mut Frame, area: Rect, error: &str) {
    // Clean up error message
    let error_msg = error
        .replace("Request failed with status code ", "")
        .replace("Error: ", "")
        .replace("Failed to connect: ", "")
        .trim()
        .to_string();

    let (display_title, display_msg) = if error_msg.contains("in use")
        && (error_msg.contains("port") || error_msg.contains("3002"))
    {
        ("Port in use", error_msg.clone())
    } else if error_msg.contains("404") {
        ("Not the Gateway (404)", "Something is running at this URL but it did not respond as the 4Runr Gateway.\n\nThe process on this port may not be the Gateway. Stop it and start the Gateway (e.g. cd apps/gateway && npm start), or use a different URL.".to_string())
    } else if error_msg.contains("429") || error_msg.to_lowercase().contains("too many requests") {
        (
            "Rate limited (429)",
            "Something on this port returned HTTP 429 (too many requests).\n\nIf this is the 4Runr Gateway, wait a few seconds and press Enter to retry. Otherwise use another port (e.g. http://localhost:3002).".to_string(),
        )
    } else if error_msg.contains("ECONNREFUSED") || error_msg.contains("Connection refused") {
        ("Connection Refused", "The Gateway server is not running or not accessible.\n\nStart the server and try again.".to_string())
    } else if error_msg.contains("ETIMEDOUT") || error_msg.contains("timeout") {
        (
            "Connection Timeout",
            "The server did not respond in time.\n\nCheck your network connection and try again."
                .to_string(),
        )
    } else if error_msg.is_empty() {
        (
            "Connection Failed",
            "Unable to connect to the Gateway server.".to_string(),
        )
    } else {
        ("Connection Error", error_msg)
    };

    // Clean, prominent error box
    let block = Block::default()
        .title(format!(" ✗ {}", display_title))
        .borders(Borders::ALL)
        .border_style(Style::default().fg(ERROR_RED).bold())
        .style(Style::default().bg(BG_PANEL));

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

    let wrap_w = inner.width.saturating_sub(2) as usize;
    let mut message_lines: Vec<Line> = Vec::new();
    for raw in display_msg.split('\n') {
        let t = raw.trim();
        if t.is_empty() {
            continue;
        }
        for part in wrap_text_for_panel(t, wrap_w.max(12)) {
            message_lines.push(Line::from(Span::styled(
                part,
                Style::default().fg(TEXT_PRIMARY).bg(BG_PANEL),
            )));
        }
    }

    // Never draw more lines than fit inside the block (avoids garbled overlap on small panes).
    let footer_h = 2u16;
    let max_body = (inner.height).saturating_sub(footer_h).max(1) as usize;
    if message_lines.len() > max_body {
        message_lines.truncate(max_body.saturating_sub(1));
        message_lines.push(Line::from(Span::styled(
            "… (message truncated — resize taller or read Activity Log below)",
            Style::default().fg(TEXT_DIM).bg(BG_PANEL),
        )));
    }

    let mut all_lines = vec![Line::from("")];
    all_lines.extend(message_lines);
    all_lines.push(Line::from(""));
    all_lines.push(Line::from(Span::styled(
        "Press Enter to retry or ESC to cancel",
        Style::default().fg(TEXT_DIM).bg(BG_PANEL),
    )));

    let paragraph = Paragraph::new(all_lines)
        .style(Style::default().bg(BG_PANEL))
        .alignment(Alignment::Left)
        .wrap(Wrap { trim: true });

    f.render_widget(paragraph, inner);
}

fn render_success(f: &mut Frame, area: Rect, state: &AppState) {
    let portal = &state.connection_portal;
    let url_raw = state
        .gateway_url
        .as_deref()
        .or(state.connection_portal.last_successful_url.as_deref())
        .unwrap_or("unknown");
    let url = single_display_line_url(url_raw);
    let health_status = if state.gateway_healthy {
        ("Healthy & Verified", NEON_GREEN)
    } else if session_linked(state) {
        (
            "Unreachable — press R to re-verify or D to disconnect",
            ERROR_RED,
        )
    } else {
        ("Verifying /health…", AMBER_WARN)
    };
    let block = Block::default()
        .title(" ✓ CONNECTED ")
        .borders(Borders::ALL)
        .border_style(Style::default().fg(NEON_GREEN).bold())
        .style(Style::default().bg(BG_PANEL));
    let inner = block.inner(area);
    f.render_widget(block, area);
    let mut lines: Vec<Line> = vec![Line::from("")];
    if !portal.connection_success && session_linked(state) {
        lines.push(Line::from(Span::styled(
            "Session still linked — press D to disconnect, R to re-verify, Enter to reconnect.",
            Style::default().fg(CYBER_CYAN),
        )));
        lines.push(Line::from(""));
    }
    lines.extend([
        Line::from(vec![
            Span::styled("Connected to ", Style::default().fg(TEXT_DIM)),
            Span::styled(url, Style::default().fg(CYBER_CYAN).bold()),
        ]),
        Line::from(""),
        Line::from(vec![
            Span::styled("Status: ", Style::default().fg(TEXT_DIM)),
            Span::styled(health_status.0, Style::default().fg(health_status.1).bold()),
        ]),
        Line::from(""),
    ]);
    let mut tail: Vec<Line> = Vec::new();
    if let Some(ref s) = state.gateway_db_summary {
        tail.push(Line::from(vec![
            Span::styled("4Runr DB: ", Style::default().fg(TEXT_DIM)),
            Span::styled(s.as_str(), Style::default().fg(CYBER_CYAN)),
        ]));
        tail.push(Line::from(""));
    }
    tail.extend([
        Line::from(Span::styled(
            "Portal Monitoring: live /health + /ready + /metrics on each refresh (HTTP totals rise when traffic hits this Gateway).",
            Style::default().fg(TEXT_DIM),
        )),
        Line::from(""),
        Line::from(vec![
            Span::styled("Press ", Style::default().fg(TEXT_DIM)),
            Span::styled("D", Style::default().fg(ERROR_RED).bold()),
            Span::styled(" disconnect  ·  ", Style::default().fg(TEXT_DIM)),
            Span::styled("R", Style::default().fg(AMBER_WARN).bold()),
            Span::styled(" re-verify  ·  ", Style::default().fg(TEXT_DIM)),
            Span::styled("M", Style::default().fg(CYBER_CYAN).bold()),
            Span::styled(" Portal Monitoring  ·  ", Style::default().fg(TEXT_DIM)),
            Span::styled("ESC", Style::default().fg(BRAND_PURPLE).bold()),
            Span::styled(" close", Style::default().fg(TEXT_DIM)),
        ]),
    ]);
    lines.extend(tail);

    let paragraph = Paragraph::new(lines)
        .alignment(Alignment::Left)
        .wrap(Wrap { trim: true });

    f.render_widget(paragraph, inner);
}

fn render_instructions(f: &mut Frame, area: Rect) {
    let lines = vec![
        Line::from(""),
        Line::from(Span::styled(
            "Press L to continue in LOCAL mode, or start Gateway and press Enter to connect",
            Style::default().fg(TEXT_DIM),
        ))
        .alignment(Alignment::Center),
    ];

    let paragraph = Paragraph::new(lines).alignment(Alignment::Center);

    f.render_widget(paragraph, area);
}

fn render_actions(f: &mut Frame, area: Rect, state: &AppState) {
    // Fill background first for windowed terminals
    let bg_fill = Block::default().style(Style::default().bg(BG_PANEL));
    f.render_widget(bg_fill, area);
    f.render_widget(Clear, area);

    let portal = &state.connection_portal;

    // If the session is already linked, never show "Connecting…" in the footer (stale `connecting` flag).
    let text = if portal.connecting && !session_linked(state) {
        Line::from(vec![
            Span::styled("⏳ ", Style::default().fg(AMBER_WARN)),
            Span::styled("Connecting...", Style::default().fg(AMBER_WARN)),
        ])
    } else if session_linked(state) {
        Line::from(vec![
            Span::styled("✓ ", Style::default().fg(NEON_GREEN)),
            Span::styled("Gateway linked — ", Style::default().fg(NEON_GREEN)),
            Span::styled("D", Style::default().fg(ERROR_RED).bold()),
            Span::styled(" disconnect  ·  ", Style::default().fg(NEON_GREEN)),
            Span::styled("R", Style::default().fg(AMBER_WARN).bold()),
            Span::styled(" re-verify  ·  ", Style::default().fg(NEON_GREEN)),
            Span::styled("M", Style::default().fg(CYBER_CYAN).bold()),
            Span::styled(" monitor  ·  ", Style::default().fg(NEON_GREEN)),
            Span::styled("ESC", Style::default().fg(BRAND_PURPLE).bold()),
            Span::styled(" close", Style::default().fg(NEON_GREEN)),
        ])
    } else {
        Line::from(vec![
            Span::styled("↑↓ ", Style::default().fg(CYBER_CYAN).bold()),
            Span::styled("Navigate  ", Style::default().fg(TEXT_DIM)),
            Span::styled("│  ", Style::default().fg(TEXT_MUTED)),
            Span::styled("Enter ", Style::default().fg(NEON_GREEN).bold()),
            Span::styled("Connect  ", Style::default().fg(TEXT_DIM)),
            Span::styled("│  ", Style::default().fg(TEXT_MUTED)),
            Span::styled("L ", Style::default().fg(AMBER_WARN).bold()),
            Span::styled("Local Mode  ", Style::default().fg(TEXT_DIM)),
            Span::styled("│  ", Style::default().fg(TEXT_MUTED)),
            Span::styled("ESC ", Style::default().fg(BRAND_PURPLE).bold()),
            Span::styled("Cancel", Style::default().fg(TEXT_DIM)),
        ])
    };

    let block = Block::default()
        .borders(Borders::ALL)
        .border_style(Style::default().fg(TEXT_DIM))
        .style(Style::default().bg(BG_PANEL));

    let paragraph = Paragraph::new(text)
        .block(block)
        .alignment(Alignment::Center);

    f.render_widget(paragraph, area);
}

fn render_too_small_connection(f: &mut Frame, area: Rect) {
    // Clear screen
    f.render_widget(Clear, area);

    // Render background
    let bg = Block::default().style(Style::default().bg(BG_PANEL));
    f.render_widget(bg, area);

    // Center the message
    let msg = vec![
        Line::from(""),
        Line::from(Span::styled(
            "Connection Portal",
            Style::default().fg(BRAND_PURPLE).bold(),
        )),
        Line::from(""),
        Line::from(Span::styled(
            "Terminal too small",
            Style::default().fg(ERROR_RED),
        )),
        Line::from(""),
        Line::from(Span::styled(
            format!("Current: {}x{}", area.width, area.height),
            Style::default().fg(TEXT_DIM),
        )),
        Line::from(Span::styled(
            "Required: 60x24",
            Style::default().fg(TEXT_PRIMARY),
        )),
    ];

    let paragraph = Paragraph::new(msg).alignment(Alignment::Center);

    f.render_widget(
        paragraph,
        Rect {
            x: area.x,
            y: area.y + (area.height / 3),
            width: area.width,
            height: area.height / 3,
        },
    );
}
