//! Guild name retrieval and caching functions.
use super::paths;

use std::collections::HashMap;
use std::fs::File;
use std::sync::RwLock;

use once_cell::sync::Lazy;
use serde::{Deserialize, Serialize};

static CACHE: Lazy<RwLock<HashMap<String, Guild>>> = Lazy::new(|| RwLock::new(HashMap::new()));

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Guild {
    tag: String,
    name: String,
}

impl Guild {
    pub fn tag(&self) -> &str {
        &self.tag
    }

    pub fn name(&self) -> &str {
        &self.name
    }
}

/// Looks up the given guild.
///
/// This checks the cache first, and if nothing was found, it will hit the API.
pub fn lookup(api_id: &str) -> Option<Guild> {
    {
        let cache = CACHE.read().unwrap();
        if let Some(guild) = cache.get(api_id) {
            return Some(guild.clone());
        }
    }

    let mut cache = CACHE.write().unwrap();
    let url = format!("https://api.guildwars2.com/v2/guild/{}", api_id);
    let result = ureq::get(&url)
        .call()
        .into_json()
        .expect("Invalid JSON in API response");
    let name = result["name"].as_str()?;
    let tag = result["tag"].as_str()?;
    let guild = Guild {
        tag: tag.into(),
        name: name.into(),
    };
    cache.insert(api_id.into(), guild.clone());
    Some(guild)
}

/// Loads the cache from the file system.
pub fn prepare_cache() {
    let path = paths::cache_path();
    if !path.is_file() {
        return;
    }

    let file = File::open(path).expect("Unable to read cache file");
    let cache = serde_json::from_reader(file).expect("Cache file has invalid format");
    let mut target = CACHE.write().unwrap();
    *target = cache;
}

/// Saves the cache to the file system
pub fn save_cache() {
    let path = paths::cache_path();
    let file = File::create(path).expect("Cannot open cache for writing");
    serde_json::to_writer(file, &*CACHE.read().unwrap()).unwrap();
}