use ratatui::layout::Rect;
use ratatui::style::{Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, Borders, Paragraph};
use ratatui::Frame;

use crate::app::App;
use crate::engine::LinkState;
use crate::midi;
use crate::state::OptionsFocus;
use crate::theme::{self, ThemeColors};
use crate::widgets::{render_scroll_indicators, IndicatorAlign};

pub fn render(frame: &mut Frame, app: &App, link: &LinkState, area: Rect) {
    let theme = theme::get();

    let block = Block::default()
        .borders(Borders::ALL)
        .title(" Options ")
        .border_style(Style::new().fg(theme.modal.input));

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

    let padded = Rect {
        x: inner.x + 2,
        y: inner.y + 1,
        width: inner.width.saturating_sub(4),
        height: inner.height.saturating_sub(2),
    };

    let focus = app.options.focus;
    let content_width = padded.width as usize;

    let onboarding_str = format!("{}/6 dismissed", app.ui.onboarding_dismissed.len());
    let hue_str = format!("{}°", app.ui.hue_rotation as i32);

    let mut lines: Vec<Line> = vec![
        render_section_header("DISPLAY", &theme),
        render_divider(content_width, &theme),
        render_option_line(
            "Theme",
            app.ui.color_scheme.label(),
            focus == OptionsFocus::ColorScheme,
            &theme,
        ),
        render_option_line(
            "Hue rotation",
            &hue_str,
            focus == OptionsFocus::HueRotation,
            &theme,
        ),
        render_option_line(
            "Refresh rate",
            app.audio.config.refresh_rate.label(),
            focus == OptionsFocus::RefreshRate,
            &theme,
        ),
        render_option_line(
            "Runtime highlight",
            if app.ui.runtime_highlight { "On" } else { "Off" },
            focus == OptionsFocus::RuntimeHighlight,
            &theme,
        ),
        render_option_line(
            "Show scope",
            if app.audio.config.show_scope { "On" } else { "Off" },
            focus == OptionsFocus::ShowScope,
            &theme,
        ),
        render_option_line(
            "Show spectrum",
            if app.audio.config.show_spectrum {
                "On"
            } else {
                "Off"
            },
            focus == OptionsFocus::ShowSpectrum,
            &theme,
        ),
        render_option_line(
            "Show lissajous",
            if app.audio.config.show_lissajous {
                "On"
            } else {
                "Off"
            },
            focus == OptionsFocus::ShowLissajous,
            &theme,
        ),
        render_option_line(
            "Gain boost",
            &gain_boost_label(app.audio.config.gain_boost),
            focus == OptionsFocus::GainBoost,
            &theme,
        ),
        render_option_line(
            "Normalize",
            if app.audio.config.normalize_viz { "On" } else { "Off" },
            focus == OptionsFocus::NormalizeViz,
            &theme,
        ),
        render_option_line(
            "Completion",
            if app.ui.show_completion { "On" } else { "Off" },
            focus == OptionsFocus::ShowCompletion,
            &theme,
        ),
        render_option_line(
            "Show preview",
            if app.audio.config.show_preview { "On" } else { "Off" },
            focus == OptionsFocus::ShowPreview,
            &theme,
        ),
        render_option_line(
            "Performance mode",
            if app.ui.performance_mode { "On" } else { "Off" },
            focus == OptionsFocus::PerformanceMode,
            &theme,
        ),
    ];
    if app.plugin_mode {
        let zoom_str = format!("{:.0}%", app.ui.zoom_factor * 100.0);
        let window_str = format!("{}x{}", app.ui.window_width, app.ui.window_height);
        lines.push(render_option_line(
            "Font",
            &app.ui.font,
            focus == OptionsFocus::Font,
            &theme,
        ));
        lines.push(render_option_line(
            "Zoom",
            &zoom_str,
            focus == OptionsFocus::ZoomFactor,
            &theme,
        ));
        lines.push(render_option_line(
            "Window",
            &window_str,
            focus == OptionsFocus::WindowSize,
            &theme,
        ));
    }
    if !app.plugin_mode {
        let enabled = link.is_enabled();
        let peers = link.peers();
        let (status_text, status_color) = if !enabled {
            ("DISABLED", theme.link_status.disabled)
        } else if peers > 0 {
            ("CONNECTED", theme.link_status.connected)
        } else {
            ("LISTENING", theme.link_status.listening)
        };
        let peer_text = if enabled && peers > 0 {
            if peers == 1 {
                " · 1 peer".to_string()
            } else {
                format!(" · {peers} peers")
            }
        } else {
            String::new()
        };
        let link_header = Line::from(vec![
            Span::styled(
                "ABLETON LINK",
                Style::new().fg(theme.ui.header).add_modifier(Modifier::BOLD),
            ),
            Span::raw("  "),
            Span::styled(
                status_text,
                Style::new().fg(status_color).add_modifier(Modifier::BOLD),
            ),
            Span::styled(peer_text, Style::new().fg(theme.ui.text_muted)),
        ]);
        let quantum_str = format!("{:.0}", link.quantum());
        let tempo_str = format!("{:.1} BPM", link.tempo());
        let beat_str = format!("{:.2}", link.beat());
        let phase_str = format!("{:.2}", link.phase());
        let tempo_style = Style::new().fg(theme.values.tempo).add_modifier(Modifier::BOLD);
        let value_style = Style::new().fg(theme.values.value);

        lines.push(Line::from(""));
        lines.extend([
            link_header,
            render_divider(content_width, &theme),
            render_option_line(
                "Enabled",
                if link.is_enabled() { "On" } else { "Off" },
                focus == OptionsFocus::LinkEnabled,
                &theme,
            ),
            render_option_line(
                "Start/Stop sync",
                if link.is_start_stop_sync_enabled() {
                    "On"
                } else {
                    "Off"
                },
                focus == OptionsFocus::StartStopSync,
                &theme,
            ),
            render_option_line("Quantum", &quantum_str, focus == OptionsFocus::Quantum, &theme),
            Line::from(""),
            render_section_header("SESSION", &theme),
            render_divider(content_width, &theme),
            render_readonly_line("Tempo", &tempo_str, tempo_style, &theme),
            render_readonly_line("Beat", &beat_str, value_style, &theme),
            render_readonly_line("Phase", &phase_str, value_style, &theme),
        ]);

        let midi_outputs = midi::list_midi_outputs();
        let midi_inputs = midi::list_midi_inputs();
        let midi_out_display = |slot: usize| -> String {
            if let Some(idx) = app.midi.selected_outputs[slot] {
                midi_outputs
                    .get(idx)
                    .map(|d| d.name.clone())
                    .unwrap_or_else(|| "(disconnected)".to_string())
            } else if midi_outputs.is_empty() {
                "(none found)".to_string()
            } else {
                "(not connected)".to_string()
            }
        };
        let midi_in_display = |slot: usize| -> String {
            if let Some(idx) = app.midi.selected_inputs[slot] {
                midi_inputs
                    .get(idx)
                    .map(|d| d.name.clone())
                    .unwrap_or_else(|| "(disconnected)".to_string())
            } else if midi_inputs.is_empty() {
                "(none found)".to_string()
            } else {
                "(not connected)".to_string()
            }
        };
        let midi_out_0 = midi_out_display(0);
        let midi_out_1 = midi_out_display(1);
        let midi_out_2 = midi_out_display(2);
        let midi_out_3 = midi_out_display(3);
        let midi_in_0 = midi_in_display(0);
        let midi_in_1 = midi_in_display(1);
        let midi_in_2 = midi_in_display(2);
        let midi_in_3 = midi_in_display(3);

        lines.push(Line::from(""));
        lines.extend([
            render_section_header("MIDI OUTPUTS", &theme),
            render_divider(content_width, &theme),
            render_option_line("Output 0", &midi_out_0, focus == OptionsFocus::MidiOutput0, &theme),
            render_option_line("Output 1", &midi_out_1, focus == OptionsFocus::MidiOutput1, &theme),
            render_option_line("Output 2", &midi_out_2, focus == OptionsFocus::MidiOutput2, &theme),
            render_option_line("Output 3", &midi_out_3, focus == OptionsFocus::MidiOutput3, &theme),
            Line::from(""),
            render_section_header("MIDI INPUTS", &theme),
            render_divider(content_width, &theme),
            render_option_line("Input 0", &midi_in_0, focus == OptionsFocus::MidiInput0, &theme),
            render_option_line("Input 1", &midi_in_1, focus == OptionsFocus::MidiInput1, &theme),
            render_option_line("Input 2", &midi_in_2, focus == OptionsFocus::MidiInput2, &theme),
            render_option_line("Input 3", &midi_in_3, focus == OptionsFocus::MidiInput3, &theme),
        ]);
    }
    if !app.plugin_mode {
        lines.push(Line::from(""));
        lines.extend([
            render_section_header("ONBOARDING", &theme),
            render_divider(content_width, &theme),
            render_option_line(
                "Reset guides",
                &onboarding_str,
                focus == OptionsFocus::ResetOnboarding,
                &theme,
            ),
            render_option_line(
                "Demo on startup",
                if app.ui.load_demo_on_startup { "On" } else { "Off" },
                focus == OptionsFocus::LoadDemoOnStartup,
                &theme,
            ),
        ]);
    }

    // Insert description below focused option
    let focus_vec_idx = focus.line_index(app.plugin_mode);
    if let Some(desc) = option_description(focus) {
        if focus_vec_idx < lines.len() {
            lines.insert(focus_vec_idx + 1, render_description_line(desc, &theme));
        }
    }

    let total_lines = lines.len();
    let max_visible = padded.height as usize;

    let focus_line = focus.line_index(app.plugin_mode);

    let scroll_offset = if total_lines <= max_visible {
        0
    } else {
        focus_line
            .saturating_sub(max_visible / 2)
            .min(total_lines.saturating_sub(max_visible))
    };

    let visible_end = (scroll_offset + max_visible).min(total_lines);
    let visible_lines: Vec<Line> = lines
        .into_iter()
        .skip(scroll_offset)
        .take(visible_end - scroll_offset)
        .collect();

    frame.render_widget(Paragraph::new(visible_lines), padded);

    render_scroll_indicators(
        frame,
        padded,
        scroll_offset,
        visible_end - scroll_offset,
        total_lines,
        theme.ui.text_dim,
        IndicatorAlign::Right,
    );
}

