//! Built-in text editor using ratatui
//!
//! A simple but functional text editor for editing page files directly within the CLI tool.
//! Supports basic text editing operations, syntax highlighting for Rust and TOML, and file saving.

use anyhow::{Context, Result};
use crossterm::event::{self, Event, KeyCode, KeyModifiers};
use ratatui::{
    layout::{Constraint, Direction as LayoutDirection, Layout, Rect},
    style::{Color, Modifier, Style},
    text::{Line, Span},
    widgets::{Block, Borders, Clear, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState},
    Frame,
};
use std::collections::HashMap;
use std::fs;
use std::path::PathBuf;
use std::time::Duration;

use crate::ui::SafeTerminal;

#[derive(Debug, Clone)]
pub struct EditorState {
    pub content: Vec<String>,
    pub cursor_row: usize,
    pub cursor_col: usize,
    pub scroll_offset: usize,
    pub file_path: PathBuf,
    pub modified: bool,
    pub show_help: bool,
    pub status_message: Option<String>,
    pub file_type: FileType,
}

#[derive(Debug, Clone, PartialEq)]
pub enum FileType {
    Rust,
    Toml,
    Ftl,
    Text,
}

impl FileType {
    pub fn from_path(path: &PathBuf) -> Self {
        match path.extension().and_then(|s| s.to_str()) {
            Some("rs") => FileType::Rust,
            Some("toml") => FileType::Toml,
            Some("ftl") => FileType::Ftl,
            _ => FileType::Text,
        }
    }

    pub fn name(&self) -> &'static str {
        match self {
            FileType::Rust => "Rust",
            FileType::Toml => "TOML",
            FileType::Ftl => "Fluent",
            FileType::Text => "Text",
        }
    }
}

impl EditorState {
    pub fn new(file_path: PathBuf) -> Result<Self> {
        let content = if file_path.exists() {
            fs::read_to_string(&file_path)
                .with_context(|| format!("Failed to read file: {}", file_path.display()))?
                .lines()
                .map(String::from)
                .collect()
        } else {
            vec![String::new()]
        };

        let file_type = FileType::from_path(&file_path);

        Ok(EditorState {
            content,
            cursor_row: 0,
            cursor_col: 0,
            scroll_offset: 0,
            file_path,
            modified: false,
            show_help: false,
            status_message: None,
            file_type,
        })
    }

    pub fn save(&mut self) -> Result<()> {
        let content_str = self.content.join("\n");
        fs::write(&self.file_path, content_str)
            .with_context(|| format!("Failed to write file: {}", self.file_path.display()))?;

        self.modified = false;
        self.status_message = Some(format!("Saved: {}", self.file_path.display()));
        Ok(())
    }

    pub fn insert_char(&mut self, c: char) {
        if self.cursor_row >= self.content.len() {
            self.content.push(String::new());
        }

        let line = &mut self.content[self.cursor_row];
        if self.cursor_col >= line.len() {
            line.push(c);
        } else {
            line.insert(self.cursor_col, c);
        }

        self.cursor_col += 1;
        self.modified = true;
    }

    pub fn insert_newline(&mut self) {
        if self.cursor_row >= self.content.len() {
            self.content.push(String::new());
        }

        let current_line = self.content[self.cursor_row].clone();
        let (before, after) = current_line.split_at(self.cursor_col);

        self.content[self.cursor_row] = before.to_string();
        self.content.insert(self.cursor_row + 1, after.to_string());

        self.cursor_row += 1;
        self.cursor_col = 0;
        self.modified = true;
    }

    pub fn delete_char(&mut self) {
        if self.cursor_col > 0 {
            // Delete character before cursor
            let line = &mut self.content[self.cursor_row];
            if self.cursor_col <= line.len() {
                line.remove(self.cursor_col - 1);
                self.cursor_col -= 1;
                self.modified = true;
            }
        } else if self.cursor_row > 0 {
            // Delete newline - merge with previous line
            let current_line = self.content.remove(self.cursor_row);
            self.cursor_row -= 1;
            self.cursor_col = self.content[self.cursor_row].len();
            self.content[self.cursor_row].push_str(&current_line);
            self.modified = true;
        }
    }

    pub fn delete_forward(&mut self) {
        if self.cursor_row < self.content.len() {
            let line = &mut self.content[self.cursor_row];
            if self.cursor_col < line.len() {
                line.remove(self.cursor_col);
                self.modified = true;
            } else if self.cursor_row < self.content.len() - 1 {
                // Delete newline - merge with next line
                let next_line = self.content.remove(self.cursor_row + 1);
                self.content[self.cursor_row].push_str(&next_line);
                self.modified = true;
            }
        }
    }

