//! ANSI-colored output helpers, mirroring the ok!/err!/hint!/note! pattern
//! used by step2glb and chip-fetcher so log scraping is consistent across
//! the Adom CLI ecosystem. is_terminal()-gated per gallia/skills/
//! adom-cli-design/SKILL.md — never leak raw ANSI to pipes / log files.

#![allow(dead_code)]

use std::io::IsTerminal;
use std::sync::OnceLock;

pub const GREEN: &str = "\x1b[32m";
pub const RED: &str = "\x1b[31m";
pub const YELLOW: &str = "\x1b[33m";
pub const CYAN: &str = "\x1b[36m";
pub const BOLD: &str = "\x1b[1m";
pub const DIM: &str = "\x1b[2m";
pub const RESET: &str = "\x1b[0m";

static STDOUT_IS_TTY: OnceLock<bool> = OnceLock::new();
static STDERR_IS_TTY: OnceLock<bool> = OnceLock::new();

pub fn stdout_is_tty() -> bool {
    *STDOUT_IS_TTY.get_or_init(|| std::io::stdout().is_terminal())
}
pub fn stderr_is_tty() -> bool {
    *STDERR_IS_TTY.get_or_init(|| std::io::stderr().is_terminal())
}

/// Strip CSI sequences like `\x1b[36m`, `\x1b[0m`, `\x1b[1;32m` etc.
/// Used when stdout/stderr isn't a TTY so log files don't get raw escape
/// codes leaked into them.
pub fn strip_ansi(s: &str) -> String {
    let mut out = String::with_capacity(s.len());
    let mut chars = s.chars().peekable();
    while let Some(c) = chars.next() {
        if c == '\x1b' && chars.peek() == Some(&'[') {
            chars.next();
            for c2 in chars.by_ref() {
                if c2.is_ascii_alphabetic() {
                    break;
                }
            }
            continue;
        }
        out.push(c);
    }
    out
}

pub fn ok<S: AsRef<str>>(msg: S) {
    let s = msg.as_ref();
    if stdout_is_tty() {
        println!("{GREEN}{BOLD}OK:{RESET} {s}");
    } else {
        println!("OK: {}", strip_ansi(s));
    }
}
pub fn err<S: AsRef<str>>(msg: S) {
    let s = msg.as_ref();
    if stderr_is_tty() {
        eprintln!("{RED}{BOLD}ERROR:{RESET} {s}");
    } else {
        eprintln!("ERROR: {}", strip_ansi(s));
    }
}
pub fn warn<S: AsRef<str>>(msg: S) {
    let s = msg.as_ref();
    if stderr_is_tty() {
        eprintln!("{YELLOW}{BOLD}WARN:{RESET} {s}");
    } else {
        eprintln!("WARN: {}", strip_ansi(s));
    }
}
pub fn hint<S: AsRef<str>>(msg: S) {
    let s = msg.as_ref();
    if stderr_is_tty() {
        eprintln!("{DIM}Hint:{RESET} {s}");
    } else {
        eprintln!("Hint: {}", strip_ansi(s));
    }
}

#[macro_export]
macro_rules! note {
    ($($arg:tt)*) => {{
        let __s = format!($($arg)*);
        if $crate::ui::stdout_is_tty() {
            println!("{}", __s);
        } else {
            println!("{}", $crate::ui::strip_ansi(&__s));
        }
    }};
}