use std::io;
use std::time::{Duration, Instant};

use ratatui::crossterm::event::{self, Event, KeyCode, KeyEventKind};
use ratatui::crossterm::terminal::{self, EnterAlternateScreen, LeaveAlternateScreen};
use ratatui::crossterm::{ExecutableCommand, execute};
use ratatui::prelude::*;
use ratatui::widgets::{Block, Paragraph, Wrap};
use tui_overlay::{Anchor, Backdrop, Easing, Overlay, OverlayState, Slide};

const TICK_MS: u64 = 16;

// ---------------------------------------------------------------------------
// Configuration tables
// ---------------------------------------------------------------------------

struct Preset {
    name: &'static str,
    desc: &'static str,
    overlay_title: &'static str,
    anchor: Anchor,
    slide: Option<Slide>,
    width: Constraint,
    height: Constraint,
    backdrop: bool,
}

const PRESETS: &[Preset] = &[
    Preset {
        name: "Side Drawer",
        desc: "Evil plans, filed alphabetically",
        overlay_title: " Evil Plans ",
        anchor: Anchor::Right,
        slide: Some(Slide::Right),
        width: Constraint::Percentage(30),
        height: Constraint::Percentage(100),
        backdrop: true,
    },
    Preset {
        name: "Bottom Drawer",
        desc: "Doom device engineering console",
        overlay_title: " Doom Device Diagnostics ",
        anchor: Anchor::Bottom,
        slide: Some(Slide::Bottom),
        width: Constraint::Percentage(100),
        height: Constraint::Percentage(40),
        backdrop: true,
    },
    Preset {
        name: "Partial Drawer",
        desc: "Minion performance reviews",
        overlay_title: " Minion File ",
        anchor: Anchor::BottomLeft,
        slide: Some(Slide::Bottom),
        width: Constraint::Percentage(60),
        height: Constraint::Percentage(40),
        backdrop: true,
    },
    Preset {
        name: "Modal",
        desc: "HR has concerns about this one",
        overlay_title: " SELF-DESTRUCT ",
        anchor: Anchor::Center,
        slide: None,
        width: Constraint::Percentage(50),
        height: Constraint::Percentage(40),
        backdrop: true,
    },
    Preset {
        name: "Popover",
        desc: "Intel on that meddling hero",
        overlay_title: " Sector Intel ",
        anchor: Anchor::BottomRight,
        slide: Some(Slide::Bottom),
        width: Constraint::Length(40),
        height: Constraint::Length(14),
        backdrop: false,
    },
    Preset {
        name: "Notification",
        desc: "Urgent lair security alert",
        overlay_title: " ALERT ",
        anchor: Anchor::TopRight,
        slide: Some(Slide::Top),
        width: Constraint::Length(44),
        height: Constraint::Length(3),
        backdrop: false,
    },
    Preset {
        name: "Toast",
        desc: "Important lair announcements",
        overlay_title: "",
        anchor: Anchor::BottomRight,
        slide: Some(Slide::Bottom),
        width: Constraint::Length(44),
        height: Constraint::Length(3),
        backdrop: false,
    },
];

const SPEEDS: &[(u64, &str)] = &[
    (0, "Instant"),
    (100, "Fast"),
    (200, "Normal"),
    (400, "Slow"),
    (800, "Glacial"),
];

const ANCHORS: &[(Anchor, &str)] = &[
    (Anchor::TopLeft, "TopLeft"),
    (Anchor::Top, "Top"),
    (Anchor::TopRight, "TopRight"),
    (Anchor::Left, "Left"),
    (Anchor::Center, "Center"),
    (Anchor::Right, "Right"),
    (Anchor::BottomLeft, "BottomLeft"),
    (Anchor::Bottom, "Bottom"),
    (Anchor::BottomRight, "BottomRight"),
];

const SLIDES: &[(Option<Slide>, &str)] = &[
    (None, "None"),
    (Some(Slide::Left), "Left"),
    (Some(Slide::Right), "Right"),
    (Some(Slide::Top), "Top"),
    (Some(Slide::Bottom), "Bottom"),
];

