use crossterm::event::{KeyCode, KeyEvent, KeyModifiers, MouseButton, MouseEvent, MouseEventKind};
use ratatui::layout::Rect;

#[derive(Default)]
pub struct EguiMouseState {
    prev_down: bool,
    prev_col: u16,
    prev_row: u16,
}

pub fn convert_egui_mouse(
    ctx: &egui::Context,
    widget_rect: egui::Rect,
    term: Rect,
    state: &mut EguiMouseState,
) -> Vec<MouseEvent> {
    let mut events = Vec::new();
    if widget_rect.width() < 1.0
        || widget_rect.height() < 1.0
        || term.width == 0
        || term.height == 0
    {
        return events;
    }

    ctx.input(|i| {
        let Some(pos) = i.pointer.latest_pos() else {
            return;
        };
        if !widget_rect.contains(pos) {
            if state.prev_down && !i.pointer.primary_down() {
                events.push(MouseEvent {
                    kind: MouseEventKind::Up(MouseButton::Left),
                    column: state.prev_col,
                    row: state.prev_row,
                    modifiers: KeyModifiers::empty(),
                });
                state.prev_down = false;
            }
            return;
        }

        let col =
            ((pos.x - widget_rect.left()) / widget_rect.width() * term.width as f32) as u16;
        let row =
            ((pos.y - widget_rect.top()) / widget_rect.height() * term.height as f32) as u16;
        let col = col.min(term.width.saturating_sub(1));
        let row = row.min(term.height.saturating_sub(1));

        let is_down = i.pointer.primary_down();
        let moved = col != state.prev_col || row != state.prev_row;

        if !state.prev_down && is_down {
            events.push(MouseEvent {
                kind: MouseEventKind::Down(MouseButton::Left),
                column: col,
                row,
                modifiers: KeyModifiers::empty(),
            });
        } else if state.prev_down && is_down && moved {
            events.push(MouseEvent {
                kind: MouseEventKind::Drag(MouseButton::Left),
                column: col,
                row,
                modifiers: KeyModifiers::empty(),
            });
        } else if state.prev_down && !is_down {
            events.push(MouseEvent {
                kind: MouseEventKind::Up(MouseButton::Left),
                column: col,
                row,
                modifiers: KeyModifiers::empty(),
            });
        } else if !is_down && moved {
            events.push(MouseEvent {
                kind: MouseEventKind::Moved,
                column: col,
                row,
                modifiers: KeyModifiers::empty(),
            });
        }

        state.prev_down = is_down;
        state.prev_col = col;
        state.prev_row = row;

        let scroll = i.raw_scroll_delta.y;
        if scroll > 1.0 {
            events.push(MouseEvent {
                kind: MouseEventKind::ScrollUp,
                column: col,
                row,
                modifiers: KeyModifiers::empty(),
            });
        } else if scroll < -1.0 {
            events.push(MouseEvent {
                kind: MouseEventKind::ScrollDown,
                column: col,
                row,
                modifiers: KeyModifiers::empty(),
            });
        }
    });

    events
}

pub fn convert_egui_events(ctx: &egui::Context) -> Vec<KeyEvent> {
    let mut events = Vec::new();

    for event in &ctx.input(|i| i.events.clone()) {
        convert_event(event, &mut events);
    }

    events
}

