use crate::{Context, Error};
use crate::shared_functions::get_all_admin_users;
use poise::serenity_prelude as sere;
use tracing::{error, info, debug, trace};

#[derive(Debug, poise::ChoiceParameter)]
pub enum AutoRoleOperationType {
    #[name = "Enable auto-role"]
    EnableAutoRole,
    #[name = "Set auto-role (does not enable the feature)"]
    SetAutoRole,
    #[name = "Disable auto-role"]
    DisableAutoRole,
    #[name = "Show auto-role"]
    ShowAutoRole,
}

/// Auto role command
#[poise::command(slash_command, prefix_command)]
pub async fn manage_auto_role(ctx: Context<'_>,
    #[description = "Operation to perform"]
    operation: Option<AutoRoleOperationType>,
    #[description = "Role"]
    role: Option<sere::Role>
) -> Result<(), Error> {
    trace!("manage_auto_role command called by user {}", ctx.author().id.get());
    let response_message = ctx.say("Running command...").await?;
    
    if !is_admin(ctx).await? {
        debug!("User {} is not an admin, denying access", ctx.author().id.get());
        edit_response_message(&response_message, ctx, "You are not allowed to run this command.".to_string(), false).await?;
        return Ok(());
    }

    match operation {
        Some(AutoRoleOperationType::EnableAutoRole) => {
            if let Some(s_role) = role {
                enable_auto_role(ctx, &response_message, s_role).await?;
            } else {
                edit_response_message(&response_message, ctx, "Please provide a role".to_string(), false).await?;
            }
        }
        Some(AutoRoleOperationType::SetAutoRole) => {
            if let Some(s_role) = role {
                set_auto_role(ctx, &response_message, s_role).await?;
            } else {
                edit_response_message(&response_message, ctx, "Please provide a role".to_string(), false).await?;
            }
        }
        Some(AutoRoleOperationType::DisableAutoRole) => {
            if let Some(s_role) = role {
                disable_auto_role(ctx, &response_message, s_role).await?;
            } else {
                edit_response_message(&response_message, ctx, "Please provide a role".to_string(), false).await?;
            }
        }
        Some(AutoRoleOperationType::ShowAutoRole) => {
            show_auto_role(ctx, &response_message).await?;
        }
        None => {
            edit_response_message(&response_message, ctx, "Please provide an operation".to_string(), false).await?;
        }
    }
    Ok(())
}

async fn is_admin(ctx: Context<'_>) -> Result<bool, Error> {
    let user_running_command = ctx.author().id.get() as i64;
    let admins = get_all_admin_users(ctx).await?;
    
    for user in admins {
        if user.id.get() as i64 == user_running_command {
            return Ok(true);
        }
    }
    Ok(false)
}

async fn enable_auto_role(ctx: Context<'_>, response_message: &poise::ReplyHandle<'_>, role: sere::Role) -> Result<(), Error> {
    trace!("Executing enable_auto_role for role {}", role.name);
    if !is_any_role_set_in_guild(ctx).await? {
        trace!("No role is set in guild {}", ctx.guild_id().unwrap().get());
        edit_response_message(response_message, ctx, "No role is set. Please set a role first.".to_string(), false).await?;
        return Ok(());
    }
    if !does_role_exist_in_guild(ctx, &role).await? {
        trace!("Role {} does not exist in guild {}", role.name, ctx.guild_id().unwrap().get());
        edit_response_message(response_message, ctx, "This role does not exist".to_string(), false).await?;
        return Ok(());
    }
    if is_auto_role_enabled(ctx, &role).await? {
        trace!("Auto role {} is already enabled in guild {}", role.name, ctx.guild_id().unwrap().get());
        edit_response_message(response_message, ctx, "Auto role is already enabled".to_string(), false).await?;
        return Ok(());
    }
    
    let query = sqlx::query!("UPDATE auto_roles SET enabled = true WHERE guild_id = $1 AND role_id = $2;", ctx.guild_id().unwrap().get() as i64, role.id.get() as i64).execute(&ctx.data().pool).await;
    match query {
        Ok(_) => {
            debug!("Enabled auto role {} in guild {}", role.name, ctx.guild_id().unwrap().get());
            edit_response_message(response_message, ctx, format!("Enabled auto role: <@&{}>", role.id.get()), false).await?;
        }
        Err(e) => {
            error!("Failed to enable {} as auto role in guild {}: {}", role.id.get(), ctx.guild_id().unwrap().get(), e);
            edit_response_message(response_message, ctx, format!("Failed to enable {} as auto role: {}", role.id.get(), e), false).await?;
        }
    }
    
    Ok(())
}

async fn is_any_role_set_in_guild(ctx: Context<'_>) -> Result<bool, Error> {
    let query = sqlx::query!("SELECT * FROM auto_roles WHERE guild_id = $1", ctx.guild_id().unwrap().get() as i64).fetch_optional(&ctx.data().pool).await?;
    if query.is_some() {
        return Ok(true);
    }
    return Ok(false)
}

async fn does_role_exist_in_guild(ctx: Context<'_>, target_role: &sere::Role) -> Result<bool, Error> {
    if let Some(guild) = ctx.guild() {
        if guild.roles.contains_key(&target_role.id) {
            return Ok(true);
        }
    }
    return Ok(false)
}