const EASINGS: &[(Easing, &str)] = &[
    (Easing::Linear, "Linear"),
    (Easing::EaseIn, "EaseIn"),
    (Easing::EaseOut, "EaseOut"),
    (Easing::EaseInOut, "EaseInOut"),
];

// ---------------------------------------------------------------------------
// App state
// ---------------------------------------------------------------------------

struct App {
    state: OverlayState,
    preset_idx: usize,
    speed_idx: usize,
    easing_idx: usize,
    anchor_idx: usize,
    slide_idx: usize,
    backdrop_on: bool,
    width: Constraint,
    height: Constraint,
}

impl App {
    fn new() -> Self {
        let mut app = Self {
            state: Self::build_state(2, 2),
            preset_idx: 0,
            speed_idx: 2,
            easing_idx: 2,
            anchor_idx: 0,
            slide_idx: 0,
            backdrop_on: true,
            width: Constraint::Percentage(30),
            height: Constraint::Percentage(100),
        };

        app.apply_preset();
        app
    }

    fn build_state(speed_idx: usize, easing_idx: usize) -> OverlayState {
        OverlayState::new()
            .with_duration(Duration::from_millis(SPEEDS[speed_idx].0))
            .with_easing(EASINGS[easing_idx].0)
    }

    fn rebuild_state(&mut self) {
        let was_visible = self.state.is_open() || self.state.is_animating();

        self.state = Self::build_state(self.speed_idx, self.easing_idx);

        if was_visible {
            self.state.open();
            self.state.tick(Duration::from_secs(1));
        }
    }

    fn apply_preset(&mut self) {
        let p = &PRESETS[self.preset_idx];

        self.anchor_idx = ANCHORS
            .iter()
            .position(|&(a, _)| a == p.anchor)
            .unwrap_or(0);

        self.slide_idx = SLIDES.iter().position(|&(s, _)| s == p.slide).unwrap_or(0);

        self.width = p.width;
        self.height = p.height;
        self.backdrop_on = p.backdrop;
    }

    fn constraint_label(c: Constraint) -> String {
        match c {
            Constraint::Percentage(p) => format!("{p}%"),
            Constraint::Length(l) => format!("{l}"),
            _ => format!("{c:?}"),
        }
    }
}

fn adjust_constraint(c: Constraint, delta: i16) -> Constraint {
    match c {
        Constraint::Percentage(p) => {
            Constraint::Percentage((p as i16 + delta).clamp(10, 100) as u16)
        }
        Constraint::Length(l) => Constraint::Length((l as i16 + delta).clamp(3, 200) as u16),
        other => other,
    }
}

// ---------------------------------------------------------------------------
// Main loop
// ---------------------------------------------------------------------------

fn main() -> io::Result<()> {
    terminal::enable_raw_mode()?;
    io::stdout().execute(EnterAlternateScreen)?;
    let mut terminal = Terminal::new(CrosstermBackend::new(io::stdout()))?;

    let result = run(&mut terminal);

    terminal::disable_raw_mode()?;
    execute!(io::stdout(), LeaveAlternateScreen)?;
    result
}