fn convert_event(event: &egui::Event, events: &mut Vec<KeyEvent>) {
    match event {
        egui::Event::Key {
            key,
            pressed,
            modifiers,
            ..
        } => {
            if !*pressed {
                return;
            }
            let mods = convert_modifiers(*modifiers);
            // For character keys, only handle Ctrl+key (without Alt) as shortcuts.
            // All other character input (bare, Shift, Alt/Option, AltGr=Ctrl+Alt)
            // defers to Event::Text which respects the active keyboard layout.
            if is_character_key(*key) {
                let ctrl_without_alt =
                    mods.contains(KeyModifiers::CONTROL) && !mods.contains(KeyModifiers::ALT);
                if !ctrl_without_alt {
                    return;
                }
            }
            if let Some(code) = convert_key(*key) {
                events.push(KeyEvent::new(code, mods));
            }
        }
        egui::Event::Text(text) => {
            for c in text.chars() {
                if !c.is_control() {
                    events.push(KeyEvent::new(KeyCode::Char(c), KeyModifiers::empty()));
                }
            }
        }
        // egui intercepts Ctrl+C/V/X and converts them to these high-level events
        // instead of passing through raw Key events (see egui issue #4065).
        // Synthesize the equivalent KeyEvent so the application's input handler receives them.
        egui::Event::Copy => events.push(KeyEvent::new(KeyCode::Char('c'), KeyModifiers::CONTROL)),
        egui::Event::Cut => events.push(KeyEvent::new(KeyCode::Char('x'), KeyModifiers::CONTROL)),
        egui::Event::Paste(_) => {
            events.push(KeyEvent::new(KeyCode::Char('v'), KeyModifiers::CONTROL));
        }
        _ => {}
    }
}

fn convert_key(key: egui::Key) -> Option<KeyCode> {
    Some(match key {
        egui::Key::ArrowDown => KeyCode::Down,
        egui::Key::ArrowLeft => KeyCode::Left,
        egui::Key::ArrowRight => KeyCode::Right,
        egui::Key::ArrowUp => KeyCode::Up,
        egui::Key::Escape => KeyCode::Esc,
        egui::Key::Tab => KeyCode::Tab,
        egui::Key::Backspace => KeyCode::Backspace,
        egui::Key::Enter => KeyCode::Enter,
        egui::Key::Space => KeyCode::Char(' '),
        egui::Key::Insert => KeyCode::Insert,
        egui::Key::Delete => KeyCode::Delete,
        egui::Key::Home => KeyCode::Home,
        egui::Key::End => KeyCode::End,
        egui::Key::PageUp => KeyCode::PageUp,
        egui::Key::PageDown => KeyCode::PageDown,
        egui::Key::F1 => KeyCode::F(1),
        egui::Key::F2 => KeyCode::F(2),
        egui::Key::F3 => KeyCode::F(3),
        egui::Key::F4 => KeyCode::F(4),
        egui::Key::F5 => KeyCode::F(5),
        egui::Key::F6 => KeyCode::F(6),
        egui::Key::F7 => KeyCode::F(7),
        egui::Key::F8 => KeyCode::F(8),
        egui::Key::F9 => KeyCode::F(9),
        egui::Key::F10 => KeyCode::F(10),
        egui::Key::F11 => KeyCode::F(11),
        egui::Key::F12 => KeyCode::F(12),
        egui::Key::F13 => KeyCode::F(13),
        egui::Key::F14 => KeyCode::F(14),
        egui::Key::F15 => KeyCode::F(15),
        egui::Key::F16 => KeyCode::F(16),
        egui::Key::F17 => KeyCode::F(17),
        egui::Key::F18 => KeyCode::F(18),
        egui::Key::F19 => KeyCode::F(19),
        egui::Key::F20 => KeyCode::F(20),
        egui::Key::A => KeyCode::Char('a'),
        egui::Key::B => KeyCode::Char('b'),
        egui::Key::C => KeyCode::Char('c'),
        egui::Key::D => KeyCode::Char('d'),
        egui::Key::E => KeyCode::Char('e'),
        egui::Key::F => KeyCode::Char('f'),
        egui::Key::G => KeyCode::Char('g'),
        egui::Key::H => KeyCode::Char('h'),
        egui::Key::I => KeyCode::Char('i'),
        egui::Key::J => KeyCode::Char('j'),
        egui::Key::K => KeyCode::Char('k'),
        egui::Key::L => KeyCode::Char('l'),
        egui::Key::M => KeyCode::Char('m'),
        egui::Key::N => KeyCode::Char('n'),
        egui::Key::O => KeyCode::Char('o'),
        egui::Key::P => KeyCode::Char('p'),
        egui::Key::Q => KeyCode::Char('q'),
        egui::Key::R => KeyCode::Char('r'),
        egui::Key::S => KeyCode::Char('s'),
        egui::Key::T => KeyCode::Char('t'),
        egui::Key::U => KeyCode::Char('u'),
        egui::Key::V => KeyCode::Char('v'),
        egui::Key::W => KeyCode::Char('w'),
        egui::Key::X => KeyCode::Char('x'),
        egui::Key::Y => KeyCode::Char('y'),
        egui::Key::Z => KeyCode::Char('z'),
        egui::Key::Num0 => KeyCode::Char('0'),
        egui::Key::Num1 => KeyCode::Char('1'),
        egui::Key::Num2 => KeyCode::Char('2'),
        egui::Key::Num3 => KeyCode::Char('3'),
        egui::Key::Num4 => KeyCode::Char('4'),
        egui::Key::Num5 => KeyCode::Char('5'),
        egui::Key::Num6 => KeyCode::Char('6'),
        egui::Key::Num7 => KeyCode::Char('7'),
        egui::Key::Num8 => KeyCode::Char('8'),
        egui::Key::Num9 => KeyCode::Char('9'),
        egui::Key::Minus => KeyCode::Char('-'),
        egui::Key::Equals => KeyCode::Char('='),
        egui::Key::OpenBracket => KeyCode::Char('['),
        egui::Key::CloseBracket => KeyCode::Char(']'),
        egui::Key::Semicolon => KeyCode::Char(';'),
        egui::Key::Comma => KeyCode::Char(','),
        egui::Key::Period => KeyCode::Char('.'),
        egui::Key::Slash => KeyCode::Char('/'),
        egui::Key::Backslash => KeyCode::Char('\\'),
        egui::Key::Backtick => KeyCode::Char('`'),
        egui::Key::Quote => KeyCode::Char('\''),
        egui::Key::Colon => KeyCode::Char(':'),
        egui::Key::Pipe => KeyCode::Char('|'),
        egui::Key::Questionmark => KeyCode::Char('?'),
        egui::Key::Exclamationmark => KeyCode::Char('!'),
        egui::Key::OpenCurlyBracket => KeyCode::Char('{'),
        egui::Key::CloseCurlyBracket => KeyCode::Char('}'),
        egui::Key::Plus => KeyCode::Char('+'),
        _ => return None,
    })
}

