use ratatui::{
    layout::{Constraint, Direction, Layout, Rect},
    style::{Modifier, Style},
    text::{Line, Span},
    widgets::{Block, BorderType, Borders, Paragraph},
    Frame,
};

use crate::app::App;
use crate::chat::{TodoDisplayStatus, TodoMessageState};
use crate::render::utils::{sanitize_terminal_text, truncate_display_width};

impl App {
    pub(crate) fn render_sidebar(&self, frame: &mut Frame, area: Rect) {
        let Some(plan) = self.state.plan_state.as_ref() else {
            self.render_session_diff(frame, area);
            return;
        };

        if area.height < 12 {
            self.render_plan_panel(frame, area, plan);
            return;
        }

        let plan_height = plan_panel_height(plan, area.height);
        let chunks = Layout::default()
            .direction(Direction::Vertical)
            .constraints([Constraint::Length(plan_height), Constraint::Min(5)])
            .split(area);
        self.render_plan_panel(frame, chunks[0], plan);
        self.render_session_diff(frame, chunks[1]);
    }

    fn render_plan_panel(&self, frame: &mut Frame, area: Rect, plan: &TodoMessageState) {
        let block = Block::default()
            .borders(Borders::ALL)
            .border_type(BorderType::Rounded)
            .border_style(Style::default().fg(self.state.theme.border))
            .title(" Plan ")
            .style(Style::default().bg(self.state.theme.background));
        let inner = block.inner(area);
        frame.render_widget(block, area);

        if inner.width == 0 || inner.height == 0 {
            return;
        }

        let completed = plan
            .items
            .iter()
            .filter(|(status, _)| *status == TodoDisplayStatus::Completed)
            .count();
        let total = plan.items.len();
        let mut lines = vec![
            Line::from(vec![
                Span::styled(
                    format!("{completed}/{total} done"),
                    Style::default()
                        .fg(self.state.theme.foreground)
                        .add_modifier(Modifier::BOLD),
                ),
                Span::raw("  "),
                Span::styled(
                    truncate_display_width(&plan.title, inner.width.saturating_sub(10) as usize),
                    Style::default().fg(self.state.theme.muted),
                ),
            ]),
            Line::raw(""),
        ];

        let max_lines = inner.height as usize;
        let mut shown_items = 0usize;
        for (status, content) in &plan.items {
            if lines.len() + 1 > max_lines {
                break;
            }
            shown_items += 1;
            let (marker, color) = match status {
                TodoDisplayStatus::Completed => ("✓", self.state.theme.success),
                TodoDisplayStatus::InProgress => (">", self.state.theme.accent),
                TodoDisplayStatus::Pending => (" ", self.state.theme.muted),
            };
            let prefix = sanitize_terminal_text(&format!("[{marker}] "));
            let content_width = inner.width.saturating_sub(prefix.chars().count() as u16) as usize;
            lines.push(Line::from(vec![
                Span::styled(prefix, Style::default().fg(color)),
                Span::styled(
                    truncate_display_width(content, content_width),
                    Style::default().fg(self.state.theme.foreground),
                ),
            ]));
        }

        let remaining = plan.items.len().saturating_sub(shown_items);
        if remaining > 0 && lines.len() < max_lines {
            lines.push(Line::from(Span::styled(
                format!("… {remaining} more"),
                Style::default().fg(self.state.theme.muted),
            )));
        }

        frame.render_widget(Paragraph::new(lines), inner);
    }

    pub(crate) fn render_session_diff(&self, frame: &mut Frame, area: Rect) {
        let block = Block::default()
            .borders(Borders::ALL)
            .border_type(BorderType::Rounded)
            .border_style(Style::default().fg(self.state.theme.border))
            .title(" Session Diff ")
            .style(Style::default().bg(self.state.theme.background));
        let inner = block.inner(area);
        frame.render_widget(block, area);

        if inner.width == 0 || inner.height == 0 {
            return;
        }

        let entries = self.state.sorted_session_file_changes();
        if entries.is_empty() {
            let empty = Paragraph::new("No file changes in this session yet.")
                .style(Style::default().fg(self.state.theme.muted));
            frame.render_widget(empty, inner);
            return;
        }

        let total_additions: u32 = entries.iter().map(|entry| entry.additions).sum();
        let total_deletions: u32 = entries.iter().map(|entry| entry.deletions).sum();
        let mut lines = vec![
            Line::from(vec![
                Span::styled(
                    format!("{} files", entries.len()),
                    Style::default()
                        .fg(self.state.theme.foreground)
                        .add_modifier(Modifier::BOLD),
                ),
                Span::raw("  "),
                Span::styled(
                    format!("+{total_additions}"),
                    Style::default().fg(self.state.theme.success),
                ),
                Span::raw(" "),
                Span::styled(
                    format!("-{total_deletions}"),
                    Style::default().fg(self.state.theme.error),
                ),
            ]),
            Line::raw(""),
        ];

        let max_lines = inner.height as usize;
        let mut shown_entries = 0usize;
        for entry in &entries {
            if lines.len() + 4 > max_lines {
                break;
            }
            shown_entries += 1;
            let display_path = self.state.display_file_path(&entry.file_path);
            lines.push(Line::from(Span::styled(
                truncate_display_width(&display_path, inner.width as usize),
                Style::default()
                    .fg(self.state.theme.foreground)
                    .add_modifier(Modifier::BOLD),
            )));
            lines.push(Line::from(vec![
                Span::raw("  "),
                Span::styled(
                    format!("+{}", entry.additions),
                    Style::default().fg(self.state.theme.success),
                ),
                Span::raw(" "),
                Span::styled(
                    format!("-{}", entry.deletions),
                    Style::default().fg(self.state.theme.error),
                ),
            ]));
            lines.push(Line::raw(""));
        }

        let remaining = entries.len().saturating_sub(shown_entries);
        if remaining > 0 && lines.len() < max_lines {
            lines.push(Line::from(Span::styled(
                format!("… {remaining} more"),
                Style::default().fg(self.state.theme.muted),
            )));
        }

        frame.render_widget(Paragraph::new(lines), inner);
    }
}

fn plan_panel_height(plan: &TodoMessageState, available_height: u16) -> u16 {
    if available_height < 14 {
        return available_height;
    }
    let desired = (plan.items.len() as u16).saturating_add(4);
    desired.clamp(7, (available_height / 2).max(7))
}