    pub fn move_cursor(&mut self, direction: CursorDirection, terminal_height: u16) {
        match direction {
            CursorDirection::Up => {
                if self.cursor_row > 0 {
                    self.cursor_row -= 1;
                    self.adjust_cursor_col();
                    self.adjust_scroll(terminal_height);
                }
            }
            CursorDirection::Down => {
                if self.cursor_row < self.content.len() - 1 {
                    self.cursor_row += 1;
                    self.adjust_cursor_col();
                    self.adjust_scroll(terminal_height);
                }
            }
            CursorDirection::Left => {
                if self.cursor_col > 0 {
                    self.cursor_col -= 1;
                } else if self.cursor_row > 0 {
                    self.cursor_row -= 1;
                    self.cursor_col = self.content[self.cursor_row].len();
                    self.adjust_scroll(terminal_height);
                }
            }
            CursorDirection::Right => {
                let line_len = if self.cursor_row < self.content.len() {
                    self.content[self.cursor_row].len()
                } else {
                    0
                };

                if self.cursor_col < line_len {
                    self.cursor_col += 1;
                } else if self.cursor_row < self.content.len() - 1 {
                    self.cursor_row += 1;
                    self.cursor_col = 0;
                    self.adjust_scroll(terminal_height);
                }
            }
        }
    }

    fn adjust_cursor_col(&mut self) {
        if self.cursor_row < self.content.len() {
            let line_len = self.content[self.cursor_row].len();
            if self.cursor_col > line_len {
                self.cursor_col = line_len;
            }
        }
    }

    fn adjust_scroll(&mut self, terminal_height: u16) {
        let visible_lines = terminal_height.saturating_sub(4) as usize; // Account for borders and status

        if self.cursor_row < self.scroll_offset {
            self.scroll_offset = self.cursor_row;
        } else if self.cursor_row >= self.scroll_offset + visible_lines {
            self.scroll_offset = self.cursor_row.saturating_sub(visible_lines - 1);
        }
    }

    pub fn toggle_help(&mut self) {
        self.show_help = !self.show_help;
    }

    pub fn clear_status(&mut self) {
        self.status_message = None;
    }
}

#[derive(Debug, Clone, Copy)]
pub enum CursorDirection {
    Up,
    Down,
    Left,
    Right,
}

/// Run the built-in text editor
pub fn run_builtin_editor(file_path: PathBuf, translations: HashMap<String, String>) -> Result<()> {
    let mut editor_state = EditorState::new(file_path)?;
    let mut terminal = SafeTerminal::new()
        .context("Failed to setup terminal for editor")?;

    // Main editor loop
    loop {
        // Render editor UI
        terminal.draw(|frame| render_editor_ui(frame, &editor_state, &translations))
            .context("Failed to render editor UI")?;

        // Handle events
        let terminal_size = terminal.size()?;
        let terminal_rect = Rect::new(0, 0, terminal_size.width, terminal_size.height);
        if handle_editor_events(&mut editor_state, &terminal_rect)? {
            break; // Exit requested
        }
    }

    Ok(())
}

