//! Main UI layout.

use ratatui::{
    layout::{Alignment, Constraint, Direction, Layout, Rect},
    style::{Color, Modifier, Style},
    text::{Line, Span, Text},
    widgets::{Block, Borders, Clear, List, ListItem, Paragraph, Wrap},
    Frame,
};

use crate::app::{App, InputMode, RadioState, LICENSE_TEXT};

use super::captures_list::render_captures_list;
use super::command::render_command_line;
use super::settings_menu::{render_settings_dropdown, render_settings_tabs};
use super::signal_menu::render_signal_menu;
use super::status_bar::render_status_bar;
use super::text_overlay::render_text_overlay;

use crate::app::InputMode as IM;

const VERSION: &str = env!("CARGO_PKG_VERSION");

/// RSSI bar width (right side)
const RSSI_BAR_WIDTH: u16 = 5;

/// Draw the entire UI
pub fn draw_ui(frame: &mut Frame, app: &App) {
    let show_settings = matches!(app.input_mode, IM::SettingsSelect | IM::SettingsEdit);
    let show_command = app.input_mode == IM::Command;

    // Full-width rows: header, settings (optional), then middle row split into [captures | RX bar], status, command (optional), help
    let main_area = frame.area();
    let mut v_constraints = vec![
        Constraint::Length(3),  // Header (full width)
        Constraint::Min(8),     // Middle: captures + RX bar
        Constraint::Length(3),  // Status bar (full width)
        Constraint::Length(1),  // Help bar (full width)
    ];
    if show_settings {
        v_constraints.insert(1, Constraint::Length(3)); // Settings tabs (full width)
    }
    if show_command {
        v_constraints.insert(v_constraints.len() - 1, Constraint::Length(3)); // Command (full width)
    }

    let rows = Layout::default()
        .direction(Direction::Vertical)
        .constraints(v_constraints)
        .split(main_area);

    let mut idx = 0;
    render_header(frame, rows[idx], app);
    idx += 1;

    if show_settings {
        render_settings_tabs(frame, rows[idx], app);
        idx += 1;
    }

    // Only the middle row is split: captures (left) | RX bar (right)
    let middle_row = rows[idx];
    let h_chunks = Layout::default()
        .direction(Direction::Horizontal)
        .constraints([Constraint::Min(0), Constraint::Length(RSSI_BAR_WIDTH)])
        .split(middle_row);
    let captures_area = h_chunks[0];
    let rssi_bar_rect = h_chunks[1];
    idx += 1;

    render_captures_list(frame, captures_area, app);
    render_rssi_bar(frame, rssi_bar_rect, app);

    render_status_bar(frame, rows[idx], app);
    idx += 1;

    if show_command {
        render_command_line(frame, rows[idx], app);
        idx += 1;
    }

    render_help_bar(frame, rows[idx], app);

    // Overlay widgets (rendered on top of everything else)
    if app.input_mode == InputMode::SignalMenu {
        render_signal_menu(frame, app);
    }

    if app.input_mode == InputMode::SettingsEdit {
        render_settings_dropdown(frame, app);
    }

    if app.input_mode == InputMode::HackRfNotDetected {
        render_hackrf_not_detected(frame, app);
    }

    if app.input_mode == InputMode::StartupImport {
        render_startup_import_prompt(frame, app);
    }

    if matches!(
        app.input_mode,
        InputMode::ExportFilename
            | InputMode::FobMetaYear
            | InputMode::FobMetaMake
            | InputMode::FobMetaModel
            | InputMode::FobMetaRegion
            | InputMode::FobMetaNotes
    ) {
        render_export_form(frame, app);
    }

    if matches!(
        app.input_mode,
        InputMode::CaptureMetaYear
            | InputMode::CaptureMetaMake
            | InputMode::CaptureMetaModel
            | InputMode::CaptureMetaRegion
    ) {
        render_capture_meta_form(frame, app);
    }

    if app.input_mode == InputMode::LoadFileBrowser {
        render_load_file_browser(frame, app);
    }

    if app.input_mode == InputMode::License {
        render_text_overlay(frame, app, "License", LICENSE_TEXT, Alignment::Left);
    }
    if app.input_mode == InputMode::Credits {
        render_text_overlay(
            frame,
            app,
            "Credits",
            super::text_overlay::CREDITS_TEXT,
            Alignment::Center,
        );
    }
}

