//! ASCII table rendering via the `tabled` crate and row-slot utilities.

use tabled::builder::Builder;
use tabled::settings::Alignment;
use tabled::settings::Modify;
use tabled::settings::Style;
use tabled::settings::object::Rows;
use tabled::settings::style::HorizontalLine;

use super::options::RowDisplay;

/// A slot in a preview: either a concrete row index or an ellipsis separator.
#[derive(Clone, Copy)]
pub enum PreviewSlot {
    Index(usize),
    Ellipsis,
}

/// Compute the row slots to show when rendering at most `max_rows` out of
/// `total`, using `row_display` to decide whether to use head or head+tail.
pub fn preview_slots(total: usize, max_rows: usize, row_display: RowDisplay) -> Vec<PreviewSlot> {
    if total == 0 || max_rows == 0 {
        return Vec::new();
    }

    if total <= max_rows {
        return (0..total).map(PreviewSlot::Index).collect();
    }

    match row_display {
        RowDisplay::Head => {
            let mut slots = (0..max_rows).map(PreviewSlot::Index).collect::<Vec<_>>();
            slots.push(PreviewSlot::Ellipsis);
            slots
        }
        RowDisplay::HeadTail if max_rows == 1 => {
            vec![PreviewSlot::Index(0), PreviewSlot::Ellipsis]
        }
        RowDisplay::HeadTail => {
            let head = max_rows.div_ceil(2);
            let tail = max_rows / 2;
            let mut slots = (0..head).map(PreviewSlot::Index).collect::<Vec<_>>();
            slots.push(PreviewSlot::Ellipsis);
            let tail_start = total.saturating_sub(tail);
            slots.extend((tail_start..total).map(PreviewSlot::Index));
            slots
        }
    }
}

/// Build an ASCII table with a modern style and a horizontal line after the
/// header row.
pub fn render_table(headers: Vec<String>, rows: Vec<Vec<String>>) -> String {
    let mut builder = Builder::default();
    builder.push_record(headers);
    for row in rows {
        builder.push_record(row);
    }
    let mut table = builder.build();
    table.with(
        Style::modern()
            .remove_horizontal()
            .horizontals([(1, HorizontalLine::inherit(Style::modern()))]),
    );
    table.with(Modify::new(Rows::first()).with(Alignment::left()));
    table.to_string()
}

/// Assemble a full repr block: optional title lines, a table, optional footer.
pub fn render_preview(
    title_lines: Vec<String>,
    headers: Vec<String>,
    rows: Vec<Vec<String>>,
    footer_lines: Vec<String>,
) -> String {
    let mut sections = title_lines;
    sections.push(render_table(headers, rows));
    sections.extend(footer_lines);
    sections.join("\n")
}