/// Handle keyboard input for the editor
fn handle_editor_events(editor: &mut EditorState, terminal_size: &Rect) -> Result<bool> {
    if event::poll(Duration::from_millis(50))? {
        if let Event::Key(key) = event::read()? {
            editor.clear_status(); // Clear status message on any key press

            match key.code {
                KeyCode::Char('q') if key.modifiers.contains(KeyModifiers::CONTROL) => {
                    if editor.modified {
                        editor.status_message = Some("File has unsaved changes! Use Ctrl+S to save or Ctrl+Q twice to quit".to_string());
                        return Ok(false);
                    }
                    return Ok(true); // Exit
                }
                KeyCode::Char('s') if key.modifiers.contains(KeyModifiers::CONTROL) => {
                    if let Err(e) = editor.save() {
                        editor.status_message = Some(format!("Save failed: {}", e));
                    }
                }
                KeyCode::Char('h') if key.modifiers.contains(KeyModifiers::CONTROL) => {
                    editor.toggle_help();
                }
                KeyCode::Esc => {
                    if editor.show_help {
                        editor.toggle_help();
                    }
                }
                KeyCode::Up => {
                    editor.move_cursor(CursorDirection::Up, terminal_size.height);
                }
                KeyCode::Down => {
                    editor.move_cursor(CursorDirection::Down, terminal_size.height);
                }
                KeyCode::Left => {
                    editor.move_cursor(CursorDirection::Left, terminal_size.height);
                }
                KeyCode::Right => {
                    editor.move_cursor(CursorDirection::Right, terminal_size.height);
                }
                KeyCode::Enter => {
                    if !editor.show_help {
                        editor.insert_newline();
                    }
                }
                KeyCode::Backspace => {
                    if !editor.show_help {
                        editor.delete_char();
                    }
                }
                KeyCode::Delete => {
                    if !editor.show_help {
                        editor.delete_forward();
                    }
                }
                KeyCode::Home => {
                    editor.cursor_col = 0;
                }
                KeyCode::End => {
                    if editor.cursor_row < editor.content.len() {
                        editor.cursor_col = editor.content[editor.cursor_row].len();
                    }
                }
                KeyCode::PageUp => {
                    let page_size = terminal_size.height.saturating_sub(4) as usize;
                    editor.cursor_row = editor.cursor_row.saturating_sub(page_size);
                    editor.adjust_cursor_col();
                    editor.adjust_scroll(terminal_size.height);
                }
                KeyCode::PageDown => {
                    let page_size = terminal_size.height.saturating_sub(4) as usize;
                    editor.cursor_row = (editor.cursor_row + page_size).min(editor.content.len().saturating_sub(1));
                    editor.adjust_cursor_col();
                    editor.adjust_scroll(terminal_size.height);
                }
                KeyCode::Char(c) => {
                    if !editor.show_help && !key.modifiers.contains(KeyModifiers::CONTROL) {
                        editor.insert_char(c);
                    }
                }
                _ => {}
            }
        }
    }

    Ok(false)
}

/// Render the editor UI
fn render_editor_ui(frame: &mut Frame, editor: &EditorState, translations: &HashMap<String, String>) {
    let chunks = Layout::default()
        .direction(LayoutDirection::Vertical)
        .constraints([
            Constraint::Length(1), // Title bar
            Constraint::Min(0),    // Editor area
            Constraint::Length(1), // Status bar
        ])
        .split(frame.area());

    // Title bar
    let title = format!("📝 Rustelo Editor - {} ({})",
        editor.file_path.file_name().unwrap_or_default().to_string_lossy(),
        editor.file_type.name());
    let title_paragraph = Paragraph::new(title)
        .style(Style::default().bg(Color::Blue).fg(Color::White));
    frame.render_widget(title_paragraph, chunks[0]);

    // Editor content area
    if editor.show_help {
        render_help_overlay(frame, chunks[1], translations);
    } else {
        render_editor_content(frame, chunks[1], editor);
    }

    // Status bar
    let status_text = if let Some(msg) = &editor.status_message {
        msg.clone()
    } else {
        format!("Row {}, Col {} | {} | Ctrl+S: Save | Ctrl+H: Help | Ctrl+Q: Quit{}",
            editor.cursor_row + 1,
            editor.cursor_col + 1,
            if editor.modified { "Modified" } else { "Saved" },
            if editor.modified { " (unsaved)" } else { "" })
    };

    let status_style = if editor.modified {
        Style::default().bg(Color::Yellow).fg(Color::Black)
    } else {
        Style::default().bg(Color::Green).fg(Color::Black)
    };

    let status_paragraph = Paragraph::new(status_text).style(status_style);
    frame.render_widget(status_paragraph, chunks[2]);
}

/// Render editor content with syntax highlighting
fn render_editor_content(frame: &mut Frame, area: Rect, editor: &EditorState) {
    let visible_lines = area.height.saturating_sub(2) as usize;
    let end_line = (editor.scroll_offset + visible_lines).min(editor.content.len());

    let mut lines = Vec::new();
    for (i, line) in editor.content[editor.scroll_offset..end_line].iter().enumerate() {
        let line_num = editor.scroll_offset + i;
        let is_cursor_line = line_num == editor.cursor_row;

        let line_content = if is_cursor_line {
            // Highlight current line
            apply_syntax_highlighting(line, &editor.file_type, true)
        } else {
            apply_syntax_highlighting(line, &editor.file_type, false)
        };

        // Add line number
        let line_num_str = format!("{:4} ", line_num + 1);
        let mut spans = vec![Span::styled(line_num_str, Style::default().fg(Color::DarkGray))];
        spans.extend(line_content);

        lines.push(Line::from(spans));
    }

    let paragraph = Paragraph::new(lines)
        .block(Block::default().borders(Borders::ALL).title("Editor"))
        .scroll((0, 0));

    frame.render_widget(paragraph, area);

    // Render scrollbar if needed
    if editor.content.len() > visible_lines {
        let scrollbar = Scrollbar::default()
            .orientation(ScrollbarOrientation::VerticalRight)
            .begin_symbol(Some("↑"))
            .end_symbol(Some("↓"));

        let mut scrollbar_state = ScrollbarState::new(editor.content.len())
            .position(editor.scroll_offset);

        frame.render_stateful_widget(
            scrollbar,
            area.inner(ratatui::layout::Margin { vertical: 1, horizontal: 0 }),
            &mut scrollbar_state,
        );
    }
}

