mod commands;
mod listener;

use ::serenity::all::GatewayIntents;
use dotenvy::dotenv;
use poise::{Framework, FrameworkOptions, serenity_prelude as serenity};
use std::{env, sync::Arc};
use surrealdb::Surreal;
use surrealdb::engine::remote::ws::{Client, Wss};
use surrealdb::opt::auth::Root;
use tracing::info;

type Error = anyhow::Error;
use tracing_subscriber::{EnvFilter, FmtSubscriber};
type Context<'a> = poise::Context<'a, Data, Error>;

struct Data {
    db: Surreal<Client>,
}

#[tokio::main()]
async fn main() -> Result<(), Error> {
    dotenv().ok();

    let subscriber = FmtSubscriber::builder()
        .with_env_filter(
            EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")),
        )
        .finish();

    tracing::subscriber::set_global_default(subscriber)?;

    let token = env::var("DISCORD_TOKEN")?;

    let surreal_address =
        env::var("SURREAL_ADDRESS").expect("Expected SURREAL_ADDRESS in environment");
    let surreal_user = env::var("SURREAL_USER").expect("Expected SURREAL_USER in environment");
    let surreal_pass = env::var("SURREAL_PASS").expect("Expected SURREAL_PASS in environment");
    let surreal_ns = env::var("SURREAL_NS").expect("Expected SURREAL_NS in environment");
    let surreal_db = env::var("SURREAL_DB").expect("Expected SURREAL_DB in environment");

    let db = Surreal::new::<Wss>(&surreal_address).await?;

    db.signin(Root {
        username: &surreal_user,
        password: &surreal_pass,
    })
    .await?;
    info!("Successfully signed in to SurrealDB");

    db.use_ns(&surreal_ns).use_db(&surreal_db).await?;

    let db_clone = db.clone();
    let framework = Framework::builder()
        .options(FrameworkOptions::<Data, Error> {
            commands: vec![
                commands::level::set_level_roles(),
                commands::level::get_level_roles(),
                commands::level::leaderboard(),
                commands::level::set_levelup_message_channel(),
                commands::level::set_levelup_message(),
                commands::level::levelup_role_bridger(),
                commands::fun::say(),
                commands::utility::auto_response(),
                commands::utility::view_auto_responses(),
                commands::utility::delete_auto_response(),
                commands::utility::edit_auto_response(),
            ],
            prefix_options: poise::PrefixFrameworkOptions {
                prefix: Some("!".into()),
                edit_tracker: Some(Arc::new(poise::EditTracker::for_timespan(
                    std::time::Duration::from_secs(600),
                ))),
                case_insensitive_commands: true,
                ..Default::default()
            },
            ..Default::default()
        })
        .setup(move |context, _ready, framework| {
            Box::pin(async move {
                poise::builtins::register_globally(context, &framework.options().commands).await?;
                Ok(Data { db: db_clone })
            })
        })
        .build();

    let mut client = serenity::Client::builder(token, GatewayIntents::all())
        .event_handler(listener::Handler)
        .framework(framework)
        .await?;

    {
        let mut data = client.data.write().await;
        data.insert::<commands::level::DbKey>(db.clone());
        data.insert::<commands::utility::DbKey>(db.clone());
    }

    if let Err(why) = client.start_autosharded().await {
        eprintln!("An error occurred while running the client: {why}");
    }

    Ok(())
}
