use ratatui::Frame;
use ratatui::layout::Rect;
use ratatui::style::{Color, Modifier, Style};
use ratatui::text::{Line, Span};
use ratatui::widgets::{Block, BorderType, Borders, Clear, List, ListItem, ListState};
use unicode_width::UnicodeWidthStr;

use super::truncate::truncate_chars;
use crate::choices::TaskPriority;
use crate::tui::app::{Focus, WidgetState};
use crate::tui::store::{
    SidebarEntry, SidebarEntryTarget, TaskScope, TaskScopeTarget, TaskView, TuiStore,
};
use crate::tui::theme::{
    self, ACCENT, BG, BG_ALT, BORDER, FG, FG_DIM, FG_MUTED, PINK, RED, SELECTED_BG,
    SELECTED_INACTIVE, YELLOW,
};

const QUEUE_MARKER: &str = "\u{f03a}";
const INBOX_MARKER: &str = "\u{f01c}";
const TODO_MARKER: &str = "\u{f0ae}";
const ACTIVE_MARKER: &str = "\u{f111}";
const BACKLOG_MARKER: &str = "\u{f017}";
const DONE_MARKER: &str = "\u{f00c}";
const CONFLICT_MARKER: &str = "\u{f071}";
const SEARCH_MARKER: &str = "\u{f002}";
const RECENT_MARKER: &str = "\u{f1da}";
const EPIC_MARKER: &str = "\u{f005}";
const OPEN_MARKER: &str = "\u{f07c}";
const WORKSPACE_MARKER: &str = "\u{f0e8}";
const PROJECT_MARKER: &str = "\u{f07b}";
const SIDEBAR_ICON_WIDTH: usize = 2;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct SidebarClick {
    pub(crate) entry_index: usize,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct SidebarLayout {
    pub(crate) sidebar: Rect,
    pub(crate) content: Rect,
    pub(crate) overlay: bool,
}

#[cfg(test)]
pub(crate) fn sidebar_layout(terminal: Rect, focus: Focus) -> Option<SidebarLayout> {
    sidebar_layout_for(terminal, focus, true)
}

pub(crate) fn sidebar_layout_for(
    terminal: Rect,
    focus: Focus,
    sidebar_visible: bool,
) -> Option<SidebarLayout> {
    if !sidebar_visible || terminal.width < 70 || terminal.height < 18 {
        return None;
    }

    let body = Rect {
        x: terminal.x,
        y: terminal.y + 2,
        width: terminal.width,
        height: terminal.height.saturating_sub(4),
    };

    let overlay = body.width < 100;
    if overlay && focus != Focus::Sidebar {
        return None;
    }

    let sidebar = if overlay {
        sidebar_overlay_area(body)
    } else {
        Rect {
            x: body.x,
            y: body.y,
            width: body.width.min(26),
            height: body.height,
        }
    };

    Some(SidebarLayout {
        sidebar,
        content: sidebar_content_area(sidebar, overlay),
        overlay,
    })
}

pub(crate) fn sidebar_overlay_area(area: Rect) -> Rect {
    Rect {
        x: area.x + 2,
        y: area.y + 1,
        width: area.width.saturating_sub(4).min(34),
        height: area.height.saturating_sub(2).min(24),
    }
}

pub(crate) fn sidebar_content_area(sidebar: Rect, overlay: bool) -> Rect {
    if overlay {
        Rect {
            x: sidebar.x + 1,
            y: sidebar.y + 1,
            width: sidebar.width.saturating_sub(2),
            height: sidebar.height.saturating_sub(2),
        }
    } else {
        Rect {
            x: sidebar.x,
            y: sidebar.y,
            width: sidebar.width.saturating_sub(1),
            height: sidebar.height,
        }
    }
}

pub(crate) fn sidebar_click_at_for(
    entries: &[SidebarEntry],
    state: &ListState,
    focus: Focus,
    sidebar_visible: bool,
    terminal: Rect,
    column: u16,
    row: u16,
) -> Option<SidebarClick> {
    let layout = sidebar_layout_for(terminal, focus, sidebar_visible)?;
    if column < layout.content.x
        || column >= layout.content.x.saturating_add(layout.content.width)
        || row < layout.content.y
        || row >= layout.content.y.saturating_add(layout.content.height)
    {
        return None;
    }

    let entry_index = usize::from(row - layout.content.y).saturating_add(state.offset());
    entries.get(entry_index).and_then(|entry| {
        if entry.target.is_some() {
            Some(SidebarClick { entry_index })
        } else {
            None
        }
    })
}

pub(super) fn render_sidebar_overlay(
    frame: &mut Frame,
    store: &TuiStore,
    widgets: &mut WidgetState,
    focus: Focus,
    area: Rect,
) {
    let area = sidebar_overlay_area(area);
    frame.render_widget(Clear, area);
    render_sidebar(frame, store, widgets, focus, area, true);
}

pub(super) fn render_sidebar(
    frame: &mut Frame,
    store: &TuiStore,
    widgets: &mut WidgetState,
    focus: Focus,
    area: Rect,
    overlay: bool,
) {
    let content_width = area.width.saturating_sub(if overlay { 2 } else { 1 }) as usize;
    let mut items: Vec<ListItem> = store
        .sidebar_entries
        .iter()
        .enumerate()
        .map(|(index, entry)| {
            if entry.section {
                if entry.label.is_empty() || entry.label == "Smart Views" {
                    return ListItem::new(Line::from(""));
                }
                return ListItem::new(
                    Line::from(format!(" {} ", entry.label.to_uppercase())).style(
                        Style::new()
                            .fg(FG_DIM)
                            .bg(BG_ALT)
                            .add_modifier(Modifier::BOLD),
                    ),
                );
            }
            let marker = sidebar_icon(entry);
            let label = sidebar_label(entry);
            let selected = index == widgets.sidebar.selected().unwrap_or(usize::MAX);
            let is_active_view = sidebar_entry_active(entry, store);
            let color = match &entry.target {
                Some(SidebarEntryTarget::Scope(TaskScopeTarget::Project(project))) => {
                    theme::project_color(project)
                }
                Some(SidebarEntryTarget::View(TaskView::Active)) => FG_MUTED,
                Some(SidebarEntryTarget::View(TaskView::Todo)) => FG_DIM,
                Some(SidebarEntryTarget::View(TaskView::Epics)) => YELLOW,
                Some(SidebarEntryTarget::View(TaskView::RecentActions)) => ACCENT,
                _ => FG,
            };
            let label_style = if is_active_view {
                Style::new().fg(ACCENT).add_modifier(Modifier::BOLD)
            } else if selected {
                Style::new().fg(FG).add_modifier(Modifier::BOLD)
            } else {
                Style::new().fg(FG)
            };
            let line = sidebar_entry_line(
                entry,
                marker,
                &label,
                label_style,
                color,
                is_active_view,
                content_width,
            );
            ListItem::new(line)
        })
        .collect();

    let urgent_count = store
        .tasks
        .iter()
        .filter(|task| task.task.priority == TaskPriority::Urgent)
        .count() as i64;
    let conflict_count = store.tasks.iter().filter(|task| task.has_conflict).count() as i64;
    items.extend([
        ListItem::new(Line::from("")),
        ListItem::new(
            Line::from("FILTERS").style(Style::new().fg(FG_DIM).add_modifier(Modifier::BOLD)),
        ),
        filter_item("▲", "urgent", urgent_count, RED, area.width),
        filter_item("⚡", "conflicts", conflict_count, PINK, area.width),
    ]);

    let highlight_style = if focus == Focus::Sidebar {
        Style::new().bg(SELECTED_BG)
    } else {
        SELECTED_INACTIVE
    };
    let borders = if overlay {
        Borders::ALL
    } else {
        Borders::RIGHT
    };
    let list = List::new(items)
        .block(
            Block::new()
                .borders(borders)
                .border_type(BorderType::Rounded)
                .border_style(Style::new().fg(BORDER))
                .style(Style::new().bg(BG)),
        )
        .highlight_style(highlight_style);
    frame.render_stateful_widget(list, area, &mut widgets.sidebar);
}

fn badge(count: i64, active: bool) -> Span<'static> {
    if count <= 0 {
        return Span::raw(" ");
    }
    let color = if active { ACCENT } else { FG_MUTED };
    Span::styled(
        format!("{count:>2}"),
        Style::new().fg(color).add_modifier(Modifier::BOLD),
    )
}