/// Render the RSSI bar on the right (vertical bar, bottom = strong).
fn render_rssi_bar(frame: &mut Frame, area: Rect, app: &App) {
    let block = Block::default()
        .borders(Borders::ALL)
        .title(" RX ");
    let inner = block.inner(area);
    frame.render_widget(block, area);

    if inner.width == 0 || inner.height == 0 {
        return;
    }

    // Normalize RSSI (0..~1) to fill ratio; scale so ~0.5 magnitude ≈ full bar
    let fill_ratio = (app.rssi / 0.6).min(1.0);
    let filled_rows = (inner.height as f32 * fill_ratio).round() as u16;

    let filled_style = Style::default().fg(Color::Green);
    let empty_style = Style::default().fg(Color::DarkGray);

    let mut lines = Vec::with_capacity(inner.height as usize);
    for r in 0..inner.height {
        let fill = r >= inner.height.saturating_sub(filled_rows);
        let (style, ch) = if fill {
            (filled_style, "█")
        } else {
            (empty_style, " ")
        };
        let s = ch.repeat(inner.width as usize);
        lines.push(Line::from(Span::styled(s, style)));
    }
    let paragraph = Paragraph::new(Text::from(lines));
    frame.render_widget(paragraph, inner);
}

/// Render the header with title and radio status
fn render_header(frame: &mut Frame, area: Rect, app: &App) {
    let (status_symbol, status_style) = match app.radio_state {
        RadioState::Disconnected => ("○", Style::default().fg(Color::Red)),
        RadioState::Idle => ("○", Style::default().fg(Color::Yellow)),
        RadioState::Receiving => ("●", Style::default().fg(Color::Green)),
        RadioState::Transmitting => ("●", Style::default().fg(Color::Red)),
    };

    let title = format!("Keyfob Analysis Toolkit v{}", VERSION);

    // Build radio info string: device name (if any), state, freq, gains
    let amp_str = if app.amp_enabled { "ON" } else { "OFF" };
    let device_str = app
        .radio_device_name()
        .unwrap_or("No device");
    let radio_info = format!(
        "{} {} | {} | {:.2} MHz | LNA:{} VGA:{} AMP:{}",
        status_symbol,
        app.radio_state,
        device_str,
        app.frequency_mhz(),
        app.lna_gain,
        app.vga_gain,
        amp_str
    );

    // Calculate padding for right-alignment
    let padding = area
        .width
        .saturating_sub(title.len() as u16 + radio_info.len() as u16 + 4);

    let header_line = Line::from(vec![
        Span::styled(title, Style::default().add_modifier(Modifier::BOLD)),
        Span::raw(" ".repeat(padding as usize)),
        Span::styled(radio_info, status_style),
    ]);

    let header = Paragraph::new(header_line).block(
        Block::default()
            .borders(Borders::ALL)
            .border_style(Style::default()),
    );

    frame.render_widget(header, area);
}

