use log::warn;
use rocket::{
    get,
    http::CookieJar,
    patch, post,
    serde::json::{json, Json},
    State,
};
use serenity::{client::Context, model::id::UserId};
use sqlx::{MySql, Pool};

use crate::utils::check_subscription;
use crate::web::{
    guards::transaction::Transaction,
    routes::{
        dashboard::{
            api::user::models::{create_reminder, Reminder},
            PatchReminder, MIN_INTERVAL,
        },
        JsonResult,
    },
    Database,
};

#[post("/api/user/reminders", data = "<reminder>")]
pub async fn create_user_reminder(
    reminder: Json<Reminder>,
    cookies: &CookieJar<'_>,
    ctx: &State<Context>,
    mut transaction: Transaction<'_>,
) -> JsonResult {
    let user_id =
        cookies.get_private("userid").map(|c| c.value().parse::<u64>().ok()).flatten().unwrap();

    match create_reminder(
        ctx.inner(),
        &mut transaction,
        UserId::new(user_id),
        reminder.into_inner(),
    )
    .await
    {
        Ok(r) => match transaction.commit().await {
            Ok(_) => Ok(r),
            Err(e) => {
                warn!("Couldn't commit transaction: {:?}", e);
                json_err!("Couldn't commit transaction.")
            }
        },

        Err(e) => Err(e),
    }
}

#[get("/api/user/reminders")]
pub async fn get_reminders(
    cookies: &CookieJar<'_>,
    ctx: &State<Context>,
    pool: &State<Pool<MySql>>,
) -> JsonResult {
    let user_id =
        cookies.get_private("userid").map(|c| c.value().parse::<u64>().ok()).flatten().unwrap();
    let channel = UserId::new(user_id).create_dm_channel(ctx.inner()).await;

    match channel {
        Ok(channel) => sqlx::query_as_unchecked!(
            Reminder,
            "
            SELECT
                reminders.attachment,
                reminders.attachment_name,
                reminders.content,
                reminders.embed_author,
                reminders.embed_author_url,
                reminders.embed_color,
                reminders.embed_description,
                reminders.embed_footer,
                reminders.embed_footer_url,
                reminders.embed_image_url,
                reminders.embed_thumbnail_url,
                reminders.embed_title,
                IFNULL(reminders.embed_fields, '[]') AS embed_fields,
                reminders.enabled,
                reminders.expires,
                reminders.interval_seconds,
                reminders.interval_days,
                reminders.interval_months,
                reminders.name,
                reminders.tts,
                reminders.uid,
                reminders.utc_time
            FROM reminders
            INNER JOIN channels ON channels.id = reminders.channel_id
            WHERE `status` = 'pending' AND channels.channel = ?
            ",
            channel.id.get()
        )
        .fetch_all(pool.inner())
        .await
        .map(|r| Ok(json!(r)))
        .unwrap_or_else(|e| {
            warn!("Failed to complete SQL query: {:?}", e);

            json_err!("Could not load reminders")
        }),

        Err(e) => {
            warn!("Couldn't get DM channel: {:?}", e);

            json_err!("Could not find a DM channel")
        }
    }
}

