//! Design system de terminal do CLI SDD.
//!
//! Convenções visuais:
//! - **Banner em caixa** (`╭─ SDD <title> ──… ╰──`) abre comandos de inspeção
//!   (`doctor`, `health`, `list`) e concentra status + contagens.
//! - **Rail de timeline** (`◇`/`◆` + `│`) orienta comandos de execução
//!   (`init`, `sync`, `save`).
//! - **Seções** (`◇ Title (n)`) agrupam detalhamento com árvore `├─└─`.
//! - **Quick start** fecha comandos longos com próximos passos.
//!
//! Todas as funções respeitam `NO_COLOR`, `TERM=dumb` e redirecionamento
//! (não-TTY): quando o estilo está desativado, os glifos continuam (são
//! legíveis em pipe) mas o dim ANSI é suprimido.

use std::io::{self, IsTerminal};
use std::env;

/// Largura interna do banner (conta os `─` entre `╭`/`╰` e o conteúdo).
const BANNER_WIDTH: usize = 38;

pub fn use_terminal_style() -> bool {
    io::stdout().is_terminal()
        && env::var_os("NO_COLOR").is_none()
        && env::var("TERM").map_or(true, |term| term != "dumb")
}

pub fn muted(text: &str) -> String {
    if use_terminal_style() {
        format!("\x1b[2m{text}\x1b[0m")
    } else {
        text.to_string()
    }
}

/// `✓` para pass, `✕` para fail, `⚠` para warn, `◌` para skipped/unknown.
pub fn status_icon(status: &str) -> &'static str {
    match status {
        "pass" => "✓",
        "fail" => "✕",
        "warn" => "⚠",
        "skipped" | "skip" => "◌",
        _ => "◌",
    }
}

/// Sinal humano para a linha de status: `clean`, `attention`, `blocked`,
/// `invalid`, `missing`, `skipped`.
pub fn status_signal(status: &str, has_invalid: bool, has_missing: bool) -> &'static str {
    match status {
        "pass" => {
            if has_invalid {
                "invalid"
            } else if has_missing {
                "missing"
            } else {
                "clean"
            }
        }
        "warn" => "attention",
        "fail" => "blocked",
        "skipped" | "skip" => "skipped",
        _ => "unknown",
    }
}

// ── Banner em caixa ──────────────────────────────────────────────────────

/// Abre `╭─ SDD <title> ────` com largura fixa.
pub fn banner(title: &str) {
    let header = format!("─ SDD {title} ");
    let dashes = BANNER_WIDTH.saturating_sub(header.chars().count());
    println!("╭{header}{}", "─".repeat(dashes));
}

/// Fecha o banner: `╰──────`.
pub fn banner_close() {
    println!("╰{}", "─".repeat(BANNER_WIDTH));
}

/// Linha de status dentro do banner: `│ ✓ PASS        clean`.
pub fn status_line(status: &str, signal: &str) {
    let icon = status_icon(status);
    let upper = status.to_uppercase();
    println!("│ {icon} {upper:<4}        {signal}");
}

/// Linha de resumo dentro do banner: `│ ◇ Label     body`.
pub fn summary_line(label: &str, body: &str) {
    println!("│ ◇ {label:<10} {body}");
}

// ── Seções e árvore ──────────────────────────────────────────────────────

/// Abre uma seção de detalhamento: linha em branco + `◇ Title (count)`.
pub fn section(title: &str, count: usize) {
    println!();
    println!("◇ {title} ({count})");
}

/// Estado vazio de uma seção: `  ✓ none`.
pub fn section_none() {
    println!("  ✓ none");
}

/// Item de seção: `  {icon} {text}`.
pub fn item(icon: &str, text: &str) {
    println!("  {icon} {text}");
}

/// Item de seção com identificador alinhado: `  {icon} {id:<width} {body}`.
pub fn item_labeled(icon: &str, id: &str, width: usize, body: &str) {
    println!("  {icon} {id:<width$} {body}");
}

/// Detalhes em árvore (`├─`/`└─`) com preview opcional e linha `+N more`.
pub fn tree(details: &[String], preview_limit: Option<usize>) {
    let visible = preview_limit
        .map(|limit| details.len().min(limit))
        .unwrap_or(details.len());
    let hidden = details.len().saturating_sub(visible);
    let total_lines = visible + usize::from(hidden > 0);
    for (index, detail) in details.iter().take(visible).enumerate() {
        let branch = if index + 1 == total_lines {
            "└─"
        } else {
            "├─"
        };
        println!("    {branch} {detail}");
    }
    if hidden > 0 {
        println!("    └─ +{hidden} more");
    }
}

/// Pluralização simples: 1 → singular, demais → plural.
pub fn plural<'a>(count: usize, singular: &'a str, plural_str: &'a str) -> &'a str {
    if count == 1 {
        singular
    } else {
        plural_str
    }
}

// ── Rail de timeline (execução) ──────────────────────────────────────────

#[derive(Clone, Copy)]
pub enum RailMarker {
    Hollow,
    Filled,
}

impl RailMarker {
    pub fn symbol(self) -> &'static str {
        match self {
            RailMarker::Hollow => "◇",
            RailMarker::Filled => "◆",
        }
    }
}

pub fn rail_blank() {
    println!("│");
}

pub fn rail_detail(detail: &str) {
    println!("│  {}", muted(detail));
}

pub fn rail_node(marker: RailMarker, label: &str, detail: Option<&str>) {
    println!("{} {label}", marker.symbol());
    if let Some(detail) = detail {
        rail_detail(detail);
    }
    rail_blank();
}

// ── Quick start ──────────────────────────────────────────────────────────

/// Fecha comandos longos com próximos passos.
pub fn quick_start(steps: &[String], done: &str) {
    println!("◇ Quick start ─────────────");
    rail_blank();
    for step in steps {
        rail_detail(step);
    }
    rail_blank();
    println!("└ {done}");
}