fn render_section_header(title: &str, theme: &theme::ThemeColors) -> Line<'static> {
    Line::from(Span::styled(
        title.to_string(),
        Style::new().fg(theme.ui.header).add_modifier(Modifier::BOLD),
    ))
}

fn render_divider(width: usize, theme: &theme::ThemeColors) -> Line<'static> {
    Line::from(Span::styled(
        "─".repeat(width),
        Style::new().fg(theme.ui.border),
    ))
}

fn render_option_line(label: &str, value: &str, focused: bool, theme: &theme::ThemeColors) -> Line<'static> {
    let highlight = Style::new().fg(theme.hint.key).add_modifier(Modifier::BOLD);
    let normal = Style::new().fg(theme.ui.text_primary);
    let label_style = Style::new().fg(theme.ui.text_muted);

    let prefix = if focused { "> " } else { "  " };
    let prefix_style = if focused { highlight } else { normal };

    let value_display = if focused {
        format!("< {value} >")
    } else {
        format!("  {value}  ")
    };
    let value_style = if focused { highlight } else { normal };

    let label_width = 20;
    let padded_label = format!("{label:<label_width$}");

    Line::from(vec![
        Span::styled(prefix.to_string(), prefix_style),
        Span::styled(padded_label, label_style),
        Span::styled(value_display, value_style),
    ])
}