fn convert_modifiers(mods: egui::Modifiers) -> KeyModifiers {
    let mut result = KeyModifiers::empty();
    if mods.shift {
        result |= KeyModifiers::SHIFT;
    }
    if mods.ctrl || mods.command {
        result |= KeyModifiers::CONTROL;
    }
    if mods.alt {
        result |= KeyModifiers::ALT;
    }
    result
}

fn is_character_key(key: egui::Key) -> bool {
    matches!(
        key,
        egui::Key::A
            | egui::Key::B
            | egui::Key::C
            | egui::Key::D
            | egui::Key::E
            | egui::Key::F
            | egui::Key::G
            | egui::Key::H
            | egui::Key::I
            | egui::Key::J
            | egui::Key::K
            | egui::Key::L
            | egui::Key::M
            | egui::Key::N
            | egui::Key::O
            | egui::Key::P
            | egui::Key::Q
            | egui::Key::R
            | egui::Key::S
            | egui::Key::T
            | egui::Key::U
            | egui::Key::V
            | egui::Key::W
            | egui::Key::X
            | egui::Key::Y
            | egui::Key::Z
            | egui::Key::Num0
            | egui::Key::Num1
            | egui::Key::Num2
            | egui::Key::Num3
            | egui::Key::Num4
            | egui::Key::Num5
            | egui::Key::Num6
            | egui::Key::Num7
            | egui::Key::Num8
            | egui::Key::Num9
            | egui::Key::Space
            | egui::Key::Minus
            | egui::Key::Equals
            | egui::Key::OpenBracket
            | egui::Key::CloseBracket
            | egui::Key::Semicolon
            | egui::Key::Comma
            | egui::Key::Period
            | egui::Key::Slash
            | egui::Key::Backslash
            | egui::Key::Backtick
            | egui::Key::Quote
            | egui::Key::Colon
            | egui::Key::Pipe
            | egui::Key::Questionmark
            | egui::Key::Exclamationmark
            | egui::Key::OpenCurlyBracket
            | egui::Key::CloseCurlyBracket
            | egui::Key::Plus
    )
}
