use std::{collections::HashMap, env};

use chrono::{DateTime, Utc};
use rocket::{
    http::{CookieJar, Status},
    serde::json::json,
    State,
};
use rocket_dyn_templates::Template;
use serde::Serialize;
use sqlx::{MySql, Pool};

use crate::routes::JsonResult;

fn is_admin(cookies: &CookieJar<'_>) -> bool {
    cookies
        .get_private("userid")
        .map_or(false, |cookie| Some(cookie.value().to_string()) == env::var("ADMIN_ID").ok())
}

#[get("/")]
pub async fn admin_dashboard_home(cookies: &CookieJar<'_>) -> Result<Template, Status> {
    if let Some(cookie) = cookies.get_private("userid") {
        let map: HashMap<&str, String> = HashMap::new();
        if Some(cookie.value().to_string()) == env::var("ADMIN_ID").ok() {
            Ok(Template::render("admin_dashboard", &map))
        } else {
            Err(Status::Forbidden)
        }
    } else {
        Err(Status::Unauthorized)
    }
}

#[derive(Serialize)]
struct TimeFrame {
    time_key: DateTime<Utc>,
    count: i64,
}

#[get("/data")]
pub async fn bot_data(cookies: &CookieJar<'_>, pool: &State<Pool<MySql>>) -> JsonResult {
    if !is_admin(cookies) {
        return json_err!("Not authorized");
    }

    let backlog = sqlx::query!(
        "SELECT COUNT(1) AS backlog FROM reminders WHERE `utc_time` < NOW() AND enabled = 1 AND `status` = 'pending'"
    )
    .fetch_one(pool.inner())
    .await
    .unwrap();

    let schedule_once = sqlx::query_as_unchecked!(
        TimeFrame,
        "SELECT
            FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(`utc_time`) / 300) * 300) AS `time_key`,
            COUNT(1) AS `count`
        FROM reminders
        WHERE
            `utc_time` < DATE_ADD(NOW(), INTERVAL 1 DAY) AND
            `utc_time` >= NOW() AND
            `enabled` = 1 AND
            `status` = 'pending' AND
            `interval_seconds` IS NULL AND
            `interval_months` IS NULL AND
            `interval_days` IS NULL
        GROUP BY `time_key`
        ORDER BY `time_key`"
    )
    .fetch_all(pool.inner())
    .await
    .unwrap();

    let schedule_interval = sqlx::query_as_unchecked!(
        TimeFrame,
        "SELECT
            FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(`utc_time`) / 300) * 300) AS `time_key`,
            COUNT(1) AS `count`
        FROM reminders
        WHERE
            `utc_time` < DATE_ADD(NOW(), INTERVAL 1 DAY) AND
            `utc_time` >= NOW() AND
            `status` = 'pending' AND
            `enabled` = 1 AND (
                `interval_seconds` IS NOT NULL OR
                `interval_months` IS NOT NULL OR
                `interval_days` IS NOT NULL
            )
        GROUP BY `time_key`
        ORDER BY `time_key`"
    )
    .fetch_all(pool.inner())
    .await
    .unwrap();

    let schedule_once_long = sqlx::query_as_unchecked!(
        TimeFrame,
        "SELECT
            FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(`utc_time`) / 86400) * 86400) AS `time_key`,
            COUNT(1) AS `count`
        FROM reminders
        WHERE
            `utc_time` < DATE_ADD(NOW(), INTERVAL 31 DAY) AND
            `utc_time` >= NOW() AND
            `enabled` = 1 AND
            `status` = 'pending' AND
            `interval_seconds` IS NULL AND
            `interval_months` IS NULL AND
            `interval_days` IS NULL
        GROUP BY `time_key`
        ORDER BY `time_key`"
    )
    .fetch_all(pool.inner())
    .await
    .unwrap();

    let schedule_interval_long = sqlx::query_as_unchecked!(
        TimeFrame,
        "SELECT
            FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(`utc_time`) / 86400) * 86400) AS `time_key`,
            COUNT(1) AS `count`
        FROM reminders
        WHERE
            `utc_time` < DATE_ADD(NOW(), INTERVAL 31 DAY) AND
            `utc_time` >= NOW() AND
            `status` = 'pending' AND
            `enabled` = 1 AND (
                `interval_seconds` IS NOT NULL OR
                `interval_months` IS NOT NULL OR
                `interval_days` IS NOT NULL
            )
        GROUP BY `time_key`
        ORDER BY `time_key`"
    )
    .fetch_all(pool.inner())
    .await
    .unwrap();

    let history = sqlx::query_as_unchecked!(
        TimeFrame,
        "SELECT
            FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(`utc_time`) / 86400) * 86400) AS `time_key`,
            COUNT(1) AS `count`
        FROM stat
        WHERE
            `utc_time` > DATE_SUB(NOW(), INTERVAL 31 DAY) AND
            `type` = 'reminder_sent'
        GROUP BY `time_key`
        ORDER BY `time_key`"
    )
    .fetch_all(pool.inner())
    .await
    .unwrap();

    let history_failed = sqlx::query_as_unchecked!(
        TimeFrame,
        "SELECT
            FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(`utc_time`) / 86400) * 86400) AS `time_key`,
            COUNT(1) AS `count`
        FROM stat
        WHERE
            `utc_time` > DATE_SUB(NOW(), INTERVAL 31 DAY) AND
            `type` = 'reminder_failed'
        GROUP BY `time_key`
        ORDER BY `time_key`"
    )
    .fetch_all(pool.inner())
    .await
    .unwrap();

    let interval_count = sqlx::query!(
        "SELECT COUNT(1) AS count
        FROM reminders
        WHERE
            `status` = 'pending' AND (
                `interval_seconds` IS NOT NULL OR
                `interval_months` IS NOT NULL OR
                `interval_days` IS NOT NULL
            )"
    )
    .fetch_one(pool.inner())
    .await
    .unwrap();

    let reminder_count = sqlx::query!(
        "SELECT COUNT(1) AS count
        FROM reminders
        WHERE
            `status` = 'pending' AND
            `interval_seconds` IS NULL AND
            `interval_months` IS NULL AND
            `interval_days` IS NULL"
    )
    .fetch_one(pool.inner())
    .await
    .unwrap();

    Ok(json!({
        "backlog": backlog.backlog,
        "scheduleShort": {
            "once": schedule_once,
            "interval": schedule_interval
        },
        "scheduleLong": {
            "once": schedule_once_long,
            "interval": schedule_interval_long,
        },
        "historyLong": {
            "sent": history,
            "failed": history_failed,
        },
        "count": {
            "reminders": reminder_count.count,
            "intervals": interval_count.count,
        }
    }))
}
