use crossterm::event::{self, KeyCode};
use ratatui::layout::{Constraint, Layout, Rect};
use ratatui::style::{Modifier, Style, Stylize};
use ratatui::text::{Line, Span};
use ratatui::widgets::{HighlightSpacing, List, ListItem, ListState, Row, Table, TableState};
use ratatui::Frame;
use std::io::{self, IsTerminal, Write};
use std::panic::{self, AssertUnwindSafe};

pub struct TerminalGuard;

impl Drop for TerminalGuard {
    fn drop(&mut self) {
        ratatui::restore();
    }
}

pub fn select_list(items: &[&str]) -> Option<usize> {
    if items.is_empty() {
        return None;
    }

    if !io::stdout().is_terminal() {
        return select_list_text(items);
    }

    let _guard = TerminalGuard;
    let result = panic::catch_unwind(AssertUnwindSafe(|| {
        ratatui::run::<_, Result<_, color_eyre::Report>>(|terminal| {
            let mut state = ListState::default().with_selected(Some(0));
            loop {
                terminal.draw(|frame| render_list(frame, items, &mut state, "Select"))?;

                if let Some(key) = event::read()?.as_key_press_event() {
                    match key.code {
                        KeyCode::Char('j') | KeyCode::Down => state.select_next(),
                        KeyCode::Char('k') | KeyCode::Up => state.select_previous(),
                        KeyCode::Char('g') | KeyCode::Home => state.select_first(),
                        KeyCode::Char('G') | KeyCode::End => state.select_last(),
                        KeyCode::Enter | KeyCode::Char('y') | KeyCode::Char('Y') => {
                            if let Some(idx) = state.selected() {
                                return Ok(Some(idx));
                            }
                        }
                        KeyCode::Char('q') | KeyCode::Esc => return Ok(None),
                        _ => {}
                    }
                }
            }
        })
    }));

    match result {
        Ok(Ok(opt)) => opt,
        _ => None,
    }
}

fn select_list_text(items: &[&str]) -> Option<usize> {
    let stdout = io::stdout();
    let mut handle = stdout.lock();

    writeln!(handle, "Select from the following:").ok();
    for (i, item) in items.iter().enumerate() {
        writeln!(handle, "  {}) {}", i + 1, item).ok();
    }
    write!(handle, "Enter choice (1-{}) or q to quit: ", items.len()).ok();
    handle.flush().ok();

    let mut input = String::new();
    io::stdin().read_line(&mut input).ok();

    let trimmed = input.trim();
    if trimmed == "q" || trimmed.is_empty() {
        return None;
    }

    match trimmed.parse::<usize>() {
        Ok(n) if n > 0 && n <= items.len() => Some(n - 1),
        _ => select_list_text(items),
    }
}

fn render_list(frame: &mut Frame, items: &[&str], state: &mut ListState, label: &str) {
    let constraints = [
        Constraint::Length(1),
        Constraint::Fill(1),
        Constraint::Length(1),
    ];
    let layout = Layout::vertical(constraints).spacing(1);
    let areas: [Rect; 3] = layout.areas(frame.area());
    let [header_area, list_area, footer_area] = areas;

    let title = Line::from_iter([
        Span::from(format!("Select {}", label)).bold(),
        Span::from(" (j/k navigate, Enter confirm, Esc cancel)"),
    ]);
    frame.render_widget(title.centered(), header_area);

    let list_items: Vec<ListItem> = items.iter().map(|item| ListItem::new(*item)).collect();
    let list = List::new(list_items)
        .highlight_style(Style::default().add_modifier(Modifier::REVERSED))
        .highlight_symbol("> ")
        .highlight_spacing(HighlightSpacing::Always);

    frame.render_stateful_widget(list, list_area, state);

    let footer = Line::from("Press Enter to select, q/Esc to cancel");
    frame.render_widget(footer.centered(), footer_area);
}

pub fn select_table(headers: &[&str], rows: Vec<Vec<String>>) -> Option<usize> {
    if rows.is_empty() {
        return None;
    }

    if !io::stdout().is_terminal() {
        return select_table_text(headers, rows);
    }

    let _guard = TerminalGuard;
    let result = panic::catch_unwind(AssertUnwindSafe(|| {
        ratatui::run::<_, Result<_, color_eyre::Report>>(|terminal| {
            let mut state = TableState::default().with_selected(Some(0));
            loop {
                terminal.draw(|frame| render_table(frame, headers, &rows, &mut state))?;

                if let Some(key) = event::read()?.as_key_press_event() {
                    match key.code {
                        KeyCode::Char('j') | KeyCode::Down => state.select_next(),
                        KeyCode::Char('k') | KeyCode::Up => state.select_previous(),
                        KeyCode::Enter | KeyCode::Char('y') | KeyCode::Char('Y') => {
                            if let Some(idx) = state.selected() {
                                return Ok(Some(idx));
                            }
                        }
                        KeyCode::Char('q') | KeyCode::Esc => return Ok(None),
                        _ => {}
                    }
                }
            }
        })
    }));

    match result {
        Ok(Ok(opt)) => opt,
        _ => None,
    }
}

