use std::env;
use poise::serenity_prelude as sere;
use tracing::{info, error, debug, trace};
use tracing_subscriber::EnvFilter;
pub mod commands;
pub mod event_handler;
pub mod events;
pub mod shared_functions;

pub struct Data {
    pub pool: sqlx::PgPool,
}

// Define standard types for Poise context, commands, and errors.
pub type Error = Box<dyn std::error::Error + Send + Sync>;
pub type Context<'a> = poise::Context<'a, Data, Error>;



#[tokio::main]
async fn main() {
    dotenv::dotenv().ok();

    let log_level = env::var("TG_BOT_LOG").unwrap_or_else(|_| "info".to_string());
    let filter = EnvFilter::new(format!("tg_dev_srv_bot={}", log_level));

    tracing_subscriber::fmt()
        .with_env_filter(filter)
        .with_file(true)
        .with_thread_ids(true)
        .with_thread_names(true)
        .with_line_number(true)
        .with_target(true)
        .init();

    trace!("Logging system started");
    info!("Log Level: {}", log_level);

    trace!("Env loaded");

    // Grab envs
    trace!("Loading database url");
    let database_url = env::var("TG_BOT_DATABASE_URL").expect("Expected a database url in the environment");
    trace!("Database url loaded");

    trace!("Loading token");
    let token = env::var("TG_BOT_DISCORD_TOKEN").expect("Expected a token in the environment");
    trace!("Token loaded");

    

    info!("Starting bot...");

    // Initialize database
    debug!("Initalizing database");
    let pool = sqlx::postgres::PgPoolOptions::new().connect(&database_url).await.expect("Failed to connect to database");
    debug!("Database connected");

    trace!("Applying migrations");
    sqlx::migrate!()
        .run(&pool)
        .await
        .expect("Failed to apply database migrations");
    debug!("Migrations applied");

    trace!("Loading intents");
    let intents = sere::GatewayIntents::non_privileged()
        | sere::GatewayIntents::MESSAGE_CONTENT
        | sere::GatewayIntents::GUILD_MEMBERS;
    trace!("Intents loaded");

    trace!("Initalizing framework");
    let framework = poise::Framework::builder()
        .options(poise::FrameworkOptions {
            event_handler: |ctx, event, framework, user_data| {
                Box::pin(event_handler::event_handler(ctx, event, framework, user_data))
            },
            on_error: |error| Box::pin(async move {
                match error {
                    poise::FrameworkError::Command { error, ctx, .. } => {
                        error!("Error in command `{}`: {:?}", ctx.command().name, error);
                    }
                    error => {
                        if let Err(e) = poise::builtins::on_error(error).await {
                            error!("Error while handling error: {}", e);
                        }
                    }
                }
            }),
            commands: vec![commands::ping(), commands::calc(), commands::manage_admins(), commands::manage_auto_role(), commands::version()],
            ..Default::default()
        })
        .setup(|ctx, _ready, framework| {
            Box::pin(async move {
                let guild_id = sere::GuildId::new(1522685547641045002);
                //poise::builtins::register_globally(ctx, &framework.options().commands).await?;
                poise::builtins::register_in_guild(ctx, &framework.options().commands, guild_id).await?;
                Ok(Data { pool: pool.clone() })
            })
        })
        .build();
    trace!("Framework initialized");

    debug!("Connecting to Discord");
    let mut client = sere::ClientBuilder::new(&token, intents).framework(framework).await.expect("Err creating client");
    debug!("Connected to Discord");

    info!("Bot running");
    if let Err(why) = client.start().await {
        error!("Client error: {why:?}");
    }
}