/// Apply basic syntax highlighting based on file type
fn apply_syntax_highlighting<'a>(line: &'a str, file_type: &FileType, is_cursor_line: bool) -> Vec<Span<'a>> {
    let base_style = if is_cursor_line {
        Style::default().bg(Color::Rgb(40, 40, 40))
    } else {
        Style::default()
    };

    match file_type {
        FileType::Rust => highlight_rust(line, base_style),
        FileType::Toml => highlight_toml(line, base_style),
        FileType::Ftl => highlight_ftl(line, base_style),
        FileType::Text => vec![Span::styled(line, base_style)],
    }
}

/// Basic Rust syntax highlighting
fn highlight_rust<'a>(line: &'a str, base_style: Style) -> Vec<Span<'a>> {
    let keywords = [
        "fn", "let", "mut", "const", "static", "struct", "enum", "impl", "trait", "for", "in", "while",
        "loop", "if", "else", "match", "return", "break", "continue", "pub", "use", "mod", "crate",
        "super", "self", "where", "unsafe", "async", "await", "move"
    ];

    let mut spans = Vec::new();
    let mut current = String::new();
    let mut chars = line.chars().peekable();

    while let Some(c) = chars.next() {
        if c.is_alphanumeric() || c == '_' {
            current.push(c);
        } else {
            if !current.is_empty() {
                let style = if keywords.contains(&current.as_str()) {
                    base_style.fg(Color::Magenta).add_modifier(Modifier::BOLD)
                } else if current.chars().all(|c| c.is_ascii_uppercase() || c == '_') {
                    base_style.fg(Color::Yellow) // Constants
                } else {
                    base_style
                };
                spans.push(Span::styled(current.clone(), style));
                current.clear();
            }

            // Handle comments
            if c == '/' && chars.peek() == Some(&'/') {
                let rest: String = std::iter::once(c).chain(chars).collect();
                spans.push(Span::styled(rest, base_style.fg(Color::Green)));
                break;
            }

            // Handle strings
            if c == '"' {
                let mut string_content = String::from('"');
                while let Some(sc) = chars.next() {
                    string_content.push(sc);
                    if sc == '"' && !string_content.ends_with("\\\"") {
                        break;
                    }
                }
                spans.push(Span::styled(string_content, base_style.fg(Color::Cyan)));
            } else {
                spans.push(Span::styled(c.to_string(), base_style));
            }
        }
    }

    if !current.is_empty() {
        let style = if keywords.contains(&current.as_str()) {
            base_style.fg(Color::Magenta).add_modifier(Modifier::BOLD)
        } else {
            base_style
        };
        spans.push(Span::styled(current, style));
    }

    spans
}

/// Basic TOML syntax highlighting
fn highlight_toml<'a>(line: &'a str, base_style: Style) -> Vec<Span<'a>> {
    if line.trim_start().starts_with('#') {
        // Comment
        vec![Span::styled(line, base_style.fg(Color::Green))]
    } else if line.contains('=') {
        // Key-value pair
        let parts: Vec<&str> = line.splitn(2, '=').collect();
        if parts.len() == 2 {
            let key = parts[0].trim();
            let value = parts[1].trim();
            vec![
                Span::styled(key, base_style.fg(Color::Blue)),
                Span::styled(" = ", base_style),
                Span::styled(value, base_style.fg(Color::Cyan)),
            ]
        } else {
            vec![Span::styled(line, base_style)]
        }
    } else if line.trim_start().starts_with('[') && line.trim_end().ends_with(']') {
        // Section header
        vec![Span::styled(line, base_style.fg(Color::Yellow).add_modifier(Modifier::BOLD))]
    } else {
        vec![Span::styled(line, base_style)]
    }
}