fn sidebar_entry_active(entry: &SidebarEntry, store: &TuiStore) -> bool {
    match &entry.target {
        Some(SidebarEntryTarget::View(view)) => *view == store.view_state.view,
        Some(SidebarEntryTarget::Scope(TaskScopeTarget::Workspace)) => {
            store.view_state.scope == TaskScope::Workspace
        }
        Some(SidebarEntryTarget::Scope(TaskScopeTarget::Project(project))) => {
            store.scope_project() == Some(project.as_str())
        }
        None => false,
    }
}

fn sidebar_icon(entry: &SidebarEntry) -> &'static str {
    match entry.target {
        Some(SidebarEntryTarget::View(TaskView::Queue)) => QUEUE_MARKER,
        Some(SidebarEntryTarget::View(TaskView::Inbox)) => INBOX_MARKER,
        Some(SidebarEntryTarget::View(TaskView::Todo)) => TODO_MARKER,
        Some(SidebarEntryTarget::View(TaskView::Active)) => ACTIVE_MARKER,
        Some(SidebarEntryTarget::View(TaskView::Backlog)) => BACKLOG_MARKER,
        Some(SidebarEntryTarget::View(TaskView::Done)) => DONE_MARKER,
        Some(SidebarEntryTarget::View(TaskView::Conflicts)) => CONFLICT_MARKER,
        Some(SidebarEntryTarget::View(TaskView::Search)) => SEARCH_MARKER,
        Some(SidebarEntryTarget::View(TaskView::RecentActions)) => RECENT_MARKER,
        Some(SidebarEntryTarget::View(TaskView::Epics)) => EPIC_MARKER,
        Some(SidebarEntryTarget::View(TaskView::Open)) => OPEN_MARKER,
        Some(SidebarEntryTarget::Scope(TaskScopeTarget::Workspace)) => WORKSPACE_MARKER,
        Some(SidebarEntryTarget::Scope(TaskScopeTarget::Project(_))) => PROJECT_MARKER,
        None => " ",
    }
}