fn option_description(focus: OptionsFocus) -> Option<&'static str> {
    match focus {
        OptionsFocus::ColorScheme => Some("Color scheme for the entire interface"),
        OptionsFocus::HueRotation => Some("Shift all theme colors by a hue angle"),
        OptionsFocus::RefreshRate => Some("Lower values reduce CPU usage"),
        OptionsFocus::RuntimeHighlight => Some("Highlight executed code spans during playback"),
        OptionsFocus::ShowScope => Some("Oscilloscope on the main view"),
        OptionsFocus::ShowSpectrum => Some("Spectrum analyzer on the main view"),
        OptionsFocus::ShowLissajous => Some("XY stereo phase scope (left vs right)"),
        OptionsFocus::GainBoost => Some("Amplify scope and lissajous waveforms"),
        OptionsFocus::NormalizeViz => Some("Auto-scale visualizations to fill the display"),
        OptionsFocus::ShowCompletion => Some("Word completion popup in the editor"),
        OptionsFocus::ShowPreview => Some("Step script preview on the sequencer grid"),
        OptionsFocus::PerformanceMode => Some("Hide header and footer bars"),
        OptionsFocus::Font => Some("Bitmap font for the plugin window"),
        OptionsFocus::ZoomFactor => Some("Scale factor for the plugin window"),
        OptionsFocus::WindowSize => Some("Default size for the plugin window"),
        OptionsFocus::LinkEnabled => Some("Join an Ableton Link session on the local network"),
        OptionsFocus::StartStopSync => Some("Sync transport start/stop with other Link peers"),
        OptionsFocus::Quantum => Some("Number of beats per phase cycle"),
        OptionsFocus::MidiOutput0 => Some("MIDI output device for channel group 1"),
        OptionsFocus::MidiOutput1 => Some("MIDI output device for channel group 2"),
        OptionsFocus::MidiOutput2 => Some("MIDI output device for channel group 3"),
        OptionsFocus::MidiOutput3 => Some("MIDI output device for channel group 4"),
        OptionsFocus::MidiInput0 => Some("MIDI input device for channel group 1"),
        OptionsFocus::MidiInput1 => Some("MIDI input device for channel group 2"),
        OptionsFocus::MidiInput2 => Some("MIDI input device for channel group 3"),
        OptionsFocus::MidiInput3 => Some("MIDI input device for channel group 4"),
        OptionsFocus::ResetOnboarding => Some("Re-enable all dismissed guide popups"),
        OptionsFocus::LoadDemoOnStartup => Some("Load a rotating demo song on fresh startup"),
    }
}

fn render_description_line(desc: &str, theme: &ThemeColors) -> Line<'static> {
    Line::from(Span::styled(
        format!("  {desc}"),
        Style::new().fg(theme.ui.text_dim),
    ))
}

fn gain_boost_label(gain: f32) -> String {
    format!("{:.0}x", gain)
}

fn render_readonly_line(label: &str, value: &str, value_style: Style, theme: &theme::ThemeColors) -> Line<'static> {
    let label_style = Style::new().fg(theme.ui.text_muted);
    let label_width = 20;
    let padded_label = format!("{label:<label_width$}");

    Line::from(vec![
        Span::styled("  ".to_string(), Style::new()),
        Span::styled(padded_label, label_style),
        Span::styled(format!("  {value}"), value_style),
    ])
}
