tspring 1.0.0

Terminal UI for Spring Boot - A modern TUI for managing Spring Boot applications
use ratatui::{
    layout::Rect,
    style::{Color, Modifier, Style},
    text::Span,
    widgets::{Block, Borders, Cell, Row, Table, TableState},
    Frame,
};

use super::highlight_text;
use crate::app::App;

pub fn render(f: &mut Frame, app: &App, area: Rect) {
    let filter = app.filter_text.to_lowercase();

    let filtered: Vec<(usize, &crate::model::Logger)> = app
        .loggers
        .iter()
        .enumerate()
        .filter(|(_, l)| {
            if filter.is_empty() {
                return true;
            }
            l.name.to_lowercase().contains(&filter)
                || l.effective_level.to_lowercase().contains(&filter)
                || l.configured_level
                    .as_deref()
                    .unwrap_or("")
                    .to_lowercase()
                    .contains(&filter)
        })
        .collect();

    let title = if filter.is_empty() {
        format!(" Loggers ({}) ", app.loggers.len())
    } else {
        format!(" Loggers ({}/{}) ", filtered.len(), app.loggers.len())
    };

    let block = Block::default()
        .borders(Borders::ALL)
        .border_style(Style::default().fg(Color::DarkGray))
        .title(Span::styled(
            title,
            Style::default()
                .fg(Color::Cyan)
                .add_modifier(Modifier::BOLD),
        ));

    let inner_area = block.inner(area);
    f.render_widget(block, area);

    if filtered.is_empty() {
        let msg = if app.loggers.is_empty() {
            "  No loggers loaded. Connect to a server and run :loggers"
        } else {
            "  No matching loggers."
        };
        let empty = ratatui::widgets::Paragraph::new(Span::styled(
            msg,
            Style::default().fg(Color::DarkGray),
        ));
        f.render_widget(empty, inner_area);
        return;
    }

    let header = Row::new(vec![
        Cell::from("NAME"),
        Cell::from("CONFIGURED"),
        Cell::from("EFFECTIVE"),
    ])
    .style(
        Style::default()
            .fg(Color::Yellow)
            .add_modifier(Modifier::BOLD),
    )
    .height(1);

    let rows: Vec<Row> = filtered
        .iter()
        .map(|(_, l)| {
            let configured = l.configured_level.as_deref().unwrap_or("-").to_string();
            let base = Style::default().fg(Color::White);

            let effective_style = match l.effective_level.as_str() {
                "ERROR" => Style::default().fg(Color::Red),
                "WARN" => Style::default().fg(Color::Yellow),
                "INFO" => Style::default().fg(Color::Green),
                "DEBUG" => Style::default().fg(Color::Cyan),
                "TRACE" => Style::default().fg(Color::Magenta),
                _ => Style::default().fg(Color::Gray),
            };

            Row::new(vec![
                Cell::from(highlight_text(&l.name, &filter, base)),
                Cell::from(highlight_text(&configured, &filter, base)),
                Cell::from(highlight_text(&l.effective_level, &filter, effective_style)),
            ])
        })
        .collect();

    let widths = [
        ratatui::layout::Constraint::Percentage(50),
        ratatui::layout::Constraint::Percentage(25),
        ratatui::layout::Constraint::Percentage(25),
    ];

    let table = Table::new(rows, widths).header(header).row_highlight_style(
        Style::default()
            .bg(Color::DarkGray)
            .fg(Color::White)
            .add_modifier(Modifier::BOLD),
    );

    let selected_pos = filtered
        .iter()
        .position(|(idx, _)| *idx == app.selected_logger_index)
        .unwrap_or(0);

    let mut state = TableState::default();
    state.select(Some(selected_pos));
    f.render_stateful_widget(table, inner_area, &mut state);
}