fn run(terminal: &mut Terminal<CrosstermBackend<io::Stdout>>) -> io::Result<()> {
    let mut app = App::new();
    let mut last_tick = Instant::now();

    loop {
        terminal.draw(|frame| draw(frame, &mut app))?;

        if event::poll(Duration::from_millis(TICK_MS))?
            && let Event::Key(key) = event::read()?
            && key.kind == KeyEventKind::Press
        {
            match key.code {
                KeyCode::Char('q') | KeyCode::Esc => return Ok(()),
                KeyCode::Char(' ') => app.state.toggle(),

                KeyCode::Char('p') => {
                    app.preset_idx = (app.preset_idx + 1) % PRESETS.len();
                    app.apply_preset();
                }

                KeyCode::Char('d') => {
                    app.speed_idx = (app.speed_idx + 1) % SPEEDS.len();
                    app.rebuild_state();
                }

                KeyCode::Char('e') => {
                    app.easing_idx = (app.easing_idx + 1) % EASINGS.len();
                    app.rebuild_state();
                }

                KeyCode::Char('a') => {
                    app.anchor_idx = (app.anchor_idx + 1) % ANCHORS.len();
                }

                KeyCode::Char('s') => {
                    app.slide_idx = (app.slide_idx + 1) % SLIDES.len();
                }

                KeyCode::Char('b') => app.backdrop_on = !app.backdrop_on,
                KeyCode::Right => app.width = adjust_constraint(app.width, 5),
                KeyCode::Left => app.width = adjust_constraint(app.width, -5),
                KeyCode::Up => app.height = adjust_constraint(app.height, 5),
                KeyCode::Down => app.height = adjust_constraint(app.height, -5),
                _ => {}
            }
        }

        let now = Instant::now();
        app.state.tick(now.duration_since(last_tick));
        last_tick = now;
    }
}

// ---------------------------------------------------------------------------
// Drawing
// ---------------------------------------------------------------------------

fn draw(frame: &mut ratatui::Frame, app: &mut App) {
    let [main_area, status_area] =
        Layout::vertical([Constraint::Min(0), Constraint::Length(1)]).areas(frame.area());

    render_dashboard(frame, main_area, app);

    let preset = &PRESETS[app.preset_idx];

    let mut block = Block::bordered().border_style(Style::new().fg(Color::Cyan));

    if !preset.overlay_title.is_empty() {
        block = block.title(preset.overlay_title);
    }

    let mut overlay = Overlay::new()
        .anchor(ANCHORS[app.anchor_idx].0)
        .width(app.width)
        .height(app.height)
        .block(block);

    if let Some(slide) = SLIDES[app.slide_idx].0 {
        overlay = overlay.slide(slide);
    }

    if app.backdrop_on {
        overlay = overlay.backdrop(Backdrop::new(Color::Rgb(0, 0, 0)).fg(Color::Rgb(60, 60, 60)));
    }

    frame.render_stateful_widget(overlay, main_area, &mut app.state);

    if let Some(inner) = app.state.inner_area() {
        render_overlay_content(frame, inner, app.preset_idx);
    }

    render_status_bar(frame, status_area, app);
}

// ---------------------------------------------------------------------------
// Dashboard — Villain Lair Control Panel
// ---------------------------------------------------------------------------

fn render_dashboard(frame: &mut ratatui::Frame, area: Rect, app: &App) {
    let [top, bottom] =
        Layout::vertical([Constraint::Percentage(50), Constraint::Percentage(50)]).areas(area);

    let [tl, tr] =
        Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]).areas(top);

    let [bl, br] =
        Layout::horizontal([Constraint::Percentage(50), Constraint::Percentage(50)]).areas(bottom);

    frame.render_widget(doom_device_panel(), tl);
    frame.render_widget(incident_log_panel(), tr);
    frame.render_widget(minion_roster_panel(), bl);
    frame.render_widget(config_panel(app), br);
}

fn panel_block(title: &str) -> Block<'_> {
    Block::bordered()
        .title(format!(" {title} "))
        .border_style(Style::new().fg(Color::DarkGray))
}

fn doom_device_panel() -> Paragraph<'static> {
    let bar = |label: &'static str, color: Color, fill: usize, pct: &'static str| {
        let filled: String = "█".repeat(fill);
        let empty: String = "░".repeat(10 - fill);
        Line::from(vec![
            Span::styled(format!("{label:<14}"), Style::new().fg(color)),
            Span::styled(filled, Style::new().fg(color)),
            Span::styled(format!("{empty} "), Style::new().fg(color)),
            Span::styled(pct, Style::new().fg(Color::White).bold()),
        ])
    };

    Paragraph::new(vec![
        bar("Laser Charge", Color::Red, 8, "78%"),
        bar("Shield Gen", Color::Cyan, 6, "61%"),
        bar("Trap Coverage", Color::Yellow, 3, "32%"),
        bar("Evil WiFi", Color::Magenta, 9, "92%"),
        bar("Monologue Prep", Color::Green, 10, "READY"),
    ])
    .block(panel_block("Doom Device"))
}

