use crate::notifications::events::ClientboundNotification;
use crate::{
    database::{entities::User, get_collection},
    util::result::{Error, Result},
};
use futures::StreamExt;
use mongodb::{
    bson::{doc, from_document},
    options::FindOptions,
};

use super::websocket::is_online;

pub async fn generate_ready(mut user: User) -> Result<ClientboundNotification> {
    let mut users = vec![];

    if let Some(relationships) = &user.relations {
        let user_ids: Vec<String> = relationships
            .iter()
            .map(|relationship| relationship.id.clone())
            .collect();

        let mut cursor = get_collection("users")
            .find(
                doc! {
                    "_id": {
                        "$in": user_ids
                    }
                },
                FindOptions::builder()
                    .projection(doc! { "_id": 1, "username": 1 })
                    .build(),
            )
            .await
            .map_err(|_| Error::DatabaseError {
                operation: "find",
                with: "users",
            })?;

        while let Some(result) = cursor.next().await {
            if let Ok(doc) = result {
                let mut user: User = from_document(doc).map_err(|_| Error::DatabaseError {
                    operation: "from_document",
                    with: "user",
                })?;

                user.relationship = Some(
                    relationships
                        .iter()
                        .find(|x| user.id == x.id)
                        .ok_or_else(|| Error::InternalError)?
                        .status
                        .clone(),
                );

                user.online = Some(is_online(&user.id));

                users.push(user);
            }
        }
    }

    let mut cursor = get_collection("channels")
        .find(
            doc! {
                "$or": [
                    {
                        "type": "SavedMessages",
                        "user": &user.id
                    },
                    {
                        "type": "DirectMessage",
                        "recipients": &user.id,
                        "active": true
                    },
                    {
                        "type": "Group",
                        "recipients": &user.id
                    }
                ]
            },
            None,
        )
        .await
        .map_err(|_| Error::DatabaseError {
            operation: "find",
            with: "channels",
        })?;

    let mut channels = vec![];
    while let Some(result) = cursor.next().await {
        if let Ok(doc) = result {
            channels.push(from_document(doc).map_err(|_| Error::DatabaseError {
                operation: "from_document",
                with: "channel",
            })?);
        }
    }

    user.online = Some(true);
    users.push(user);

    Ok(ClientboundNotification::Ready { users, channels })
}