use chrono::NaiveDateTime;
use poise::serenity_prelude::{model::channel::Channel, CacheHttp, ChannelId, CreateWebhook};
use secrecy::ExposeSecret;
use sqlx::MySqlPool;

use crate::{consts::DEFAULT_AVATAR, Error};

pub struct ChannelData {
    pub id: u32,
    pub channel: u64,
    pub nudge: i16,
    pub webhook_id: Option<u64>,
    pub webhook_token: Option<String>,
    pub paused: bool,
    pub paused_until: Option<NaiveDateTime>,
}

impl ChannelData {
    pub async fn from_channel(
        channel: &Channel,
        pool: &MySqlPool,
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
        let channel_id = channel.id().get().to_owned();

        if let Ok(c) = sqlx::query_as_unchecked!(
            Self,
            "
            SELECT id, channel, nudge, webhook_id, webhook_token, paused,
                paused_until
            FROM channels
            WHERE channel = ?
            ",
            channel_id
        )
        .fetch_one(pool)
        .await
        {
            Ok(c)
        } else {
            let props = channel.to_owned().guild().map(|g| (g.guild_id.get().to_owned(), g.name));

            let (guild_id, channel_name) =
                if let Some((a, b)) = props { (Some(a), Some(b)) } else { (None, None) };

            sqlx::query!(
                "INSERT IGNORE INTO channels (channel, guild_id) VALUES (?, (SELECT id FROM guilds WHERE guild = ?))",
                channel_id,
                guild_id
            )
            .execute(&pool.clone())
            .await?;

            Ok(sqlx::query_as_unchecked!(
                Self,
                "
                SELECT id, channel, nudge, webhook_id, webhook_token, paused, paused_until
                FROM channels
                WHERE channel = ?
                ",
                channel_id
            )
            .fetch_one(pool)
            .await?)
        }
    }

    pub async fn commit_changes(&self, pool: &MySqlPool) {
        sqlx::query!(
            "
            UPDATE channels
            SET
                nudge = ?,
                webhook_id = ?,
                webhook_token = ?,
                paused = ?,
                paused_until = ?
            WHERE id = ?
            ",
            self.nudge,
            self.webhook_id,
            self.webhook_token,
            self.paused,
            self.paused_until,
            self.id
        )
        .execute(pool)
        .await
        .unwrap();
    }

    pub async fn ensure_webhook(
        &mut self,
        ctx: impl CacheHttp,
        pool: &MySqlPool,
    ) -> Result<(), Error> {
        if self.webhook_id.is_none() || self.webhook_token.is_none() {
            let guild_channel = ChannelId::new(self.channel);

            let webhook = guild_channel
                .create_webhook(ctx.http(), CreateWebhook::new("Reminder").avatar(&*DEFAULT_AVATAR))
                .await?;

            self.webhook_id = Some(webhook.id.get().to_owned());
            self.webhook_token = webhook.token.map(|s| s.expose_secret().clone());
            self.commit_changes(pool).await;
        }

        Ok(())
    }
}