#[patch("/api/user/reminders", data = "<reminder>")]
pub async fn edit_reminder(
    reminder: Json<PatchReminder>,
    ctx: &State<Context>,
    mut transaction: Transaction<'_>,
    pool: &State<Pool<Database>>,
    cookies: &CookieJar<'_>,
) -> JsonResult {
    let user_id_cookie =
        cookies.get_private("userid").map(|c| c.value().parse::<u64>().ok()).flatten();

    if user_id_cookie.is_none() {
        return Err(json!({"error": "User not authorized"}));
    }

    let mut error = vec![];
    let user_id = user_id_cookie.unwrap();

    if reminder.message_ok() {
        update_field!(transaction.executor(), error, reminder.[
            content,
            embed_author,
            embed_description,
            embed_footer,
            embed_title,
            embed_fields
        ]);
    } else {
        error.push("Message exceeds limits.".to_string());
    }

    update_field!(transaction.executor(), error, reminder.[
        attachment,
        attachment_name,
        embed_author_url,
        embed_color,
        embed_footer_url,
        embed_image_url,
        embed_thumbnail_url,
        enabled,
        expires,
        name,
        tts,
        utc_time
    ]);

    if reminder.interval_days.flatten().is_some()
        || reminder.interval_months.flatten().is_some()
        || reminder.interval_seconds.flatten().is_some()
    {
        if check_subscription(&ctx.inner(), transaction.executor(), UserId::from(user_id), None)
            .await
        {
            let new_interval_length = match reminder.interval_days {
                Some(interval) => interval.unwrap_or(0),
                None => sqlx::query!(
                    "SELECT interval_days AS days FROM reminders WHERE uid = ?",
                    reminder.uid
                )
                .fetch_one(transaction.executor())
                .await
                .map_err(|e| {
                    warn!("Error updating reminder interval: {:?}", e);
                    json!({ "reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"] })
                })?
                .days
                .unwrap_or(0),
            } * 86400 + match reminder.interval_months {
                Some(interval) => interval.unwrap_or(0),
                None => sqlx::query!(
                    "SELECT interval_months AS months FROM reminders WHERE uid = ?",
                    reminder.uid
                )
                .fetch_one(transaction.executor())
                .await
                .map_err(|e| {
                    warn!("Error updating reminder interval: {:?}", e);
                    json!({ "reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"] })
                })?
                .months
                .unwrap_or(0),
            } * 2592000 + match reminder.interval_seconds {
                Some(interval) => interval.unwrap_or(0),
                None => sqlx::query!(
                    "SELECT interval_seconds AS seconds FROM reminders WHERE uid = ?",
                    reminder.uid
                )
                .fetch_one(transaction.executor())
                .await
                .map_err(|e| {
                    warn!("Error updating reminder interval: {:?}", e);
                    json!({ "reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"] })
                })?
                .seconds
                .unwrap_or(0),
            };

            if new_interval_length < *MIN_INTERVAL {
                error.push(String::from("New interval is too short."));
            } else {
                update_field!(transaction.executor(), error, reminder.[
                    interval_days,
                    interval_months,
                    interval_seconds
                ]);
            }
        }
    } else {
        sqlx::query!(
            "
            UPDATE reminders
            SET interval_seconds = NULL, interval_days = NULL, interval_months = NULL
            WHERE uid = ?
            ",
            reminder.uid
        )
        .execute(transaction.executor())
        .await
        .map_err(|e| {
            warn!("Error updating reminder interval: {:?}", e);
            json!({ "reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"] })
        })?;
    }

    if let Err(e) = transaction.commit().await {
        warn!("Couldn't commit transaction: {:?}", e);
        return json_err!("Couldn't commit transaction");
    }

    match sqlx::query_as_unchecked!(
        Reminder,
        "
        SELECT reminders.attachment,
         reminders.attachment_name,
         reminders.content,
         reminders.embed_author,
         reminders.embed_author_url,
         reminders.embed_color,
         reminders.embed_description,
         reminders.embed_footer,
         reminders.embed_footer_url,
         reminders.embed_image_url,
         reminders.embed_thumbnail_url,
         reminders.embed_title,
         reminders.embed_fields,
         reminders.enabled,
         reminders.expires,
         reminders.interval_seconds,
         reminders.interval_days,
         reminders.interval_months,
         reminders.name,
         reminders.tts,
         reminders.uid,
         reminders.utc_time
        FROM reminders
        LEFT JOIN channels ON channels.id = reminders.channel_id
        WHERE uid = ?
        ",
        reminder.uid
    )
    .fetch_one(pool.inner())
    .await
    {
        Ok(reminder) => Ok(json!({"reminder": reminder, "errors": error})),

        Err(e) => {
            warn!("Error exiting `edit_reminder': {:?}", e);

            Err(json!({"reminder": Option::<Reminder>::None, "errors": vec!["Unknown error"]}))
        }
    }
}
