use poise::serenity_prelude::model::id::GuildId;
use serde::{Deserialize, Serialize};
use serde_json::Value;

use crate::{
    utils::{Extract, Recordable},
    ApplicationContext, Context,
};

#[derive(Serialize, Deserialize, Recordable)]
#[serde(tag = "command_name")]
pub enum RecordedCommand {
    #[serde(rename = "unset_allowed_dm")]
    UnsetAllowedDm(crate::commands::allowed_dm::unset::Options),
    #[serde(rename = "set_allowed_dm")]
    SetAllowedDm(crate::commands::allowed_dm::set::Options),
    #[serde(rename = "unset_ephemeral_confirmations")]
    UnsetEphemeralConfirmations(crate::commands::settings::ephemeral_confirmations::unset::Options),
    #[serde(rename = "set_ephemeral_confirmations")]
    SetEphemeralConfirmations(crate::commands::settings::ephemeral_confirmations::set::Options),
    #[serde(rename = "timer_delete")]
    TimerDelete(crate::commands::timer::delete::Options),
    #[serde(rename = "timer_list")]
    TimerList(crate::commands::timer::list::Options),
    #[serde(rename = "timer_start")]
    TimerStart(crate::commands::timer::start::Options),
    #[serde(rename = "todo_channel_add")]
    TodoChannelAdd(crate::commands::todo::channel::add::Options),
    #[serde(rename = "todo_channel_view")]
    TodoChannelView(crate::commands::todo::channel::view::Options),
    #[serde(rename = "todo_guild_add")]
    TodoGuildAdd(crate::commands::todo::guild::add::Options),
    #[serde(rename = "todo_guild_view")]
    TodoGuildView(crate::commands::todo::guild::view::Options),
    #[serde(rename = "todo_user_add")]
    TodoUserAdd(crate::commands::todo::user::add::Options),
    #[serde(rename = "todo_user_view")]
    TodoUserView(crate::commands::todo::user::view::Options),
    #[serde(rename = "clock")]
    Clock(crate::commands::clock::Options),
    #[serde(rename = "dashboard")]
    Dashboard(crate::commands::dashboard::Options),
    #[serde(rename = "delete")]
    Delete(crate::commands::delete::Options),
    #[serde(rename = "donate")]
    Donate(crate::commands::patreon::info::Options),
    #[serde(rename = "help")]
    Help(crate::commands::help::Options),
    #[serde(rename = "info")]
    Info(crate::commands::info::Options),
    #[serde(rename = "look")]
    Look(crate::commands::look::Options),
    #[serde(rename = "multiline")]
    Multiline(crate::commands::multiline::Options),
    #[serde(rename = "nudge")]
    Nudge(crate::commands::nudge::Options),
    #[serde(rename = "offset")]
    Offset(crate::commands::offset::Options),
    #[serde(rename = "pause")]
    Pause(crate::commands::pause::Options),
    #[serde(rename = "remind")]
    Remind(crate::commands::remind::Options),
    #[serde(rename = "timezone")]
    Timezone(crate::commands::timezone::Options),
    #[serde(rename = "webhook")]
    Webhook(crate::commands::webhook::Options),
}

impl RecordedCommand {
    pub fn from_context(ctx: ApplicationContext) -> Option<Self> {
        match ctx.command().identifying_name.as_str() {
            "unset_allowed_dm" => Some(Self::UnsetAllowedDm(
                crate::commands::allowed_dm::unset::Options::extract(ctx),
            )),
            "set_allowed_dm" => {
                Some(Self::SetAllowedDm(crate::commands::allowed_dm::set::Options::extract(ctx)))
            }
            "unset_ephemeral_confirmations" => Some(Self::UnsetEphemeralConfirmations(
                crate::commands::settings::ephemeral_confirmations::unset::Options::extract(ctx),
            )),
            "set_ephemeral_confirmations" => Some(Self::SetEphemeralConfirmations(
                crate::commands::settings::ephemeral_confirmations::set::Options::extract(ctx),
            )),
            "timer_delete" => {
                Some(Self::TimerDelete(crate::commands::timer::delete::Options::extract(ctx)))
            }
            "timer_list" => {
                Some(Self::TimerList(crate::commands::timer::list::Options::extract(ctx)))
            }
            "timer_start" => {
                Some(Self::TimerStart(crate::commands::timer::start::Options::extract(ctx)))
            }
            "todo_channel_add" => Some(Self::TodoChannelAdd(
                crate::commands::todo::channel::add::Options::extract(ctx),
            )),
            "todo_channel_view" => Some(Self::TodoChannelView(
                crate::commands::todo::channel::view::Options::extract(ctx),
            )),
            "todo_guild_add" => {
                Some(Self::TodoGuildAdd(crate::commands::todo::guild::add::Options::extract(ctx)))
            }
            "todo_guild_view" => {
                Some(Self::TodoGuildView(crate::commands::todo::guild::view::Options::extract(ctx)))
            }
            "todo_user_add" => {
                Some(Self::TodoUserAdd(crate::commands::todo::user::add::Options::extract(ctx)))
            }
            "todo_user_view" => {
                Some(Self::TodoUserView(crate::commands::todo::user::view::Options::extract(ctx)))
            }
            "clock" => Some(Self::Clock(crate::commands::clock::Options::extract(ctx))),
            "dashboard" => Some(Self::Dashboard(crate::commands::dashboard::Options::extract(ctx))),
            "delete" => Some(Self::Delete(crate::commands::delete::Options::extract(ctx))),
            "donate" => Some(Self::Donate(crate::commands::patreon::info::Options::extract(ctx))),
            "help" => Some(Self::Help(crate::commands::help::Options::extract(ctx))),
            "info" => Some(Self::Info(crate::commands::info::Options::extract(ctx))),
            "look" => Some(Self::Look(crate::commands::look::Options::extract(ctx))),
            "multiline" => Some(Self::Multiline(crate::commands::multiline::Options::extract(ctx))),
            "nudge" => Some(Self::Nudge(crate::commands::nudge::Options::extract(ctx))),
            "offset" => Some(Self::Offset(crate::commands::offset::Options::extract(ctx))),
            "pause" => Some(Self::Pause(crate::commands::pause::Options::extract(ctx))),
            "remind" => Some(Self::Remind(crate::commands::remind::Options::extract(ctx))),
            "timezone" => Some(Self::Timezone(crate::commands::timezone::Options::extract(ctx))),
            "webhook" => Some(Self::Webhook(crate::commands::webhook::Options::extract(ctx))),
            _ => None,
        }
    }
}

pub struct CommandMacro {
    pub guild_id: GuildId,
    pub name: String,
    pub description: Option<String>,
    pub commands: Vec<RecordedCommand>,
}

pub async fn guild_command_macro(ctx: &Context<'_>, name: &str) -> Option<CommandMacro> {
    struct Row {
        name: String,
        description: Option<String>,
        commands: Value,
    }

    let row = sqlx::query_as!(
        Row,
        "
        SELECT m.name, m.description, m.commands
        FROM command_macro m
        INNER JOIN guilds g
            ON g.id = m.guild_id
        WHERE guild = ?
            AND m.name = ?
        ",
        ctx.guild_id().unwrap().get(),
        name
    )
    .fetch_one(&ctx.data().database)
    .await
    .ok()?;

    let command_macro = CommandMacro {
        guild_id: ctx.guild_id().unwrap(),
        name: row.name,
        description: row.description,
        commands: serde_json::from_value(row.commands).unwrap(),
    };

    Some(command_macro)
}
