use crossterm::event::{KeyCode, KeyEvent};
use ratatui::layout::{Constraint, Direction, Layout, Margin, Rect};
use ratatui::text::{Line, Span};
use ratatui::widgets::{
    Block, Borders, Paragraph, Scrollbar, ScrollbarOrientation, ScrollbarState,
};
use ratatui::Frame;

use crate::action::Action;
use crate::components::{ui_utils, Component};
use crate::theme::theme;

pub struct LogTab {
    pub lines: Vec<LogLine>,
    pub scroll_offset: usize,
    pub auto_scroll: bool,
    /// Whether we're in filter input mode
    pub filter_active: bool,
    /// Current filter query
    pub filter_query: String,
}

#[derive(Debug, Clone)]
pub struct LogLine {
    pub text: String,
    pub level: LogLevel,
    pub timestamp: String,
}

#[derive(Debug, Clone)]
pub enum LogLevel {
    Info,
    Success,
    Error,
    Command,
}

impl LogTab {
    pub fn new() -> Self {
        Self {
            lines: Vec::new(),
            scroll_offset: 0,
            auto_scroll: true,
            filter_active: false,
            filter_query: String::new(),
        }
    }

    pub fn add_line(&mut self, text: String) {
        let level = if text.starts_with("[stderr]")
            || text.to_lowercase().contains("error")
            || text.to_lowercase().contains("fail")
        {
            LogLevel::Error
        } else if text.contains("success")
            || text.contains("Finished")
            || text.contains("completed")
        {
            LogLevel::Success
        } else if text.starts_with('$') || text.starts_with('>') {
            LogLevel::Command
        } else {
            LogLevel::Info
        };

        let timestamp = local_time_now();

        self.lines.push(LogLine {
            text,
            level,
            timestamp,
        });

        // No need to update scroll_offset here — auto_scroll is handled at render time
    }

    pub fn add_command(&mut self, cmd: &str) {
        self.lines.push(LogLine {
            text: format!("$ {}", cmd),
            level: LogLevel::Command,
            timestamp: local_time_now(),
        });
    }

    pub fn clear(&mut self) {
        self.lines.clear();
        self.scroll_offset = 0;
    }

    fn filtered_lines(&self) -> Vec<(usize, &LogLine)> {
        if self.filter_query.is_empty() {
            self.lines.iter().enumerate().collect()
        } else {
            let query = self.filter_query.to_lowercase();
            let terms: Vec<&str> = query.split_whitespace().collect();
            self.lines
                .iter()
                .enumerate()
                .filter(|(_, line)| {
                    let haystack = format!("{} {}", line.timestamp, line.text).to_lowercase();
                    terms.iter().all(|term| haystack.contains(term))
                })
                .collect()
        }
    }

    fn scroll_to_bottom(&mut self) {
        // Set scroll_offset to a very large value; render will clamp it.
        // This also enables auto_scroll so future lines stay pinned to bottom.
        self.scroll_offset = usize::MAX;
        self.auto_scroll = true;
    }
}

impl Component for LogTab {
    fn handle_key(&mut self, key: KeyEvent) -> Action {
        // ── Filter input mode ───────────────────
        if self.filter_active {
            match key.code {
                KeyCode::Esc => {
                    self.filter_active = false;
                    self.filter_query.clear();
                    return Action::Noop;
                }
                KeyCode::Enter => {
                    self.filter_active = false;
                    return Action::Noop;
                }
                KeyCode::Char(c) => {
                    self.filter_query.push(c);
                    self.scroll_offset = 0;
                    return Action::Noop;
                }
                KeyCode::Backspace => {
                    self.filter_query.pop();
                    return Action::Noop;
                }
                KeyCode::Down => {
                    self.auto_scroll = false;
                    self.scroll_offset = self.scroll_offset.saturating_add(1);
                    return Action::Noop;
                }
                KeyCode::Up => {
                    self.auto_scroll = false;
                    self.scroll_offset = self.scroll_offset.saturating_sub(1);
                    return Action::Noop;
                }
                _ => return Action::Noop,
            }
        }

        // ── Normal mode ─────────────────────────
        match key.code {
            KeyCode::Char('c') => {
                self.clear();
                Action::Noop
            }
            KeyCode::Char('f') | KeyCode::Char('/') => {
                self.filter_active = true;
                self.filter_query.clear();
                Action::Noop
            }
            KeyCode::Esc => {
                if !self.filter_query.is_empty() {
                    self.filter_query.clear();
                }
                Action::Noop
            }
            KeyCode::Char('j') | KeyCode::Down => {
                self.auto_scroll = false;
                self.scroll_offset = self.scroll_offset.saturating_add(1);
                Action::Noop
            }
            KeyCode::Char('k') | KeyCode::Up => {
                self.auto_scroll = false;
                self.scroll_offset = self.scroll_offset.saturating_sub(1);
                Action::Noop
            }
            KeyCode::Char('d') | KeyCode::PageDown => {
                self.auto_scroll = false;
                self.scroll_offset = self.scroll_offset.saturating_add(10);
                Action::Noop
            }
            KeyCode::Char('u') | KeyCode::PageUp => {
                self.auto_scroll = false;
                self.scroll_offset = self.scroll_offset.saturating_sub(10);
                Action::Noop
            }
            KeyCode::Char('G') | KeyCode::End => {
                self.scroll_to_bottom();
                Action::Noop
            }
            KeyCode::Char('g') | KeyCode::Home => {
                self.auto_scroll = false;
                self.scroll_offset = 0;
                Action::Noop
            }
            KeyCode::Char('a') => {
                self.scroll_to_bottom();
                Action::Noop
            }
            _ => Action::Noop,
        }
    }

