use aiken::cmd::{
    blueprint::{self, address},
    build, check, completion, docs, fmt, lsp, new,
    packages::{self, add},
    tx, uplc, Cmd,
};

use owo_colors::OwoColorize;

fn main() -> miette::Result<()> {
    panic_handler();

    match Cmd::default() {
        Cmd::New(args) => new::exec(args),
        Cmd::Fmt(args) => fmt::exec(args),
        Cmd::Build(args) => build::exec(args),
        Cmd::Address(args) => address::exec(args),
        Cmd::Check(args) => check::exec(args),
        Cmd::Docs(args) => docs::exec(args),
        Cmd::Add(args) => add::exec(args),
        Cmd::Blueprint(args) => blueprint::exec(args),
        Cmd::Packages(args) => packages::exec(args),
        Cmd::Lsp(args) => lsp::exec(args),
        Cmd::Tx(sub_cmd) => tx::exec(sub_cmd),
        Cmd::Uplc(sub_cmd) => uplc::exec(sub_cmd),
        Cmd::Completion(sub_cmd) => completion::exec(sub_cmd),
    }
}

fn panic_handler() {
    std::panic::set_hook(Box::new(move |info| {
        let message = info
            .payload()
            .downcast_ref::<&str>()
            .map(|s| (*s).to_string())
            .or_else(|| {
                info.payload()
                    .downcast_ref::<String>()
                    .map(|s| s.to_string())
            })
            .unwrap_or_else(|| "unknown error".to_string());

        let location = info.location().map_or_else(
            || "".into(),
            |location| {
                format!(
                    "{}:{}:{}\n\n    ",
                    location.file(),
                    location.line(),
                    location.column(),
                )
            },
        );

        let error_message = indoc::formatdoc! {
            r#"{fatal}

                Whoops! You found a bug in the Aiken compiler.

                Please report this error at https://github.com/aiken-lang/aiken/issues/new.
                In your bug report please provide the information below and if possible the code
                that produced it.

                    {location}{message}"#,
            fatal = "aiken::fatal::error".red().bold(),
            location = location.purple(),
        };

        println!("{error_message}")
    }));
}
