#![warn(clippy::str_to_string)]
#![feature(iter_map_windows)]
#![feature(let_chains)]
#![feature(array_try_map)]
#![feature(async_closure)]
#![feature(try_blocks)]
#![feature(thread_local)]
#![feature(generic_arg_infer)]
#![feature(lazy_cell_consume)]
#![feature(iter_collect_into)]

mod arcaea;
mod assets;
mod bitmap;
mod cli;
mod commands;
mod context;
mod levenshtein;
mod logs;
mod recognition;
mod time;
mod transform;
mod user;

use arcaea::play::generate_missing_scores;
use clap::Parser;
use cli::{Cli, Command};
use context::{Error, UserContext};
use poise::serenity_prelude::{self as serenity};
use std::{env::var, sync::Arc, time::Duration};

// {{{ Error handler
async fn on_error(error: poise::FrameworkError<'_, UserContext, Error>) {
	match error {
		error => {
			if let Err(e) = poise::builtins::on_error(error).await {
				println!("Error while handling error: {}", e)
			}
		}
	}
}
// }}}

#[tokio::main]
async fn main() {
	let cli = Cli::parse();
	match cli.command {
		Command::Discord {} => {
			// {{{ Poise options
			let options = poise::FrameworkOptions {
				commands: vec![
					commands::help(),
					commands::score::score(),
					commands::stats::stats(),
					commands::chart::chart(),
				],
				prefix_options: poise::PrefixFrameworkOptions {
					stripped_dynamic_prefix: Some(|_ctx, message, _user_ctx| {
						Box::pin(async {
							if message.author.bot || Into::<u64>::into(message.author.id) == 1 {
								Ok(None)
							} else if message.content.starts_with("!") {
								Ok(Some(message.content.split_at(1)))
							} else if message.guild_id.is_none() {
								if message.content.trim().len() == 0 {
									Ok(Some(("", "score magic")))
								} else {
									Ok(Some(("", &message.content[..])))
								}
							} else {
								Ok(None)
							}
						})
					}),
					edit_tracker: Some(Arc::new(poise::EditTracker::for_timespan(
						Duration::from_secs(3600),
					))),
					..Default::default()
				},
				on_error: |error| Box::pin(on_error(error)),
				..Default::default()
			};
			// }}}
			// {{{ Start poise
			let framework = poise::Framework::builder()
				.setup(move |ctx, _ready, framework| {
					Box::pin(async move {
						println!("Logged in as {}", _ready.user.name);
						poise::builtins::register_globally(ctx, &framework.options().commands)
							.await?;
						let ctx = UserContext::new().await?;

						if var("SHIMMERING_REGEN_SCORES").unwrap_or_default() == "1" {
							timed!("generate_missing_scores", {
								generate_missing_scores(&ctx).await?;
							});
						}

						Ok(ctx)
					})
				})
				.options(options)
				.build();

			let token = var("SHIMMERING_DISCORD_TOKEN")
				.expect("Missing `SHIMMERING_DISCORD_TOKEN` env var");
			let intents = serenity::GatewayIntents::non_privileged()
				| serenity::GatewayIntents::MESSAGE_CONTENT;

			let client = serenity::ClientBuilder::new(token, intents)
				.framework(framework)
				.await;

			client.unwrap().start().await.unwrap()
			// }}}
		}
		Command::PrepareJackets {} => {
			cli::prepare_jackets::run().expect("Could not prepare jackets");
		}
		Command::Analyse(args) => {
			cli::analyse::run(args)
				.await
				.expect("Could not analyse screenshot");
		}
	}
}