    fn render(&mut self, frame: &mut Frame, area: Rect) {
        let t = theme();

        let filtered = self.filtered_lines();
        let query_lower = self.filter_query.to_lowercase();

        // Layout: optional filter bar + log content
        let has_filter = self.filter_active || !self.filter_query.is_empty();
        let chunks = if has_filter {
            Layout::default()
                .direction(Direction::Vertical)
                .constraints([Constraint::Length(3), Constraint::Min(1)])
                .split(area)
        } else {
            Layout::default()
                .direction(Direction::Vertical)
                .constraints([Constraint::Length(0), Constraint::Min(1)])
                .split(area)
        };

        // ── Filter bar ──────────────────────────
        if has_filter {
            ui_utils::render_filter_bar(
                frame,
                chunks[0],
                &self.filter_query,
                self.filter_active,
                &format!("{}/{} lines", filtered.len(), self.lines.len()),
            );
        }

        let log_area = chunks[1];
        let visible_rows = log_area.height.saturating_sub(2);

        // ── Log lines ───────────────────────────
        let lines: Vec<Line> = filtered
            .iter()
            .map(|(_, log_line)| {
                let style = match log_line.level {
                    LogLevel::Info => t.log_info,
                    LogLevel::Success => t.log_success,
                    LogLevel::Error => t.log_error,
                    LogLevel::Command => t.status_info,
                };

                let text_span = if !query_lower.is_empty() {
                    ui_utils::highlight_match(&log_line.text, &query_lower, style, t.filter_match)
                } else {
                    vec![Span::styled(&log_line.text, style)]
                };

                let mut spans = vec![Span::styled(
                    format!("[{}] ", log_line.timestamp),
                    t.tab_inactive,
                )];
                spans.extend(text_span);
                Line::from(spans)
            })
            .collect();

        let title = if !self.filter_query.is_empty() {
            format!(
                " Log ({}/{} lines){} ",
                filtered.len(),
                self.lines.len(),
                if self.auto_scroll {
                    " [auto-scroll]"
                } else {
                    ""
                }
            )
        } else {
            format!(
                " Log ({} lines){} ",
                self.lines.len(),
                if self.auto_scroll {
                    " [auto-scroll]"
                } else {
                    ""
                }
            )
        };

        // Compute effective scroll offset
        let total_lines = filtered.len();
        let visible = visible_rows as usize;
        let max_scroll = total_lines.saturating_sub(visible);
        let effective_offset = if self.auto_scroll {
            max_scroll
        } else {
            self.scroll_offset.min(max_scroll)
        };

        let paragraph = Paragraph::new(lines)
            .block(
                Block::default()
                    .borders(Borders::ALL)
                    .border_style(t.border_focused)
                    .title(title)
                    .title_style(t.header),
            )
            .scroll((effective_offset as u16, 0));

        frame.render_widget(paragraph, log_area);

        // Re-enable auto_scroll when user scrolls back to the bottom
        if !self.auto_scroll && self.scroll_offset >= max_scroll {
            self.auto_scroll = true;
        }

        // Scrollbar
        if total_lines > visible {
            let scrollbar = Scrollbar::new(ScrollbarOrientation::VerticalRight)
                .begin_symbol(Some("↑"))
                .end_symbol(Some("↓"));
            let mut scrollbar_state = ScrollbarState::new(max_scroll).position(effective_offset);
            frame.render_stateful_widget(
                scrollbar,
                log_area.inner(Margin {
                    vertical: 1,
                    horizontal: 0,
                }),
                &mut scrollbar_state,
            );
        }
    }
}

/// Local timestamp (HH:MM:SS)
fn local_time_now() -> String {
    let now = time::OffsetDateTime::now_utc()
        .to_offset(time::UtcOffset::current_local_offset().unwrap_or(time::UtcOffset::UTC));
    format!("{:02}:{:02}:{:02}", now.hour(), now.minute(), now.second())
}
