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

use crate::input::{Key, Modifiers};
use crate::theme::Theme;

/// Convert a key event into an [`InputFieldOp`], if the key maps to a
/// single-line text-editing action.
///
/// This is a convenience function that de-duplicates the `key_to_filter_op` /
/// `key_to_input_op` helpers that were previously copy-pasted across views.
pub fn key_to_op(key: crate::input::KeyEvent) -> Option<InputFieldOp> {
    match (key.modifiers, key.key) {
        (m, Key::Char(c)) if !m.contains(Modifiers::CTRL) && !m.contains(Modifiers::ALT) => {
            Some(InputFieldOp::InsertChar(c))
        }
        (_, Key::Backspace) => Some(InputFieldOp::Backspace),
        (_, Key::Delete) => Some(InputFieldOp::DeleteForward),
        (_, Key::ArrowLeft) => Some(InputFieldOp::CursorLeft),
        (_, Key::ArrowRight) => Some(InputFieldOp::CursorRight),
        (_, Key::Home) => Some(InputFieldOp::CursorHome),
        (_, Key::End) => Some(InputFieldOp::CursorEnd),
        _ => None,
    }
}

/// A single-line text input with byte-offset cursor.
///
/// Replaces the ad-hoc `TextField` in `SearchReplaceView`, the `filter: String`
/// pattern in `FileSelector`/`CommandRunner`/`LogView`, and the raw `String`
/// inputs in `CommitWindow`.
#[derive(Debug, Default, Clone)]
pub struct InputField {
    text: String,
    /// Byte offset of the cursor within `text`.
    cursor: usize,
    /// Label shown before the input (e.g. "Filter:", "Query:").
    label: String,
}

/// Operations that can be applied to an `InputField`.
#[derive(Debug, Clone)]
pub enum InputFieldOp {
    InsertChar(char),
    Backspace,
    DeleteForward,
    CursorLeft,
    CursorRight,
    CursorHome,
    CursorEnd,
    /// Replace entire text (used by async results or programmatic resets).
    SetText(String),
}

impl InputField {
    pub fn new(label: impl Into<String>) -> Self {
        Self {
            text: String::new(),
            cursor: 0,
            label: label.into(),
        }
    }

    pub fn with_text(mut self, text: impl Into<String>) -> Self {
        self.text = text.into();
        self.cursor = self.text.len();
        self
    }

    pub fn text(&self) -> &str {
        &self.text
    }

    pub fn cursor(&self) -> usize {
        self.cursor
    }

    pub fn label(&self) -> &str {
        &self.label
    }

    pub fn set_label(&mut self, label: impl Into<String>) {
        self.label = label.into();
    }

    pub fn is_empty(&self) -> bool {
        self.text.is_empty()
    }

    // --- Mutations ---------------------------------------------------------

    pub fn insert_char(&mut self, c: char) {
        self.text.insert(self.cursor, c);
        self.cursor += c.len_utf8();
    }

    pub fn backspace(&mut self) {
        if self.cursor == 0 {
            return;
        }
        let prev = self.text[..self.cursor]
            .char_indices()
            .next_back()
            .map(|(i, _)| i)
            .unwrap_or(0);
        self.text.drain(prev..self.cursor);
        self.cursor = prev;
    }

    pub fn delete_forward(&mut self) {
        if self.cursor >= self.text.len() {
            return;
        }
        let next = self.text[self.cursor..]
            .char_indices()
            .nth(1)
            .map(|(i, _)| self.cursor + i)
            .unwrap_or(self.text.len());
        self.text.drain(self.cursor..next);
    }

    pub fn cursor_left(&mut self) {
        if self.cursor == 0 {
            return;
        }
        self.cursor = self.text[..self.cursor]
            .char_indices()
            .next_back()
            .map(|(i, _)| i)
            .unwrap_or(0);
    }

    pub fn cursor_right(&mut self) {
        if self.cursor >= self.text.len() {
            return;
        }
        self.cursor = self.text[self.cursor..]
            .char_indices()
            .nth(1)
            .map(|(i, _)| self.cursor + i)
            .unwrap_or(self.text.len());
    }

