sara-tasks 2.7.7

Sara — folder-aware task manager
use ratatui::{
    buffer::Buffer,
    layout::Rect,
    style::{Modifier, Style},
    text::{Line, Span},
    widgets::{Paragraph, Widget},
};

use crate::commands::info::handler::guide_is_stale;
use crate::commands::info::types::Detail;
use crate::commands::shared::short_id;
use crate::domain::Status;
use crate::infrastructure::tui::theme::{Ink, ink};

pub(super) fn header_bar(d: &Detail, area: Rect, buf: &mut Buffer) {
    let bg = ink(Ink::Header);
    let sep = || Span::raw("  ");
    let mut left = vec![Span::raw(" "), status_badge(d)];
    left.push(sep());
    left.push(freshness_badge(d));
    left.push(sep());
    left.push(branch_badge(d));
    left.push(sep());
    left.extend(feedback_badge(d));
    let ids = id_badge(d);
    let used: usize = left.iter().map(|s| s.width()).sum::<usize>() + ids.width();
    let pad = (area.width as usize).saturating_sub(used + 1);
    left.push(Span::raw(" ".repeat(pad.max(2))));
    left.push(ids);
    left.push(Span::raw(" "));
    Paragraph::new(Line::from(left))
        .style(Style::default().bg(bg))
        .render(area, buf);
}

fn status_badge(d: &Detail) -> Span<'static> {
    let t = &d.task;
    let (text, color) = if t.is_active() {
        ("[● ACTIVE]".to_string(), ink(Ink::Ok))
    } else {
        let color = match t.status {
            Status::Completed => ink(Ink::Ok),
            Status::Deleted => ink(Ink::Err),
            Status::Pending => ink(Ink::Text),
        };
        (
            format!("[○ {}]", t.status.to_string().to_uppercase()),
            color,
        )
    };
    Span::styled(
        text,
        Style::default().fg(color).add_modifier(Modifier::BOLD),
    )
}

fn freshness_badge(d: &Detail) -> Span<'static> {
    match &d.guide.validated_commit {
        Some(v) if guide_is_stale(d) => Span::styled(
            format!(
                "[⚠ STALE @{} · HEAD {}]",
                short_id(v),
                short_id(d.head_commit.as_deref().unwrap_or("-"))
            ),
            Style::default()
                .fg(ink(Ink::Warn))
                .add_modifier(Modifier::BOLD),
        ),
        Some(v) => Span::styled(
            format!("[✓ VALID @{}]", short_id(v)),
            Style::default().fg(ink(Ink::Ok)),
        ),
        None => Span::styled(
            "[· NOT VALIDATED]".to_string(),
            Style::default().fg(ink(Ink::Muted)),
        ),
    }
}

fn branch_badge(d: &Detail) -> Span<'static> {
    match &d.branch {
        Some(b) => Span::styled(
            format!("[⎇ {}]", b.branch),
            Style::default().fg(ink(Ink::Info)),
        ),
        None => Span::styled(
            "[⎇ NO BRANCH]".to_string(),
            Style::default().fg(ink(Ink::Muted)),
        ),
    }
}

fn feedback_badge(d: &Detail) -> Vec<Span<'static>> {
    let open: Vec<_> = d
        .annotations
        .iter()
        .filter(|a| a.kind == "comment" && a.status == "open")
        .collect();
    let revise = open.iter().filter(|a| a.request_revision).count();
    let mut text = if open.is_empty() {
        "[· NO FEEDBACK".to_string()
    } else {
        format!("[! {} FEEDBACK", open.len())
    };
    let mut style = Style::default().fg(if open.is_empty() {
        ink(Ink::Muted)
    } else {
        ink(Ink::Accent)
    });
    if revise > 0 {
        text.push_str(&format!(" · ⟳ {revise} REVISE"));
        style = Style::default()
            .fg(ink(Ink::Warn))
            .add_modifier(Modifier::BOLD);
    }
    text.push(']');
    vec![Span::styled(text, style)]
}

fn id_badge(d: &Detail) -> Span<'static> {
    let t = &d.task;
    let id = t.id.map(|i| format!("#{i}")).unwrap_or_else(|| "#-".into());
    Span::styled(
        format!("{id} · {}", short_id(&t.uuid.to_string())),
        Style::default().fg(ink(Ink::Soft)),
    )
}