use serde::{Deserialize, Serialize};

use crate::{
    consts::{HOUR, MINUTE},
    utils::{Extract, Recordable},
    Context, Error,
};

#[derive(Serialize, Deserialize, Extract)]
pub struct Options {
    hours: Option<i64>,
    minutes: Option<i64>,
    seconds: Option<i64>,
}

impl Recordable for Options {
    async fn run(self, ctx: Context<'_>) -> Result<(), Error> {
        ctx.defer().await?;

        let combined_time = self.hours.map_or(0, |h| h * HOUR as i64)
            + self.minutes.map_or(0, |m| m * MINUTE as i64)
            + self.seconds.map_or(0, |s| s);

        if combined_time == 0 {
            ctx.say("Please specify one of `hours`, `minutes` or `seconds`").await?;
        } else {
            if let Some(channels) = ctx.guild().map(|guild| {
                guild
                    .channels
                    .iter()
                    .filter(|(_, channel)| channel.is_text_based())
                    .map(|(id, _)| id.get().to_string())
                    .collect::<Vec<String>>()
            }) {
                let placeholder = vec!["?"; channels.len()].join(",");
                let sql = format!(
                    "
                    UPDATE reminders
                    INNER JOIN `channels`
                        ON `channels`.id = reminders.channel_id
                    SET reminders.`utc_time` = DATE_ADD(reminders.`utc_time`, INTERVAL ? SECOND)
                    WHERE channels.`channel` IN ({placeholder})
                    "
                );

                let mut query = sqlx::query(&sql).bind(combined_time);
                for channel in channels {
                    query = query.bind(channel);
                }

                query.execute(&ctx.data().database).await.unwrap();
            } else {
                sqlx::query!(
                    "
                    UPDATE reminders
                    INNER JOIN `channels`
                        ON `channels`.id = reminders.channel_id
                    SET reminders.`utc_time` = reminders.`utc_time` + ?
                    WHERE channels.`channel` = ?
                    ",
                    combined_time as i64,
                    ctx.channel_id().get()
                )
                .execute(&ctx.data().database)
                .await
                .unwrap();
            }

            ctx.say(format!("All reminders offset by {} seconds", combined_time)).await?;
        }

        Ok(())
    }
}

/// Move all reminders in the current server by a certain amount of time. Times get added together
#[poise::command(
    slash_command,
    rename = "offset",
    identifying_name = "offset",
    default_member_permissions = "MANAGE_GUILD"
)]
pub async fn command(
    ctx: Context<'_>,
    #[description = "Number of hours to offset by"] hours: Option<i64>,
    #[description = "Number of minutes to offset by"] minutes: Option<i64>,
    #[description = "Number of seconds to offset by"] seconds: Option<i64>,
) -> Result<(), Error> {
    (Options { hours, minutes, seconds }).run(ctx).await
}
