use chrono_tz::Tz;
use log::error;
use poise::serenity_prelude::{http::CacheHttp, model::id::UserId};
use sqlx::MySqlPool;

use crate::consts::LOCAL_TIMEZONE;

pub struct UserData {
    pub id: u64,
    pub dm_channel: u32,
    pub timezone: String,
    pub allowed_dm: bool,
}

impl UserData {
    pub async fn timezone_of<U>(user: U, pool: &MySqlPool) -> Tz
    where
        U: Into<UserId>,
    {
        let user_id = user.into().get().to_owned();

        match sqlx::query!(
            "
            SELECT IFNULL(timezone, 'UTC') AS timezone
            FROM users
            WHERE id = ?
            ",
            user_id
        )
        .fetch_one(pool)
        .await
        {
            Ok(r) => r.timezone,

            Err(_) => LOCAL_TIMEZONE.clone(),
        }
        .parse()
        .unwrap()
    }

    pub async fn from_user<U: Into<UserId>>(
        user: U,
        ctx: impl CacheHttp,
        pool: &MySqlPool,
    ) -> Result<Self, Box<dyn std::error::Error + Sync + Send>> {
        let user_id = user.into();

        match sqlx::query_as_unchecked!(
            Self,
            "
            SELECT id, dm_channel, IF(timezone IS NULL, ?, timezone) AS timezone, allowed_dm
            FROM users
            WHERE id = ?
            ",
            *LOCAL_TIMEZONE,
            user_id.get()
        )
        .fetch_one(pool)
        .await
        {
            Ok(c) => Ok(c),

            Err(sqlx::Error::RowNotFound) => {
                let dm_channel = user_id.create_dm_channel(ctx).await?;
                let pool_c = pool.clone();

                sqlx::query!(
                    "
                    INSERT IGNORE INTO channels (channel)
                    VALUES (?)
                    ",
                    dm_channel.id.get()
                )
                .execute(&pool_c)
                .await?;

                sqlx::query!(
                    "
                    INSERT INTO users (id, dm_channel, timezone)
                    VALUES (?, (SELECT id FROM channels WHERE channel = ?), ?)
                    ",
                    user_id.get(),
                    dm_channel.id.get(),
                    *LOCAL_TIMEZONE
                )
                .execute(&pool_c)
                .await?;

                Ok(sqlx::query_as_unchecked!(
                    Self,
                    "
                    SELECT id, dm_channel, timezone, allowed_dm
                    FROM users
                    WHERE id = ?
                    ",
                    user_id.get()
                )
                .fetch_one(pool)
                .await?)
            }

            Err(e) => {
                error!("Error querying for user: {:?}", e);

                Err(Box::new(e))
            }
        }
    }

    pub async fn commit_changes(&self, pool: &MySqlPool) {
        sqlx::query!(
            "
            UPDATE users
            SET timezone = ?, allowed_dm = ?
            WHERE id = ?
            ",
            self.timezone,
            self.allowed_dm,
            self.id
        )
        .execute(pool)
        .await
        .unwrap();
    }

    pub fn timezone(&self) -> Tz {
        self.timezone.parse().unwrap()
    }
}