fn incident_log_panel() -> Paragraph<'static> {
    Paragraph::new(vec![
        Line::styled(
            "[ALERT] Hero spotted near air vent",
            Style::new().fg(Color::Red),
        ),
        Line::styled(
            "[INFO]  Shark tank feeding complete",
            Style::new().fg(Color::Green),
        ),
        Line::styled(
            "[WARN]  Lava moat temp below spec",
            Style::new().fg(Color::Yellow),
        ),
        Line::styled(
            "[INFO]  Minion #47 reported for duty",
            Style::new().fg(Color::Green),
        ),
        Line::styled(
            "[ERROR] Rotating chair stuck again",
            Style::new().fg(Color::Red),
        ),
        Line::styled(
            "[INFO]  Monologue rehearsal at 3pm",
            Style::new().fg(Color::Green),
        ),
        Line::styled(
            "[WARN]  Escape pod inventory low",
            Style::new().fg(Color::Yellow),
        ),
        Line::styled(
            "[INFO]  Cat successfully petted",
            Style::new().fg(Color::Green),
        ),
    ])
    .block(panel_block("Incident Log"))
}

fn minion_roster_panel() -> Paragraph<'static> {
    let minion = |name: &'static str, status: &'static str, color: Color| {
        Line::from(vec![
            Span::styled("● ", Style::new().fg(color)),
            Span::raw(format!("{name:<17}")),
            Span::styled(status, Style::new().fg(color)),
        ])
    };

    Paragraph::new(vec![
        minion("Igor", "on duty", Color::Green),
        minion("Henchbot-3000", "on duty", Color::Green),
        minion("Karen (HR)", "on break", Color::Yellow),
        minion("Sir Clanks-a-Lot", "repairs", Color::Yellow),
        minion("The Intern", "missing", Color::Red),
    ])
    .block(panel_block("Minion Roster"))
}

fn config_panel(app: &App) -> Paragraph<'_> {
    let preset = &PRESETS[app.preset_idx];
    let label = Style::new().fg(Color::DarkGray);
    let value = Style::new().fg(Color::White);
    let highlight = Style::new().fg(Color::Cyan).bold();

    let row = |l: &'static str, v: String| {
        Line::from(vec![
            Span::styled(format!("{l:<10}"), label),
            Span::styled(v, value),
        ])
    };

    let bd = if app.backdrop_on { "on" } else { "off" };
    let speed = SPEEDS[app.speed_idx].1;
    let ms = SPEEDS[app.speed_idx].0;

    Paragraph::new(vec![
        Line::from(Span::styled(preset.name, highlight)),
        Line::from(Span::styled(preset.desc, label)),
        Line::raw(""),
        row("Anchor", ANCHORS[app.anchor_idx].1.to_string()),
        row("Slide", SLIDES[app.slide_idx].1.to_string()),
        row("Width", App::constraint_label(app.width)),
        row("Height", App::constraint_label(app.height)),
        row("Backdrop", bd.to_string()),
        row("Speed", format!("{speed} ({ms}ms)")),
        row("Easing", EASINGS[app.easing_idx].1.to_string()),
    ])
    .block(panel_block("Overlay Config"))
}

// ---------------------------------------------------------------------------
// Overlay body content — varies by preset to showcase each pattern
// ---------------------------------------------------------------------------

fn render_overlay_content(frame: &mut ratatui::Frame, area: Rect, preset_idx: usize) {
    let content = match preset_idx {
        0 => overlay_evil_plans(),
        1 => overlay_diagnostics(),
        2 => overlay_minion_file(),
        3 => overlay_self_destruct(),
        4 => overlay_sector_intel(),
        5 => overlay_alert(),
        6 => overlay_toast(),
        _ => overlay_evil_plans(),
    };

    frame.render_widget(content.wrap(Wrap { trim: false }), area);
}