/// Render the context-sensitive help bar
fn render_help_bar(frame: &mut Frame, area: Rect, app: &App) {
    let help_text = match app.input_mode {
        InputMode::Normal => {
            "Enter: Actions | d: Delete | Tab: Settings | r: RX Toggle | :: Command | q: Quit"
        }
        InputMode::Command => "Enter: Execute | Esc: Cancel",
        InputMode::SignalMenu => "Up/Down: Navigate | Enter: Select | Esc: Close",
        InputMode::SettingsSelect => "Left/Right: Select | Tab: Cycle | Enter: Edit | Esc: Back",
        InputMode::SettingsEdit => "Up/Down: Change Value | Enter: Apply | Esc: Cancel",
        InputMode::HackRfNotDetected => "Press any key to continue",
        InputMode::StartupImport => "y: Import | n: Skip",
        InputMode::ExportFilename => {
            match app.export_format {
                Some(crate::app::ExportFormat::Fob) => "Enter: Next Field | Esc: Cancel Export",
                Some(crate::app::ExportFormat::Flipper) => "Enter: Save & Export | Esc: Cancel Export",
                None => "Enter: Confirm | Esc: Cancel",
            }
        }
        InputMode::FobMetaYear
        | InputMode::FobMetaMake
        | InputMode::FobMetaModel
        | InputMode::FobMetaRegion => "Enter: Next Field | Esc: Cancel Export",
        InputMode::FobMetaNotes => "Enter: Save & Export | Esc: Cancel Export",
        InputMode::CaptureMetaYear
        | InputMode::CaptureMetaMake
        | InputMode::CaptureMetaModel => "Enter: Next Field | Esc: Cancel",
        InputMode::CaptureMetaRegion => "Enter: Save | Esc: Cancel",
        InputMode::License | InputMode::Credits => "Esc/Enter: Close | Up/Down: Scroll",
        InputMode::LoadFileBrowser => "Up/Down: Navigate | Enter: Open/Import | Esc: Close",
    };

    let help = Paragraph::new(Line::from(Span::styled(
        format!(" {}", help_text),
        Style::default().fg(Color::DarkGray),
    )));

    frame.render_widget(help, area);
}

/// Center a rect of given width/height in the given area
fn centered_rect(width: u16, height: u16, area: Rect) -> Rect {
    let x = area.x + area.width.saturating_sub(width) / 2;
    let y = area.y + area.height.saturating_sub(height) / 2;
    Rect::new(x, y, width.min(area.width), height.min(area.height))
}

const LOAD_BROWSER_VISIBLE_ROWS: usize = 16;

/// Render the :load file browser overlay (centered, list of dirs and .fob/.sub files).
fn render_load_file_browser(frame: &mut Frame, app: &App) {
    let area = frame.area();
    let popup_height = LOAD_BROWSER_VISIBLE_ROWS as u16 + 5;
    let popup_width = 56;
    let popup = centered_rect(popup_width, popup_height, area);

    frame.render_widget(Clear, popup);

    let path_str = app.load_browser_cwd.to_string_lossy();
    let path_display = if path_str.len() > popup_width as usize - 4 {
        format!("..{}", &path_str[path_str.len().saturating_sub(popup_width as usize - 5)..])
    } else {
        path_str.to_string()
    };

    let mut items: Vec<ListItem> = Vec::new();
    items.push(ListItem::new(Line::from(Span::styled(
        format!("  {}", path_display),
        Style::default().fg(Color::DarkGray),
    ))));
    items.push(ListItem::new(Line::from(Span::raw(""))));

    let entries = &app.load_browser_entries;
    let scroll = app.load_browser_scroll;
    let selected = app.load_browser_selected.min(entries.len().saturating_sub(1));
    let end = (scroll + LOAD_BROWSER_VISIBLE_ROWS).min(entries.len());

    for (i, (name, _path, is_dir)) in entries[scroll..end].iter().enumerate() {
        let idx = scroll + i;
        let is_selected = idx == selected;
        let prefix = if is_selected { " > " } else { "   " };
        let (style, suffix) = if *is_dir {
            (
                if is_selected {
                    Style::default().fg(Color::Black).bg(Color::Cyan).add_modifier(Modifier::BOLD)
                } else {
                    Style::default().fg(Color::Cyan)
                },
                "/",
            )
        } else {
            (
                if is_selected {
                    Style::default().fg(Color::Black).bg(Color::Cyan).add_modifier(Modifier::BOLD)
                } else {
                    Style::default().fg(Color::White)
                },
                "",
            )
        };
        items.push(ListItem::new(Line::from(Span::styled(
            format!("{}{}{}", prefix, name, suffix),
            style,
        ))));
    }

    let block = Block::default()
        .title(" Load file (.fob / .sub) ")
        .borders(Borders::ALL)
        .border_style(Style::default().fg(Color::Cyan));

    let list = List::new(items).block(block);
    frame.render_widget(list, popup);
}