fn select_table_text(headers: &[&str], rows: Vec<Vec<String>>) -> Option<usize> {
    let stdout = io::stdout();
    let mut handle = stdout.lock();

    let header_line: String = headers.iter().map(|h| format!("{:<20}", h)).collect();
    writeln!(handle, "{}", header_line).ok();
    writeln!(handle, "{}", "-".repeat(header_line.len())).ok();

    for (i, row) in rows.iter().enumerate() {
        let line: String = row.iter().map(|c| format!("{:<20}", c)).collect();
        writeln!(handle, "  {}) {}", i + 1, line).ok();
    }

    write!(handle, "Enter choice (1-{}) or q to quit: ", rows.len()).ok();
    handle.flush().ok();

    let mut input = String::new();
    io::stdin().read_line(&mut input).ok();

    let trimmed = input.trim();
    if trimmed == "q" || trimmed.is_empty() {
        return None;
    }

    match trimmed.parse::<usize>() {
        Ok(n) if n > 0 && n <= rows.len() => Some(n - 1),
        _ => select_table_text(headers, rows),
    }
}

fn render_table(frame: &mut Frame, headers: &[&str], rows: &[Vec<String>], state: &mut TableState) {
    let constraints = [
        Constraint::Length(1),
        Constraint::Fill(1),
        Constraint::Length(1),
    ];
    let layout = Layout::vertical(constraints).spacing(1);
    let areas: [Rect; 3] = layout.areas(frame.area());
    let [header_area, table_area, footer_area] = areas;

    let title = Line::from_iter([
        Span::from("Select Entry").bold(),
        Span::from(" (j/k navigate, Enter confirm, Esc cancel)"),
    ]);
    frame.render_widget(title.centered(), header_area);

    let widths: Vec<Constraint> = headers.iter().map(|_| Constraint::Length(20)).collect();
    let table_rows: Vec<Row> = rows
        .iter()
        .map(|row| Row::new(row.iter().map(|s| Line::from(s.as_str()))))
        .collect();

    let table = Table::new(table_rows, widths)
        .header(Row::new(headers.iter().map(|h| Line::from(*h))))
        .row_highlight_style(Style::default().add_modifier(Modifier::REVERSED))
        .highlight_symbol("> ")
        .highlight_spacing(HighlightSpacing::Always);

    frame.render_stateful_widget(table, table_area, state);

    let footer = Line::from("Press Enter to select, q/Esc to cancel");
    frame.render_widget(footer.centered(), footer_area);
}

pub struct ProvisionOption<'a> {
    pub label: &'a str,
    pub description: &'a str,
}

pub fn select_provision_profile(items: &[ProvisionOption]) -> Option<usize> {
    if items.is_empty() {
        return None;
    }

    if !io::stdout().is_terminal() {
        return select_provision_profile_text(items);
    }

    let _guard = TerminalGuard;
    let result = panic::catch_unwind(AssertUnwindSafe(|| {
        ratatui::run::<_, Result<_, color_eyre::Report>>(|terminal| {
            let mut state = ListState::default().with_selected(Some(0));
            loop {
                terminal.draw(|frame| render_provision_list(frame, items, &mut state))?;

                if let Some(key) = event::read()?.as_key_press_event() {
                    match key.code {
                        KeyCode::Char('j') | KeyCode::Down => state.select_next(),
                        KeyCode::Char('k') | KeyCode::Up => state.select_previous(),
                        KeyCode::Enter | KeyCode::Char('y') | KeyCode::Char('Y') => {
                            if let Some(idx) = state.selected() {
                                return Ok(Some(idx));
                            }
                        }
                        KeyCode::Char('q') | KeyCode::Esc => return Ok(None),
                        _ => {}
                    }
                }
            }
        })
    }));

    match result {
        Ok(Ok(opt)) => opt,
        _ => None,
    }
}

fn select_provision_profile_text(items: &[ProvisionOption]) -> Option<usize> {
    let stdout = io::stdout();
    let mut handle = stdout.lock();

    writeln!(handle, "Select provision profile:").ok();
    for (i, item) in items.iter().enumerate() {
        writeln!(handle, "  {}) {} — {}", i + 1, item.label, item.description).ok();
    }
    write!(handle, "Enter choice (1-{}) or q to quit: ", items.len()).ok();
    handle.flush().ok();

    let mut input = String::new();
    io::stdin().read_line(&mut input).ok();

    let trimmed = input.trim();
    if trimmed == "q" || trimmed.is_empty() {
        return None;
    }

    match trimmed.parse::<usize>() {
        Ok(n) if n > 0 && n <= items.len() => Some(n - 1),
        _ => select_provision_profile_text(items),
    }
}

fn render_provision_list(frame: &mut Frame, items: &[ProvisionOption], state: &mut ListState) {
    let constraints = [
        Constraint::Length(1),
        Constraint::Length(1),
        Constraint::Fill(1),
        Constraint::Length(2),
    ];
    let layout = Layout::vertical(constraints).spacing(1);
    let areas: [Rect; 4] = layout.areas(frame.area());
    let [header_area, desc_area, list_area, footer_area] = areas;

    let title = Line::from("Select Provision Profile").bold();
    frame.render_widget(title.centered(), header_area);

    if let Some(idx) = state.selected() {
        let desc = Line::from(items[idx].description).style(Style::default().dim());
        frame.render_widget(desc, desc_area);
    }

    let list_items: Vec<ListItem> = items
        .iter()
        .map(|item| ListItem::new(item.label.to_string()))
        .collect();
    let list = List::new(list_items)
        .highlight_style(Style::default().add_modifier(Modifier::REVERSED))
        .highlight_symbol("> ")
        .highlight_spacing(HighlightSpacing::Always);

    frame.render_stateful_widget(list, list_area, state);

    let footer = Line::from("Enter to select, q/Esc to cancel").centered();
    frame.render_widget(footer, footer_area);
}