fn overlay_evil_plans() -> Paragraph<'static> {
    let dim = Style::new().fg(Color::DarkGray);

    Paragraph::new(vec![
        Line::styled("Operation Moonbeam", Style::new().fg(Color::Cyan).bold()),
        Line::styled("Phase 3 of 7", dim),
        Line::raw(""),
        Line::from(vec![
            Span::styled("Objective: ", dim),
            Span::raw("Relocate the moon"),
        ]),
        Line::from(vec![
            Span::styled("Budget:    ", dim),
            Span::raw("$47 billion"),
        ]),
        Line::from(vec![
            Span::styled("Timeline:  ", dim),
            Span::raw("Thursday"),
        ]),
        Line::from(vec![
            Span::styled("Status:    ", dim),
            Span::styled("Behind schedule", Style::new().fg(Color::Yellow)),
        ]),
        Line::raw(""),
        Line::styled("Checklist:", Style::new().fg(Color::White).bold()),
        Line::styled("  [x] Build giant magnet", Style::new().fg(Color::Green)),
        Line::styled("  [x] Recruit moon experts", Style::new().fg(Color::Green)),
        Line::styled("  [ ] Find extension cord", Style::new().fg(Color::White)),
        Line::styled("  [ ] Actually move moon", Style::new().fg(Color::White)),
        Line::styled("  [ ] Gloat", Style::new().fg(Color::White)),
    ])
}

fn overlay_diagnostics() -> Paragraph<'static> {
    let dim = Style::new().fg(Color::DarkGray);

    Paragraph::new(vec![
        Line::styled(
            "=== DOOM DEVICE DIAGNOSTICS ===",
            Style::new().fg(Color::Red).bold(),
        ),
        Line::raw(""),
        Line::from(vec![
            Span::styled("Core temp:   ", dim),
            Span::styled("2,847 C", Style::new().fg(Color::Red)),
            Span::styled(" (nominal)", dim),
        ]),
        Line::from(vec![
            Span::styled("Power draw:  ", dim),
            Span::raw("1.21 GW"),
        ]),
        Line::from(vec![
            Span::styled("Coolant:     ", dim),
            Span::styled("87%", Style::new().fg(Color::Yellow)),
        ]),
        Line::from(vec![
            Span::styled("Last maint:  ", dim),
            Span::styled("\"never lol\"", Style::new().fg(Color::Red)),
        ]),
        Line::raw(""),
        Line::styled(
            "WARNING: Do not press the big red button.",
            Style::new().fg(Color::Yellow),
        ),
        Line::styled(
            "The last intern who did is now working",
            Style::new().fg(Color::DarkGray),
        ),
        Line::styled(
            "in the shark tank division.",
            Style::new().fg(Color::DarkGray),
        ),
    ])
}

fn overlay_minion_file() -> Paragraph<'static> {
    let dim = Style::new().fg(Color::DarkGray);

    Paragraph::new(vec![
        Line::styled("MINION #47 — IGOR", Style::new().fg(Color::Cyan).bold()),
        Line::raw(""),
        Line::from(vec![
            Span::styled("Role:        ", dim),
            Span::raw("Henchperson (Senior)"),
        ]),
        Line::from(vec![
            Span::styled("Tenure:      ", dim),
            Span::raw("14 years"),
        ]),
        Line::from(vec![
            Span::styled("Loyalty:     ", dim),
            Span::styled("Unquestionable", Style::new().fg(Color::Green)),
        ]),
        Line::from(vec![
            Span::styled("Skill:       ", dim),
            Span::raw("Dramatic lightning operation"),
        ]),
        Line::from(vec![
            Span::styled("Weakness:    ", dim),
            Span::raw("Compliments"),
        ]),
        Line::raw(""),
        Line::styled("Performance Notes:", Style::new().fg(Color::White).bold()),
        Line::styled("  Excellent maniacal laugh support", dim),
        Line::styled("  Needs work on trap maintenance", dim),
        Line::styled("  Voted \"Most Evil\" 3 years running", dim),
    ])
}