/// Basic Fluent (.ftl) syntax highlighting
fn highlight_ftl<'a>(line: &'a str, base_style: Style) -> Vec<Span<'a>> {
    if line.trim_start().starts_with('#') {
        // Comment
        vec![Span::styled(line, base_style.fg(Color::Green))]
    } else if line.contains('=') && !line.trim_start().starts_with(' ') {
        // Message key
        let parts: Vec<&str> = line.splitn(2, '=').collect();
        if parts.len() == 2 {
            let key = parts[0].trim();
            let value = parts[1].trim();
            vec![
                Span::styled(key, base_style.fg(Color::Blue).add_modifier(Modifier::BOLD)),
                Span::styled(" = ", base_style),
                Span::styled(value, base_style.fg(Color::White)),
            ]
        } else {
            vec![Span::styled(line, base_style)]
        }
    } else if line.contains('{') && line.contains('}') {
        // Parameter placeholders
        highlight_ftl_placeholders(line, base_style)
    } else {
        vec![Span::styled(line, base_style)]
    }
}

/// Highlight Fluent placeholders like {$param} and {TERM}
fn highlight_ftl_placeholders<'a>(line: &'a str, base_style: Style) -> Vec<Span<'a>> {
    let mut spans = Vec::new();
    let mut current = String::new();
    let mut chars = line.chars().peekable();
    let mut in_placeholder = false;

    while let Some(c) = chars.next() {
        if c == '{' {
            if !current.is_empty() {
                spans.push(Span::styled(current.clone(), base_style));
                current.clear();
            }
            in_placeholder = true;
            current.push(c);
        } else if c == '}' && in_placeholder {
            current.push(c);
            spans.push(Span::styled(current.clone(), base_style.fg(Color::Yellow)));
            current.clear();
            in_placeholder = false;
        } else {
            current.push(c);
        }
    }

    if !current.is_empty() {
        let style = if in_placeholder {
            base_style.fg(Color::Yellow)
        } else {
            base_style
        };
        spans.push(Span::styled(current, style));
    }

    spans
}

/// Render help overlay
fn render_help_overlay(frame: &mut Frame, area: Rect, _translations: &HashMap<String, String>) {
    // Center the help dialog
    let help_area = centered_rect(60, 70, area);

    let help_text = vec![
        Line::from("📝 Rustelo Built-in Editor Help"),
        Line::from(""),
        Line::from("🚀 Navigation:"),
        Line::from("  ↑↓←→     Move cursor"),
        Line::from("  Home/End  Beginning/End of line"),
        Line::from("  PgUp/PgDn Page up/down"),
        Line::from(""),
        Line::from("✏️  Editing:"),
        Line::from("  Enter     New line"),
        Line::from("  Backspace Delete before cursor"),
        Line::from("  Delete    Delete after cursor"),
        Line::from(""),
        Line::from("💾 File Operations:"),
        Line::from("  Ctrl+S    Save file"),
        Line::from("  Ctrl+Q    Quit (warns if unsaved)"),
        Line::from(""),
        Line::from("🎨 Features:"),
        Line::from("  • Syntax highlighting for Rust, TOML, Fluent"),
        Line::from("  • Line numbers"),
        Line::from("  • Scrollbar for long files"),
        Line::from("  • Current line highlighting"),
        Line::from(""),
        Line::from("Press Esc or Ctrl+H to close this help"),
    ];

    frame.render_widget(Clear, help_area);
    let help_paragraph = Paragraph::new(help_text)
        .block(Block::default()
            .borders(Borders::ALL)
            .title("Help")
            .style(Style::default().bg(Color::DarkGray)))
        .wrap(ratatui::widgets::Wrap { trim: true });

    frame.render_widget(help_paragraph, help_area);
}

/// Helper to create centered rectangle
fn centered_rect(percent_x: u16, percent_y: u16, r: Rect) -> Rect {
    let popup_layout = Layout::default()
        .direction(LayoutDirection::Vertical)
        .constraints([
            Constraint::Percentage((100 - percent_y) / 2),
            Constraint::Percentage(percent_y),
            Constraint::Percentage((100 - percent_y) / 2),
        ])
        .split(r);

    Layout::default()
        .direction(LayoutDirection::Horizontal)
        .constraints([
            Constraint::Percentage((100 - percent_x) / 2),
            Constraint::Percentage(percent_x),
            Constraint::Percentage((100 - percent_x) / 2),
        ])
        .split(popup_layout[1])[1]
}