/// Render the no-device warning (red box at startup when neither HackRF nor RTL-SDR found)
fn render_hackrf_not_detected(frame: &mut Frame, _app: &App) {
    let area = frame.area();
    let popup = centered_rect(56, 8, area);

    frame.render_widget(Clear, popup);

    let red = Color::Red;
    let text = vec![
        Line::from(""),
        Line::from(Span::styled(
            "  No HackRF or RTL-SDR detected.",
            Style::default().fg(red).add_modifier(Modifier::BOLD),
        )),
        Line::from(""),
        Line::from(Span::styled(
            "  Connect a HackRF (TX+RX) or RTL-SDR (RX only) and restart,",
            Style::default().fg(red),
        )),
        Line::from(Span::styled(
            "  or continue without TX/RX support.",
            Style::default().fg(red),
        )),
        Line::from(""),
        Line::from(Span::styled(
            "  Press any key to continue.",
            Style::default().fg(Color::White),
        )),
    ];

    let block = Block::default()
        .title(" Warning ")
        .borders(Borders::ALL)
        .border_style(Style::default().fg(red))
        .style(Style::default().fg(red));

    let paragraph = Paragraph::new(text)
        .block(block)
        .alignment(Alignment::Center)
        .wrap(Wrap { trim: true });

    frame.render_widget(paragraph, popup);
}

/// Render the startup import prompt overlay
fn render_startup_import_prompt(frame: &mut Frame, app: &App) {
    let count = app.pending_fob_files.len();
    let area = frame.area();
    let popup = centered_rect(50, 7, area);

    frame.render_widget(Clear, popup);

    let text = vec![
        Line::from(""),
        Line::from(Span::styled(
            format!(
                "Found {} file(s) (.fob / .sub) in import dir (incl. subfolders).",
                count
            ),
            Style::default().fg(Color::Yellow),
        )),
        Line::from(""),
        Line::from(Span::styled(
            "Import them? (y/n)",
            Style::default()
                .fg(Color::White)
                .add_modifier(Modifier::BOLD),
        )),
    ];

    let block = Block::default()
        .title(" Import Saved Signals ")
        .borders(Borders::ALL)
        .border_style(Style::default().fg(Color::Cyan));

    let paragraph = Paragraph::new(text)
        .block(block)
        .alignment(Alignment::Center)
        .wrap(Wrap { trim: true });

    frame.render_widget(paragraph, popup);
}