fn overlay_self_destruct() -> Paragraph<'static> {
    Paragraph::new(vec![
        Line::raw(""),
        Line::styled(
            "  INITIATE SELF-DESTRUCT?",
            Style::new().fg(Color::Red).bold(),
        ),
        Line::raw(""),
        Line::styled(
            "  Are you SURE you want to blow up",
            Style::new().fg(Color::White),
        ),
        Line::styled("  your own lair? Again??", Style::new().fg(Color::White)),
        Line::raw(""),
        Line::styled(
            "  This is the third lair this quarter.",
            Style::new().fg(Color::DarkGray),
        ),
        Line::styled("  HR has concerns.", Style::new().fg(Color::Yellow)),
    ])
}

fn overlay_sector_intel() -> Paragraph<'static> {
    let dim = Style::new().fg(Color::DarkGray);

    Paragraph::new(vec![
        Line::styled("Sector 7-G Intel", Style::new().fg(Color::Cyan).bold()),
        Line::raw(""),
        Line::from(vec![
            Span::styled("Sighting:  ", dim),
            Span::styled("2 min ago", Style::new().fg(Color::Yellow)),
        ]),
        Line::from(vec![
            Span::styled("Threat:    ", dim),
            Span::styled("Moderate", Style::new().fg(Color::Yellow)),
        ]),
        Line::from(vec![
            Span::styled("Wearing:   ", dim),
            Span::raw("Cape (tacky)"),
        ]),
        Line::from(vec![Span::styled("Weakness:  ", dim), Span::raw("TBD")]),
        Line::from(vec![
            Span::styled("Last quip: ", dim),
            Span::styled("\"Not so fast!\"", Style::new().fg(Color::White).italic()),
        ]),
    ])
}

fn overlay_alert() -> Paragraph<'static> {
    Paragraph::new(vec![Line::from(vec![
        Span::styled(" !! ", Style::new().fg(Color::Red).bold()),
        Span::styled(
            "Hero approaching main entrance",
            Style::new().fg(Color::White),
        ),
    ])])
}

fn overlay_toast() -> Paragraph<'static> {
    Paragraph::new(vec![Line::styled(
        " Lava moat is NOT a hot tub. --Mgmt",
        Style::new().fg(Color::Yellow),
    )])
}

// ---------------------------------------------------------------------------
// Status bar
// ---------------------------------------------------------------------------

fn render_status_bar(frame: &mut ratatui::Frame, area: Rect, app: &App) {
    let key = Style::new().fg(Color::Yellow).bold();
    let dim = Style::new().fg(Color::DarkGray);
    let sep = Style::new().fg(Color::Rgb(50, 50, 50));

    let phase = if app.state.is_open() {
        Span::styled("open", Style::new().fg(Color::Green))
    } else if app.state.is_closed() {
        Span::styled("closed", Style::new().fg(Color::Red))
    } else {
        Span::styled("animating", Style::new().fg(Color::Yellow))
    };

    let hint = |k: &'static str, label: &str| -> Vec<Span<'static>> {
        vec![
            Span::styled("[", sep),
            Span::styled(k, key),
            Span::styled("] ", sep),
            Span::styled(label.to_string(), dim),
            Span::styled("  ", sep),
        ]
    };

    let mut spans = vec![Span::raw(" ")];
    spans.extend(hint("space", "toggle"));
    spans.extend(hint("p", "preset"));
    spans.extend(hint("d", "speed"));
    spans.extend(hint("e", "easing"));
    spans.extend(hint("a", "anchor"));
    spans.extend(hint("s", "slide"));
    spans.extend(hint("b", "backdrop"));
    spans.extend(hint("←→↑↓", "size"));
    spans.push(phase);

    frame.render_widget(Paragraph::new(Line::from(spans)), area);
}
