mod command;
mod tombi_log;

use clap::{
    Parser,
    builder::styling::{AnsiColor, Color, Style},
};

#[derive(clap::Parser)]
#[command(
    name="tombi",
    about = app_about(),
    version = app_version(),
    styles=app_styles(),
    disable_help_subcommand(true),
)]
pub struct Args {
    #[command(subcommand)]
    pub subcommand: command::TomlCommand,

    #[command(flatten)]
    verbosity: tombi_cli_options::Verbosity,
}

impl<I, T> From<I> for Args
where
    I: IntoIterator<Item = T>,
    T: Into<std::ffi::OsString> + Clone,
{
    #[inline]
    fn from(value: I) -> Self {
        Self::parse_from(value)
    }
}

#[derive(clap::Args, Debug)]
struct CommonArgs {
    /// Disable network access
    ///
    /// Don't fetch from remote and use local schemas cache.
    #[clap(long, global = true, env("TOMBI_OFFLINE"))]
    offline: bool,

    /// Do not use cache
    ///
    /// Fetch the latest data from remote and save it to the cache
    #[clap(long, global = true, env("TOMBI_NO_CACHE"))]
    no_cache: bool,
}

pub fn run(args: impl Into<Args>) -> Result<(), crate::Error> {
    let args: Args = args.into();
    let log_level = args.verbosity.log_level();
    let show_trace_location = log_level == log::LevelFilter::Trace;

    let use_ansi_color = use_ansi_color();
    env_logger::Builder::new()
        .filter_level(log::LevelFilter::Warn)
        .filter_module("tombi", log_level)
        .filter_module("tombi_", log_level)
        .filter_module("serde_tombi", log_level)
        .format(tombi_log::format(use_ansi_color, show_trace_location))
        .target(env_logger::Target::Stderr)
        .init();

    match args.subcommand {
        command::TomlCommand::Format(args) => command::format::run(args),
        command::TomlCommand::Lint(args) => command::lint::run(args),
        command::TomlCommand::Lsp(args) => command::lsp::run(args),
        command::TomlCommand::Completion(args) => command::completion::run(args),
    }
}

fn app_about() -> String {
    let title = "Tombi";
    let title_style = Style::new()
        .bold()
        .bg_color(Some(Color::Ansi(AnsiColor::Blue)))
        .fg_color(Some(Color::Ansi(AnsiColor::White)));

    let desc_style = Style::new()
        .bg_color(Some(Color::Ansi(AnsiColor::Blue)))
        .fg_color(Some(Color::Ansi(AnsiColor::White)));

    format!(
        "{title_style}                          {title} {title_style:#}{desc_style}: TOML Toolkit                          {desc_style:#}"
    )
}

fn app_version() -> String {
    format!(
        "{} ({})",
        env!("__TOMBI_VERSION").trim_start_matches('v'),
        env!("__TOMBI_TARGET_TRIPLE"),
    )
}

const fn app_styles() -> clap::builder::Styles {
    clap::builder::Styles::plain()
        .header(
            Style::new()
                .bold()
                .fg_color(Some(Color::Ansi(AnsiColor::Blue))),
        )
        .error(
            Style::new()
                .bold()
                .fg_color(Some(Color::Ansi(AnsiColor::Red))),
        )
        .usage(
            Style::new()
                .bold()
                .fg_color(Some(Color::Ansi(AnsiColor::Blue))),
        )
        .literal(
            Style::new()
                .bold()
                .fg_color(Some(Color::Ansi(AnsiColor::Cyan))),
        )
        .placeholder(Style::new().fg_color(Some(Color::Ansi(AnsiColor::Cyan))))
        .valid(
            Style::new()
                .bold()
                .fg_color(Some(Color::Ansi(AnsiColor::Green))),
        )
        .invalid(
            Style::new()
                .bold()
                .fg_color(Some(Color::Ansi(AnsiColor::Red))),
        )
}

fn use_ansi_color() -> bool {
    std::env::var("TOMBI_NO_COLOR")
        .or_else(|_| std::env::var("NO_COLOR"))
        .map_or(true, |v| v.is_empty())
}

fn printer() -> tombi_diagnostic::printer::Pretty {
    tombi_diagnostic::printer::Pretty {
        use_ansi_color: use_ansi_color(),
    }
}
