use std::fmt::Display;
use std::io::{IsTerminal, Write};
use termcolor::ColorChoice;
use termcolor::WriteColor;
use termcolor::{ColorSpec, StandardStream};

pub use termcolor::Color;

pub fn is_stderr_terminal() -> bool {
    std::io::stderr().is_terminal()
}

fn print_color(
    stream: &mut StandardStream,
    level: impl Display,
    message: impl Display,
    color: Color,
) -> std::io::Result<()> {
    stream.reset()?;
    stream.set_color(ColorSpec::new().set_bold(true).set_fg(Some(color)))?;
    write!(stream, "{level}")?;
    stream.set_color(ColorSpec::new().set_bold(true))?;
    write!(stream, ":")?;
    stream.reset()?;
    writeln!(stream, " {message}")?;
    stream.flush()
}

fn print_nocolor(
    stream: &mut StandardStream,
    level: impl Display,
    message: impl Display,
) -> std::io::Result<()> {
    stream.reset()?;
    writeln!(stream, "{level}: {message}")?;
    stream.flush()
}

pub fn print_stderr(level: impl Display, message: impl Display, color: Color) {
    let mut stream = StandardStream::stderr(ColorChoice::Auto);

    match is_stderr_terminal() {
        true => {
            print_color(&mut stream, level, message, color).expect("error writing to stderr");
        }
        false => {
            print_nocolor(&mut stream, level, message).expect("error writing to stderr");
        }
    }
}

macro_rules! print_error {
    ($f:literal, $($arg:tt)*) => {{
        $crate::console::print_stderr(
            "error",
            ::std::format!($f, $($arg)*),
            $crate::console::Color::Red,
        );
    }};
}

macro_rules! print_warning {
    ($f:literal, $($arg:tt)*) => {{
        $crate::console::print_stderr(
            "warning",
            ::std::format!($f, $($arg)*),
            $crate::console::Color::Yellow,
        );
    }};
}

macro_rules! print_info {
    ($f:literal, $($arg:tt)*) => {{
        println!($f, $($arg)*);
    }};
}
