mod guilds;
mod models;
mod reminders;

use std::env;

use chrono_tz::Tz;
pub use guilds::*;
use log::warn;
pub use reminders::*;
use rocket::{
    get,
    http::CookieJar,
    patch,
    serde::json::{json, Json, Value as JsonValue},
    State,
};
use serde::{Deserialize, Serialize};
use serenity::{
    client::Context,
    model::id::{GuildId, RoleId},
};
use sqlx::{MySql, Pool};

use crate::web::guards::transaction::Transaction;

#[derive(Serialize)]
struct UserPreferences {
    timezone: String,
    use_browser_timezone: bool,
    dashboard_color_scheme: String,
    reset_inputs_on_create: bool,
}

#[derive(Serialize)]
struct UserInfo {
    name: String,
    patreon: bool,
    preferences: UserPreferences,
}

#[derive(Deserialize)]
pub struct UpdateUserPreferences {
    timezone: Option<String>,
    use_browser_timezone: Option<bool>,
    dashboard_color_scheme: Option<String>,
    reset_inputs_on_create: Option<bool>,
}

#[get("/api/user")]
pub async fn get_user_info(
    cookies: &CookieJar<'_>,
    ctx: &State<Context>,
    pool: &State<Pool<MySql>>,
) -> JsonValue {
    offline!(json!(UserInfo {
        name: "Discord".to_string(),
        patreon: true,
        preferences: UserPreferences {
            timezone: "UTC".to_string(),
            use_browser_timezone: true,
            dashboard_color_scheme: "system".to_string(),
            reset_inputs_on_create: false,
        }
    }));

    if let Some(user_id) =
        cookies.get_private("userid").map(|u| u.value().parse::<u64>().ok()).flatten()
    {
        let member_res = GuildId::new(env::var("PATREON_GUILD_ID").unwrap().parse().unwrap())
            .member(&ctx.inner(), user_id)
            .await;

        let preferences = sqlx::query!(
            "
            SELECT
                IFNULL(timezone, 'UTC') AS timezone,
                use_browser_timezone,
                dashboard_color_scheme,
                reset_inputs_on_create
            FROM users
            WHERE id = ?
            ",
            user_id
        )
        .fetch_one(pool.inner())
        .await
        .map_or(
            UserPreferences {
                timezone: "UTC".to_string(),
                use_browser_timezone: false,
                dashboard_color_scheme: "system".to_string(),
                reset_inputs_on_create: false,
            },
            |q| UserPreferences {
                timezone: q.timezone,
                use_browser_timezone: q.use_browser_timezone != 0,
                dashboard_color_scheme: q.dashboard_color_scheme,
                reset_inputs_on_create: q.reset_inputs_on_create != 0,
            },
        );

        let user_info = UserInfo {
            name: cookies
                .get_private("username")
                .map_or("Discord User".to_string(), |c| c.value().to_string()),
            patreon: member_res.map_or(false, |member| {
                member
                    .roles
                    .contains(&RoleId::new(env::var("PATREON_ROLE_ID").unwrap().parse().unwrap()))
            }),
            preferences,
        };

        json!(user_info)
    } else {
        json!({"error": "Not authorized"})
    }
}

#[patch("/api/user", data = "<preferences>")]
pub async fn update_user_info(
    cookies: &CookieJar<'_>,
    preferences: Json<UpdateUserPreferences>,
    mut transaction: Transaction<'_>,
) -> JsonValue {
    if let Some(user_id) =
        cookies.get_private("userid").map(|u| u.value().parse::<u64>().ok()).flatten()
    {
        if let Some(timezone) = &preferences.timezone {
            if timezone.parse::<Tz>().is_ok() {
                let _ = sqlx::query!(
                    "
                    UPDATE users
                    SET timezone = ?
                    WHERE id = ?
                    ",
                    timezone,
                    user_id,
                )
                .execute(transaction.executor())
                .await;
            } else {
                return json!({"error": "Timezone not recognised"});
            }
        }

        if let Some(dashboard_color_scheme) = &preferences.dashboard_color_scheme {
            if vec!["system", "light", "dark"].contains(&dashboard_color_scheme.as_str()) {
                let _ = sqlx::query!(
                    "
                    UPDATE users
                    SET dashboard_color_scheme = ?
                    WHERE id = ?
                    ",
                    dashboard_color_scheme,
                    user_id,
                )
                .execute(transaction.executor())
                .await;
            } else {
                return json!({"error": "Color scheme not recognised"});
            }
        }

        if let Some(reset_inputs_on_create) = &preferences.reset_inputs_on_create {
            let _ = sqlx::query!(
                "
                UPDATE users
                SET reset_inputs_on_create = ?
                WHERE id = ?
                ",
                reset_inputs_on_create,
                user_id,
            )
            .execute(transaction.executor())
            .await;
        }

        if let Some(use_browser_timezone) = &preferences.use_browser_timezone {
            let _ = sqlx::query!(
                "
                UPDATE users
                SET use_browser_timezone = ?
                WHERE id = ?
                ",
                use_browser_timezone,
                user_id,
            )
            .execute(transaction.executor())
            .await;
        }

        match transaction.commit().await {
            Ok(_) => json!({}),
            Err(e) => {
                warn!("Error updating user preferences for {}: {:?}", user_id, e);

                json!({"error": "Couldn't update preferences"})
            }
        }
    } else {
        json!({"error": "Not authorized"})
    }
}