    pub fn cursor_home(&mut self) {
        self.cursor = 0;
    }

    pub fn cursor_end(&mut self) {
        self.cursor = self.text.len();
    }

    pub fn set_text(&mut self, text: String) {
        self.text = text;
        self.cursor = self.text.len();
    }

    /// Apply an `InputFieldOp` to this field.
    pub fn apply(&mut self, op: &InputFieldOp) {
        match op {
            InputFieldOp::InsertChar(c) => self.insert_char(*c),
            InputFieldOp::Backspace => self.backspace(),
            InputFieldOp::DeleteForward => self.delete_forward(),
            InputFieldOp::CursorLeft => self.cursor_left(),
            InputFieldOp::CursorRight => self.cursor_right(),
            InputFieldOp::CursorHome => self.cursor_home(),
            InputFieldOp::CursorEnd => self.cursor_end(),
            InputFieldOp::SetText(t) => self.set_text(t.clone()),
        }
    }

    // --- Rendering ---------------------------------------------------------

    /// Render the input field into the given area.
    pub fn render(&self, frame: &mut Frame, area: Rect, focused: bool, theme: &Theme) {
        if area.height == 0 || area.width == 0 {
            return;
        }

        let bg = if focused {
            theme.bg_active()
        } else {
            theme.bg_inactive()
        };
        let fg = if focused {
            theme.fg_active()
        } else {
            theme.fg_dim()
        };
        let accent = theme.accent();

        let mut spans = Vec::new();

        // Label
        if !self.label.is_empty() {
            spans.push(Span::styled(
                format!("  {}: ", self.label),
                Style::new().fg(accent).add_modifier(Modifier::BOLD),
            ));
        }

        if focused {
            // Split text at cursor and show block cursor
            let (before, after) = self.text.split_at(self.cursor);
            spans.push(Span::styled(before.to_string(), Style::new().fg(fg).bg(bg)));

            // Cursor character (or space if at end)
            let cursor_char = after.chars().next().unwrap_or(' ');
            spans.push(Span::styled(
                cursor_char.to_string(),
                Style::new()
                    .fg(Color::Black)
                    .bg(Color::White)
                    .add_modifier(Modifier::BOLD),
            ));

            // Rest after cursor
            if !after.is_empty() {
                let rest_start = cursor_char.len_utf8();
                if rest_start < after.len() {
                    spans.push(Span::styled(
                        after[rest_start..].to_string(),
                        Style::new().fg(fg).bg(bg),
                    ));
                }
            }
        } else {
            spans.push(Span::styled(
                self.text.clone(),
                Style::new().fg(fg).bg(bg),
            ));
        }

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

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_new_empty() {
        let field = InputField::new("Filter");
        assert_eq!(field.text(), "");
        assert_eq!(field.cursor(), 0);
        assert_eq!(field.label(), "Filter");
        assert!(field.is_empty());
    }

    #[test]
    fn test_with_text() {
        let field = InputField::new("").with_text("hello");
        assert_eq!(field.text(), "hello");
        assert_eq!(field.cursor(), 5);
    }

    #[test]
    fn test_insert_char() {
        let mut field = InputField::new("");
        field.insert_char('a');
        field.insert_char('b');
        assert_eq!(field.text(), "ab");
        assert_eq!(field.cursor(), 2);
    }

    #[test]
    fn test_insert_char_middle() {
        let mut field = InputField::new("").with_text("ac");
        field.cursor_left();
        field.insert_char('b');
        assert_eq!(field.text(), "abc");
        assert_eq!(field.cursor(), 2);
    }

    #[test]
    fn test_backspace() {
        let mut field = InputField::new("").with_text("abc");
        field.backspace();
        assert_eq!(field.text(), "ab");
        assert_eq!(field.cursor(), 2);
    }

    #[test]
    fn test_backspace_at_start() {
        let mut field = InputField::new("").with_text("abc");
        field.cursor_home();
        field.backspace();
        assert_eq!(field.text(), "abc");
        assert_eq!(field.cursor(), 0);
    }

    #[test]
    fn test_backspace_empty() {
        let mut field = InputField::new("");
        field.backspace();
        assert_eq!(field.text(), "");
        assert_eq!(field.cursor(), 0);
    }

    #[test]
    fn test_delete_forward() {
        let mut field = InputField::new("").with_text("abc");
        field.cursor_home();
        field.delete_forward();
        assert_eq!(field.text(), "bc");
        assert_eq!(field.cursor(), 0);
    }

    #[test]
    fn test_delete_forward_at_end() {
        let mut field = InputField::new("").with_text("abc");
        field.delete_forward();
        assert_eq!(field.text(), "abc");
        assert_eq!(field.cursor(), 3);
    }

    #[test]
    fn test_cursor_left_right() {
        let mut field = InputField::new("").with_text("abc");
        assert_eq!(field.cursor(), 3);
        field.cursor_left();
        assert_eq!(field.cursor(), 2);
        field.cursor_left();
        assert_eq!(field.cursor(), 1);
        field.cursor_right();
        assert_eq!(field.cursor(), 2);
    }

    #[test]
    fn test_cursor_left_at_start() {
        let mut field = InputField::new("").with_text("a");
        field.cursor_home();
        field.cursor_left();
        assert_eq!(field.cursor(), 0);
    }

    #[test]
    fn test_cursor_right_at_end() {
        let mut field = InputField::new("").with_text("a");
        field.cursor_right();
        assert_eq!(field.cursor(), 1);
    }

    #[test]
    fn test_cursor_home_end() {
        let mut field = InputField::new("").with_text("hello");
        field.cursor_home();
        assert_eq!(field.cursor(), 0);
        field.cursor_end();
        assert_eq!(field.cursor(), 5);
    }

    #[test]
    fn test_set_text() {
        let mut field = InputField::new("");
        field.set_text("new value".to_string());
        assert_eq!(field.text(), "new value");
        assert_eq!(field.cursor(), 9);
    }

    #[test]
    fn test_utf8_multibyte() {
        let mut field = InputField::new("");
        field.insert_char('é');
        field.insert_char('ñ');
        assert_eq!(field.text(), "éñ");
        // é is 2 bytes, ñ is 2 bytes
        assert_eq!(field.cursor(), 4);
        field.cursor_left();
        assert_eq!(field.cursor(), 2);
        field.backspace();
        assert_eq!(field.text(), "ñ");
        assert_eq!(field.cursor(), 0);
    }

    #[test]
    fn test_utf8_emoji() {
        let mut field = InputField::new("");
        field.insert_char('🎉');
        assert_eq!(field.text(), "🎉");
        assert_eq!(field.cursor(), 4); // emoji is 4 bytes
        field.backspace();
        assert_eq!(field.text(), "");
        assert_eq!(field.cursor(), 0);
    }

    #[test]
    fn test_apply_op() {
        let mut field = InputField::new("");
        field.apply(&InputFieldOp::InsertChar('h'));
        field.apply(&InputFieldOp::InsertChar('i'));
        assert_eq!(field.text(), "hi");
        field.apply(&InputFieldOp::CursorLeft);
        field.apply(&InputFieldOp::Backspace);
        assert_eq!(field.text(), "i");
        field.apply(&InputFieldOp::CursorHome);
        field.apply(&InputFieldOp::DeleteForward);
        assert_eq!(field.text(), "");
    }

    #[test]
    fn test_apply_set_text() {
        let mut field = InputField::new("");
        field.apply(&InputFieldOp::SetText("replaced".to_string()));
        assert_eq!(field.text(), "replaced");
        assert_eq!(field.cursor(), 8);
    }

    #[test]
    fn test_delete_forward_middle() {
        let mut field = InputField::new("").with_text("abc");
        field.cursor_home();
        field.cursor_right();
        field.delete_forward();
        assert_eq!(field.text(), "ac");
        assert_eq!(field.cursor(), 1);
    }

    #[test]
    fn test_backspace_middle() {
        let mut field = InputField::new("").with_text("abc");
        field.cursor_left();
        field.backspace();
        assert_eq!(field.text(), "ac");
        assert_eq!(field.cursor(), 1);
    }
}
