use std::collections::HashMap;

use juniper::Context;

use super::comment::comment::CommentValue;
use super::comment::opinion::Opinion;
use super::comment::solution::Solution;
use super::topic::discussion::Discussion;
use super::topic::problem::Problem;
use super::topic::topic::{Topic, TopicValue};
use super::user::commenter::Commenter;
use super::user::creator::Creator;
use super::user::follower::Follower;
use super::user::user::{User, UserValue};

#[derive(Default, Clone)]
pub struct Database {
    creators: HashMap<String, Creator>,
    problems: HashMap<String, Problem>,
    discussions: HashMap<String, Discussion>,
    followers: HashMap<String, Follower>,
    commenters: HashMap<String, Commenter>,
    solutions: HashMap<String, Solution>,
    opinions: HashMap<String, Opinion>,
}

impl Context for Database {}

impl Database {
    pub fn new() -> Database {
        let mut creators = HashMap::new();
        let mut followers = HashMap::new();
        let mut commenters = HashMap::new();
        let mut problems = HashMap::new();
        let mut discussions = HashMap::new();
        let mut solutions = HashMap::new();
        let mut opinions = HashMap::new();

        creators.insert(
            "2000".to_owned(),
            Creator::new("2000", "Luke Skywalker", &["4000"]),
        );

        followers.insert(
            "6000".to_owned(),
            Follower::new("6000", "Y Skywalker", &["2000"]),
        );

        commenters.insert(
            "7000".to_owned(),
            Commenter::new("7000", "V Skywalker", &["2000"]),
        );

        problems.insert(
            "4000".to_owned(),
            Problem::new("4000", "Luke Skywalker", &["2000"], &["8000"]),
        );

        discussions.insert("5000".to_owned(), Discussion::new("5000", "X Skywalker"));

        solutions.insert(
            "8000".to_owned(),
            Solution::new("8000", "V Skywalker", "4000", "solution to problem"),
        );

        opinions.insert(
            "9000".to_owned(),
            Opinion::new("9000", "V Skywalker", "5000", "opinion for discussion"),
        );

        Database {
            problems,
            creators,
            discussions,
            followers,
            commenters,
            solutions,
            opinions,
        }
    }
    pub fn get_user(&self, id: &str) -> Option<UserValue> {
        #[allow(clippy::manual_map)]
        if let Some(h) = self.creators.get(id) {
            Some(h.clone().into())
        } else if let Some(h) = self.followers.get(id) {
            Some(h.clone().into())
        } else if let Some(h) = self.commenters.get(id) {
            Some(h.clone().into())
        } else {
            None
        }
    }
    pub fn get_commenters(&self, t: &dyn Topic) -> Vec<UserValue> {
        let mut topic_commenters = vec![];
        for (_, commenter) in self.commenters.to_owned().into_iter() {
            if commenter.topic_ids().contains(&t.id().to_string()) {
                topic_commenters.push(commenter.clone().into());
            }
        }
        topic_commenters
    }

    pub fn get_topic(&self, id: &str) -> Option<TopicValue> {
        #[allow(clippy::manual_map)]
        if let Some(h) = self.problems.get(id) {
            Some(h.clone().into())
        } else if let Some(h) = self.discussions.get(id) {
            Some(h.clone().into())
        } else {
            None
        }
    }
    pub fn get_topics(&self, c: &dyn User) -> Vec<TopicValue> {
        c.topic_ids()
            .iter()
            .flat_map(|id| self.get_topic(id))
            .collect()
    }

    pub fn get_comment(&self, id: &str) -> Option<CommentValue> {
        #[allow(clippy::manual_map)]
        if let Some(h) = self.solutions.get(id) {
            Some(h.clone().into())
        } else if let Some(h) = self.opinions.get(id) {
            Some(h.clone().into())
        } else {
            None
        }
    }
    pub fn get_comments(&self, t: &dyn Topic) -> Vec<CommentValue> {
        let mut topic_comments = vec![];
        for (_, comment) in self.opinions.to_owned().into_iter() {
            if &comment.topic_id == &t.id().to_string() {
                topic_comments.push(comment.clone().into());
            }
        }
        for (_, comment) in self.solutions.to_owned().into_iter() {
            if &comment.topic_id == &t.id().to_string() {
                topic_comments.push(comment.clone().into());
            }
        }
        topic_comments
    }
}