/// Render the export form overlay (filename + optional .fob metadata)
fn render_export_form(frame: &mut Frame, app: &App) {
    use crate::app::ExportFormat;

    let is_fob = app.export_format == Some(ExportFormat::Fob);
    let ext = if is_fob { ".fob" } else { ".sub" };

    let area = frame.area();
    // Taller popup for .fob (filename + 5 metadata fields), shorter for .sub (filename only)
    let popup_height = if is_fob { 21 } else { 11 };
    let popup = centered_rect(62, popup_height, area);

    frame.render_widget(Clear, popup);

    let active_style = Style::default()
        .fg(Color::Cyan)
        .add_modifier(Modifier::BOLD);
    let inactive_style = Style::default().fg(Color::DarkGray);
    let done_style = Style::default().fg(Color::Green);
    let value_style = Style::default().fg(Color::White);
    let dim_style = Style::default().fg(Color::DarkGray);
    let accent_style = Style::default().fg(Color::Yellow);
    let cursor = Span::styled(
        "_",
        Style::default()
            .fg(Color::Cyan)
            .add_modifier(Modifier::RAPID_BLINK),
    );

    // Build the ordered list of field modes for this export type
    // Filename is always first; .fob adds metadata fields after
    let field_modes: Vec<InputMode> = if is_fob {
        vec![
            InputMode::ExportFilename,
            InputMode::FobMetaYear,
            InputMode::FobMetaMake,
            InputMode::FobMetaModel,
            InputMode::FobMetaRegion,
            InputMode::FobMetaNotes,
        ]
    } else {
        vec![InputMode::ExportFilename]
    };

    let current_idx = field_modes
        .iter()
        .position(|m| *m == app.input_mode)
        .unwrap_or(0);

    let style_for = |idx: usize| -> Style {
        if idx == current_idx {
            active_style
        } else if idx < current_idx {
            done_style
        } else {
            inactive_style
        }
    };

    let mut lines = Vec::new();

    // --- Signal summary section ---
    if let Some(capture) = app
        .export_capture_id
        .and_then(|id| app.captures.iter().find(|c| c.id == id))
    {
        lines.push(Line::from(vec![
            Span::styled("  Signal:  ", dim_style),
            Span::styled(
                format!(
                    "#{:02} {} | {} | {} | 0x{}",
                    capture.id,
                    capture.protocol_name(),
                    capture.frequency_mhz(),
                    capture.modulation(),
                    capture.serial_hex(),
                ),
                accent_style,
            ),
        ]));
        lines.push(Line::from(vec![
            Span::styled("  Key:     ", dim_style),
            Span::styled(
                format!("0x{} ({})", capture.data_hex(), capture.encryption_type()),
                accent_style,
            ),
        ]));
    }

    lines.push(Line::from(Span::styled(
        "  ──────────────────────────────────────────────────────",
        dim_style,
    )));

    // --- Form fields ---
    struct FormField<'a> {
        label: &'a str,
        value: &'a str,
        placeholder: &'a str,
        idx: usize,
    }

    // Filename field (always present)
    let filename_display = format!("{}{}", app.export_filename, ext);
    let mut fields: Vec<FormField> = vec![
        FormField {
            label: "  File:    ",
            value: &filename_display,
            placeholder: "(enter filename)",
            idx: 0,
        },
    ];

    // .fob metadata fields
    if is_fob {
        fields.extend([
            FormField {
                label: "  Year:    ",
                value: &app.fob_meta_year,
                placeholder: "(e.g. 2024)",
                idx: 1,
            },
            FormField {
                label: "  Make:    ",
                value: &app.fob_meta_make,
                placeholder: "(auto-detected from protocol)",
                idx: 2,
            },
            FormField {
                label: "  Model:   ",
                value: &app.fob_meta_model,
                placeholder: "(e.g. Sportage, F-150)",
                idx: 3,
            },
            FormField {
                label: "  Region:  ",
                value: &app.fob_meta_region,
                placeholder: "(e.g. NA, EU, APAC, MEA)",
                idx: 4,
            },
            FormField {
                label: "  Notes:   ",
                value: &app.fob_meta_notes,
                placeholder: "(optional — color, trim, VIN, etc.)",
                idx: 5,
            },
        ]);
    }

    for field in &fields {
        let label_s = style_for(field.idx);
        let display_val = if field.value.is_empty() {
            field.placeholder
        } else {
            field.value
        };

        let val_s = if field.value.is_empty() && field.idx != current_idx {
            dim_style
        } else {
            value_style
        };

        let mut spans = vec![
            Span::styled(field.label, label_s),
            Span::styled(display_val.to_string(), val_s),
        ];

        // Show cursor on active field
        if field.idx == current_idx {
            spans.push(cursor.clone());
        }

        // Show checkmark for completed fields with values
        if field.idx < current_idx && !field.value.is_empty() {
            spans.push(Span::styled(" ✓", done_style));
        }

        lines.push(Line::from(spans));
    }

    lines.push(Line::from(""));

    // Progress indicator
    let total_fields = fields.len();
    let progress = format!(
        "  Step {}/{}",
        current_idx + 1,
        total_fields,
    );
    let hint = if current_idx == total_fields - 1 {
        "  Enter: Save & Export | Esc: Cancel"
    } else {
        "  Enter: Next | Esc: Cancel"
    };
    lines.push(Line::from(vec![
        Span::styled(progress, accent_style),
        Span::styled("  ", dim_style),
        Span::styled(hint, dim_style),
    ]));

    let title = if is_fob {
        " Export .fob — Filename & Vehicle Details "
    } else {
        " Export .sub (Flipper Zero) "
    };

    let block = Block::default()
        .title(title)
        .borders(Borders::ALL)
        .border_style(Style::default().fg(Color::Green));

    let paragraph = Paragraph::new(lines).block(block);

    frame.render_widget(paragraph, popup);
}

