use std::{collections::HashMap, sync::OnceLock, time::Instant};

use log::warn;
use rocket::{get, http::CookieJar, serde::json::json, State};
use serde::Serialize;
use serenity::{client::Context, model::id::GuildId};
use tokio::sync::RwLock;

use crate::web::{check_authorization, routes::JsonResult};

#[derive(Serialize, Clone)]
struct EmojiInfo {
    fmt: String,
    name: String,
}

#[derive(Clone)]
struct EmojiCache {
    emojis: Vec<EmojiInfo>,
    timestamp: Instant,
}

const CACHE_LENGTH: u64 = 120;

static EMOJI_CACHE: OnceLock<RwLock<HashMap<GuildId, EmojiCache>>> = OnceLock::new();

#[get("/api/guild/<id>/emojis")]
pub async fn get_guild_emojis(
    id: u64,
    cookies: &CookieJar<'_>,
    ctx: &State<Context>,
) -> JsonResult {
    offline!(Ok(json!(vec![] as Vec<EmojiInfo>)));
    check_authorization(cookies, ctx.inner(), id).await?;

    let cache_value = {
        let cache = EMOJI_CACHE.get_or_init(|| RwLock::new(HashMap::new()));
        let read_lock = cache.read().await;
        read_lock.get(&GuildId::new(id)).cloned()
    };

    if let Some(emojis) = cache_value
        .map(|v| {
            if Instant::now().duration_since(v.timestamp).as_secs() < CACHE_LENGTH {
                Some(v.emojis)
            } else {
                None
            }
        })
        .flatten()
    {
        Ok(json!(emojis))
    } else {
        let emojis_res = ctx.http.get_emojis(GuildId::new(id)).await;

        match emojis_res {
            Ok(emojis) => {
                let emojis = emojis
                    .iter()
                    .map(|emoji| EmojiInfo {
                        fmt: format!("{}", emoji),
                        name: emoji.name.to_string(),
                    })
                    .collect::<Vec<EmojiInfo>>();

                {
                    let cache = EMOJI_CACHE.get_or_init(|| RwLock::new(HashMap::new()));
                    let mut write_lock = cache.write().await;
                    write_lock.insert(
                        GuildId::new(id),
                        EmojiCache { emojis: emojis.clone(), timestamp: Instant::now() },
                    );
                }

                Ok(json!(emojis))
            }
            Err(e) => {
                warn!("Could not fetch emojis from {}: {:?}", id, e);

                json_err!("Could not get emojis")
            }
        }
    }
}