async fn set_auto_role(ctx: Context<'_>, response_message: &poise::ReplyHandle<'_>, role: sere::Role) -> Result<(), Error> {
    trace!("Executing set_auto_role for role {}", role.name);
    if !does_role_exist_in_guild(ctx, &role).await? {
        trace!("Role {} does not exist in guild {}", role.name, ctx.guild_id().unwrap().get());
        edit_response_message(response_message, ctx, "This role does not exist".to_string(), false).await?;
        return Ok(());
    }
    
    let query = sqlx::query!("INSERT INTO auto_roles (guild_id, role_id, enabled) VALUES ($1, $2, false) ON CONFLICT (guild_id) DO UPDATE SET role_id = $2;", ctx.guild_id().unwrap().get() as i64, role.id.get() as i64).execute(&ctx.data().pool).await;
    match query {
        Ok(_) => {
            debug!("Set {} as auto role in guild {}", role.name, ctx.guild_id().unwrap().get());
            edit_response_message(response_message, ctx, format!("Set {} as auto role", role.name), false).await?;
        }
        Err(e) => {
            error!("Failed to set {} as auto role in guild {}: {}", role.id.get(), ctx.guild_id().unwrap().get(), e);
            edit_response_message(response_message, ctx, format!("Failed to set {} as auto role: {}", role.id.get(), e), false).await?;
        }
    }
    
    Ok(())
}

async fn disable_auto_role(ctx: Context<'_>, response_message: &poise::ReplyHandle<'_>, role: sere::Role) -> Result<(), Error> {
    trace!("Executing disable_auto_role for role {}", role.name);
    if !is_any_role_set_in_guild(ctx).await? {
        trace!("No role is set in guild {}", ctx.guild_id().unwrap().get());
        edit_response_message(response_message, ctx, "No auto role is set. Please set a role first.".to_string(), false).await?;
        return Ok(());
    }
    if !is_auto_role_enabled(ctx, &role).await? {
        trace!("Auto role {} is already disabled in guild {}", role.name, ctx.guild_id().unwrap().get());
        edit_response_message(response_message, ctx, "Auto role is already disabled".to_string(), false).await?;
        return Ok(());
    }
    
    let query = sqlx::query!("UPDATE auto_roles SET enabled = false WHERE guild_id = $1 AND role_id = $2;", ctx.guild_id().unwrap().get() as i64, role.id.get() as i64).execute(&ctx.data().pool).await;
    match query {
        Ok(_) => {
            debug!("Disabled auto role {} in guild {}", role.name, ctx.guild_id().unwrap().get());
            edit_response_message(response_message, ctx, format!("Disabled auto role: {}", role.name), false).await?;
        }
        Err(e) => {
            error!("Failed to disable {} as auto role in guild {}: {}", role.id.get(), ctx.guild_id().unwrap().get(), e);
            edit_response_message(response_message, ctx, format!("Failed to disable {} as auto role: {}", role.id.get(), e), false).await?;
        }
    }
    
    Ok(())
}

async fn edit_response_message<'a>(response_message: &poise::ReplyHandle<'a>, ctx: Context<'_>, content: String, silent: bool) -> Result<(), Error> {
    if silent {
        response_message.edit(ctx, poise::CreateReply::default().content(content).allowed_mentions(serenity::all::CreateAllowedMentions::new().empty_users())).await?;
    } else {
        response_message.edit(ctx, poise::CreateReply::default().content(content)).await?;
    }
    Ok(())
}

async fn is_auto_role_enabled(ctx: Context<'_>, target_role: &sere::Role) -> Result<bool, Error> {
    let query = sqlx::query!("SELECT enabled FROM auto_roles WHERE guild_id = $1 AND role_id = $2;", ctx.guild_id().unwrap().get() as i64, target_role.id.get() as i64).fetch_optional(&ctx.data().pool).await?;
    if query.is_none() {
        return Ok(false);
    }
    return Ok(query.unwrap().enabled)
}

async fn is_any_auto_role_enabled(ctx: Context<'_>) -> Result<bool, Error> {
    let query = sqlx::query!("SELECT role_id FROM auto_roles WHERE guild_id = $1 AND enabled = true", ctx.guild_id().unwrap().get() as i64).fetch_optional(&ctx.data().pool).await?;
    if query.is_none() {
        return Ok(false);
    }
    return Ok(true)
}

async fn show_auto_role(ctx: Context<'_>, response_message: &poise::ReplyHandle<'_>) -> Result<(), Error> {
    trace!("Executing show_auto_role");
    if !is_any_role_set_in_guild(ctx).await? {
        trace!("No role is set in guild {}", ctx.guild_id().unwrap().get());
        edit_response_message(response_message, ctx, "No role has been set yet.".to_string(), false).await?;
        return Ok(());
    }
    if !is_any_auto_role_enabled(ctx).await? {
        trace!("Auto role is disabled in guild {}", ctx.guild_id().unwrap().get());
        edit_response_message(response_message, ctx, "Auto role is disabled.".to_string(), false).await?;
        return Ok(());
    }
    let role_id = sqlx::query!("SELECT role_id FROM auto_roles WHERE guild_id = $1", ctx.guild_id().unwrap().get() as i64).fetch_optional(&ctx.data().pool).await?;
    edit_response_message(response_message, ctx, format!("Currently, <@&{}> is set as auto role and is enabled.", role_id.unwrap().role_id), false).await?;
    Ok(())
}

// async fn list_admin_users<'a>(ctx: Context<'a>, msg: &poise::ReplyHandle<'a>) -> Result<(), Error> {
//     let admins = get_all_admin_users(ctx).await?;
//     let admin_strings: Vec<String> = admins.iter().map(|user| format!("<@{}>", user.id.get())).collect();
//     edit_response_message(msg, ctx, format!("Admin users: {}", admin_strings.join(", ")), true).await?;
//     Ok(())
// }
