use std::collections::HashMap;

use crate::entity::account::*;
use crate::entity::character::*;
use crate::entity::gateway::EntityGateway;
use crate::entity::item::*;

use std::sync::{Arc, Mutex};

#[derive(Clone)]
pub struct InMemoryGateway {
    users: Arc<Mutex<HashMap<UserAccountId, UserAccountEntity>>>,
    user_settings: Arc<Mutex<HashMap<UserSettingsId, UserSettingsEntity>>>,
    characters: Arc<Mutex<HashMap<CharacterEntityId, CharacterEntity>>>,
    items: Arc<Mutex<HashMap<ItemEntityId, ItemEntity>>>,
}

impl InMemoryGateway {
    pub fn new() -> InMemoryGateway {
        InMemoryGateway {
            users: Arc::new(Mutex::new(HashMap::new())),
            user_settings: Arc::new(Mutex::new(HashMap::new())),
            characters: Arc::new(Mutex::new(HashMap::new())),
            items: Arc::new(Mutex::new(HashMap::new())),
        }
    }
}

impl EntityGateway for InMemoryGateway {
    fn create_user(&mut self, user: NewUserAccountEntity) -> Option<UserAccountEntity> {
        let mut users = self.users.lock().unwrap();
        let id = users
            .iter()
            .fold(0, |sum, (i, _)| std::cmp::max(sum, i.0))
            + 1;
        let user = UserAccountEntity {
            id: UserAccountId(id),
            username: user.username,
            password: user.password,
            guildcard: user.guildcard,
            team_id: user.team_id,
            banned: user.banned,
            muted_until: user.muted_until,
            created_at: user.created_at,
            flags: user.flags,
        };
        users.insert(user.id, user.clone());
        Some(user)
    }

    fn get_user_by_id(&self, id: UserAccountId) -> Option<UserAccountEntity> {
        let users = self.users.lock().unwrap();
        users.get(&id).map(|k| k.clone())
    }

    fn get_user_by_name(&self, username: String) -> Option<UserAccountEntity> {
        let users = self.users.lock().unwrap();
        users
            .iter()
            .find(|(_, k)| k.username == username)
            .map(|(_, k)| k.clone())
    }

    fn save_user(&mut self, user: &UserAccountEntity) {
        let mut users = self.users.lock().unwrap();
        users.insert(user.id, user.clone());
    }

    fn get_user_settings_by_user(&self, user: &UserAccountEntity) -> Option<UserSettingsEntity> {
        let user_settings = self.user_settings.lock().unwrap();
        user_settings
            .iter()
            .find(|(_, k)| k.user_id == user.id)
            .map(|(_, k)| k.clone())
    }

    fn create_user_settings(&mut self, settings: NewUserSettingsEntity) -> Option<UserSettingsEntity> {
        let mut user_settings = self.user_settings.lock().unwrap();
        let id = user_settings
            .iter()
            .fold(0, |sum, (i, _)| std::cmp::max(sum, i.0))
            + 1;
        let new_settings = UserSettingsEntity {
            id: UserSettingsId(id),
            user_id: settings.user_id,
            settings: settings.settings,
        };
        user_settings.insert(new_settings.id, new_settings.clone());
        Some(new_settings)
    }

    fn get_characters_by_user(&self, user: &UserAccountEntity) -> [Option<CharacterEntity>; 4] {
        let characters = self.characters.lock().unwrap();
        let mut chars = [None; 4];
        characters
            .iter()
            .filter(|(_, c)| c.user_id == user.id)
            .for_each(|(_, c)| chars[c.slot as usize] = Some(c.clone()));
        chars
    }

    fn create_character(&mut self, character: NewCharacterEntity) -> Option<CharacterEntity> {
        let mut characters = self.characters.lock().unwrap();
        let id = characters
            .iter()
            .fold(0, |sum, (i, _)| std::cmp::max(sum, i.0))
            + 1;

        let new_character = CharacterEntity {
            id: CharacterEntityId(id),
            user_id: character.user_id,
            slot: character.slot,
            name: character.name,
            exp: character.exp,
            char_class: character.char_class,
            section_id: character.section_id,
            appearance: character.appearance,
            techs: character.techs,
            config: character.config,
            info_board: character.info_board,
            guildcard: character.guildcard,
            tech_menu: character.tech_menu,
            meseta: character.meseta,
        };
        characters.insert(new_character.id, new_character.clone());
        Some(new_character)
    }

    fn save_character(&mut self, char: &CharacterEntity) {
        let mut characters = self.characters.lock().unwrap();
        characters.insert(char.id, char.clone());
    }

    fn get_guild_card_data_by_user(&self, user: &UserAccountEntity) -> GuildCardDataEntity {
        GuildCardDataEntity::new(user.id)
    }

    fn create_item(&mut self, item: NewItemEntity) -> Option<ItemEntity> {
        let mut items = self.items.lock().unwrap();
        let id = items
            .iter()
            .fold(0, |sum, (i, _)| std::cmp::max(sum, i.0))
            + 1;
        let new_item = ItemEntity {
            id: ItemEntityId(id),
            location: item.location,
            item: item.item,
        };
        items.insert(ItemEntityId(id), new_item.clone());
        Some(new_item)
    }

    fn save_item(&mut self, item: &ItemEntity) {
        let mut items = self.items.lock().unwrap();
        items.insert(item.id, item.clone());
    }

    fn get_items_by_character(&self, character: &CharacterEntity) -> Vec<ItemEntity> {
        let items = self.items.lock().unwrap();
        items
            .iter()
            .filter(|(_, k)| {
                match k.location {
                    ItemLocation::Inventory{character_id, ..} => character_id == character.id,
                    ItemLocation::Bank{character_id, ..} => character_id == character.id,
                    _ => false
                }
            })
            .map(|(_, k)| {
                k.clone()
            })
            .collect()
    }
}