/// Render the capture metadata form (Year/Make/Model/Region for vuln lookup). Shown when user presses 'i' on a capture.
fn render_capture_meta_form(frame: &mut Frame, app: &App) {
    let area = frame.area();
    let popup = centered_rect(62, 18, area);

    frame.render_widget(Clear, popup);

    let active_style = Style::default()
        .fg(Color::Cyan)
        .add_modifier(Modifier::BOLD);
    let inactive_style = Style::default().fg(Color::DarkGray);
    let done_style = Style::default().fg(Color::Green);
    let value_style = Style::default().fg(Color::White);
    let dim_style = Style::default().fg(Color::DarkGray);
    let accent_style = Style::default().fg(Color::Yellow);
    let cursor = Span::styled(
        "_",
        Style::default()
            .fg(Color::Cyan)
            .add_modifier(Modifier::RAPID_BLINK),
    );

    let field_modes = [
        InputMode::CaptureMetaYear,
        InputMode::CaptureMetaMake,
        InputMode::CaptureMetaModel,
        InputMode::CaptureMetaRegion,
    ];
    let current_idx = field_modes
        .iter()
        .position(|m| *m == app.input_mode)
        .unwrap_or(0);

    let style_for = |idx: usize| -> Style {
        if idx == current_idx {
            active_style
        } else if idx < current_idx {
            done_style
        } else {
            inactive_style
        }
    };

    let mut lines = Vec::new();

    if let Some(capture) = app
        .capture_meta_capture_id
        .and_then(|id| app.captures.iter().find(|c| c.id == id))
    {
        lines.push(Line::from(vec![
            Span::styled("  Signal:  ", dim_style),
            Span::styled(
                format!(
                    "#{:02} {} | {} | {} | 0x{}",
                    capture.id,
                    capture.protocol_name(),
                    capture.frequency_mhz(),
                    capture.modulation(),
                    capture.serial_hex(),
                ),
                accent_style,
            ),
        ]));
        lines.push(Line::from(vec![
            Span::styled("  Key:     ", dim_style),
            Span::styled(
                format!("0x{} ({})", capture.data_hex(), capture.encryption_type()),
                accent_style,
            ),
        ]));
    }

    lines.push(Line::from(Span::styled(
        "  ──────────────────────────────────────────────────────",
        dim_style,
    )));

    struct FormField<'a> {
        label: &'a str,
        value: &'a str,
        placeholder: &'a str,
        idx: usize,
    }
    let fields: [FormField; 4] = [
        FormField {
            label: "  Year:    ",
            value: &app.capture_meta_year,
            placeholder: "(e.g. 2021)",
            idx: 0,
        },
        FormField {
            label: "  Make:    ",
            value: &app.capture_meta_make,
            placeholder: "(e.g. Renault, Honda)",
            idx: 1,
        },
        FormField {
            label: "  Model:   ",
            value: &app.capture_meta_model,
            placeholder: "(e.g. ZOE, Civic — or ALL)",
            idx: 2,
        },
        FormField {
            label: "  Region:  ",
            value: &app.capture_meta_region,
            placeholder: "(e.g. NA, EU, or ALL)",
            idx: 3,
        },
    ];

    for field in &fields {
        let label_s = style_for(field.idx);
        let display_val = if field.value.is_empty() {
            field.placeholder
        } else {
            field.value
        };
        let val_s = if field.value.is_empty() && field.idx != current_idx {
            dim_style
        } else {
            value_style
        };
        let mut spans = vec![
            Span::styled(field.label, label_s),
            Span::styled(display_val.to_string(), val_s),
        ];
        if field.idx == current_idx {
            spans.push(cursor.clone());
        }
        if field.idx < current_idx && !field.value.is_empty() {
            spans.push(Span::styled(" ✓", done_style));
        }
        lines.push(Line::from(spans));
    }

    lines.push(Line::from(""));
    let progress = format!("  Step {}/4 — Used for vuln lookup and .fob export", current_idx + 1);
    lines.push(Line::from(Span::styled(progress, dim_style)));

    let block = Block::default()
        .title(" Capture Metadata — Year / Make / Model / Region ")
        .borders(Borders::ALL)
        .border_style(Style::default().fg(Color::Cyan));

    let paragraph = Paragraph::new(lines).block(block);
    frame.render_widget(paragraph, popup);
}