fn sidebar_label(entry: &SidebarEntry) -> String {
    match entry.target {
        Some(SidebarEntryTarget::View(TaskView::Queue)) => "Queue".to_string(),
        Some(SidebarEntryTarget::View(TaskView::Inbox)) => "Inbox".to_string(),
        Some(SidebarEntryTarget::View(TaskView::Active)) => "All active".to_string(),
        Some(SidebarEntryTarget::View(TaskView::Backlog)) => "Backlog".to_string(),
        Some(SidebarEntryTarget::View(TaskView::Todo)) => "All todo".to_string(),
        Some(SidebarEntryTarget::View(TaskView::Done)) => "Done".to_string(),
        Some(SidebarEntryTarget::View(TaskView::Conflicts)) => "Conflicts".to_string(),
        Some(SidebarEntryTarget::View(TaskView::Search)) => "Search".to_string(),
        Some(SidebarEntryTarget::View(TaskView::RecentActions)) => "Recent actions".to_string(),
        Some(SidebarEntryTarget::View(TaskView::Epics)) => "Epics".to_string(),
        Some(SidebarEntryTarget::View(TaskView::Open)) => "Open".to_string(),
        Some(SidebarEntryTarget::Scope(TaskScopeTarget::Workspace)) => "Workspace".to_string(),
        Some(SidebarEntryTarget::Scope(TaskScopeTarget::Project(_))) => entry
            .label
            .split_once(' ')
            .map(|(_, name)| name)
            .unwrap_or(&entry.label)
            .trim_end_matches('*')
            .to_string(),
        None => entry.label.clone(),
    }
}

fn sidebar_icon_cell(marker: &str) -> String {
    let marker_width = marker.width();
    let padding = SIDEBAR_ICON_WIDTH.saturating_sub(marker_width) + 1;
    format!("{marker}{}", " ".repeat(padding))
}

fn sidebar_entry_line(
    entry: &SidebarEntry,
    marker: &str,
    label: &str,
    label_style: Style,
    marker_color: Color,
    active: bool,
    width: usize,
) -> Line<'static> {
    let marker_cell = sidebar_icon_cell(marker);
    let count = entry.count.to_string();
    let reserved_width = marker_cell.width() + count.width() + 1;
    let label_width = width.saturating_sub(reserved_width);
    let label = truncate_chars(label, label_width);
    let used_width = marker_cell.width() + label.width() + count.width();
    let spacer_width = width.saturating_sub(used_width).max(1);
    let count_style = if active {
        Style::new().fg(ACCENT).add_modifier(Modifier::BOLD)
    } else {
        Style::new().fg(FG_MUTED).add_modifier(Modifier::BOLD)
    };
    Line::from(vec![
        Span::styled(marker_cell, Style::new().fg(marker_color)),
        Span::styled(label, label_style),
        Span::raw(" ".repeat(spacer_width)),
        Span::styled(count, count_style),
    ])
}

fn filter_item(icon: &str, label: &str, count: i64, color: Color, width: u16) -> ListItem<'static> {
    let icon_cell = if icon == "⚡" {
        format!("{icon} ")
    } else {
        format!("{icon}  ")
    };
    let count_width = if count > 0 { 2 } else { 1 };
    let label_width = (width as usize).saturating_sub(icon_cell.chars().count() + count_width + 2);
    ListItem::new(Line::from(vec![
        Span::styled(icon_cell, Style::new().fg(color)),
        Span::styled(format!("{label:<label_width$}"), Style::new().fg(FG_MUTED)),
        badge(count, false),
    ]))
}

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

    #[test]
    fn sidebar_icon_cell_uses_fixed_display_width() {
        assert_eq!(sidebar_icon_cell("○").width(), 3);
        assert_eq!(sidebar_icon_cell(QUEUE_MARKER).width(), 3);
        assert_eq!(sidebar_icon_cell(EPIC_MARKER).width(), 3);
    }

    #[test]
    fn sidebar_entry_line_truncates_label_and_preserves_count() {
        let entry = SidebarEntry {
            label: "APP very-long-project-name".to_string(),
            count: 12,
            section: false,
            target: Some(SidebarEntryTarget::Scope(TaskScopeTarget::Project(
                "very-long-project-name".to_string(),
            ))),
        };

        let rendered = sidebar_entry_line(
            &entry,
            "●",
            "very-long-project-name",
            Style::new().fg(FG),
            FG,
            false,
            14,
        )
        .to_string();

        assert!(rendered.contains("…"));
        assert!(rendered.ends_with("12"));
    }
}
