use super::channel::ChannelType;
use super::Response;
use crate::database::{self, channel::Channel, PermissionCalculator};
use crate::guards::auth::UserRef;
use crate::guards::guild::GuildRef;

use bson::{doc, from_bson, Bson};
use rocket_contrib::json::Json;
use serde::{Deserialize, Serialize};
use ulid::Ulid;

macro_rules! with_permissions {
    ($user: expr, $target: expr) => {{
        let permissions = PermissionCalculator::new($user.clone())
            .guild($target.clone())
            .as_permission();

        if !permissions.get_access() {
            return None;
        }

        permissions
    }};
}

/// fetch your guilds
#[get("/@me")]
pub fn my_guilds(user: UserRef) -> Response {
    let col = database::get_collection("guilds");
    let guilds = col
        .find(
            doc! {
                "members": {
                    "$elemMatch": {
                        "id": user.id,
                    }
                }
            },
            None,
        )
        .unwrap();

    let mut parsed = vec![];
    for item in guilds {
        let doc = item.unwrap();
        parsed.push(json!({
            "id": doc.get_str("_id").unwrap(),
            "name": doc.get_str("name").unwrap(),
            "description": doc.get_str("description").unwrap(),
            "owner": doc.get_str("owner").unwrap(),
        }));
    }

    Response::Success(json!(parsed))
}

/// fetch a guild
#[get("/<target>")]
pub fn guild(user: UserRef, target: GuildRef) -> Option<Response> {
    with_permissions!(user, target);

    let mut targets = vec![];
    for channel in target.channels {
        targets.push(Bson::String(channel));
    }

    let col = database::get_collection("channels");
    match col.find(
        doc! {
            "_id": {
                "$in": targets,
            }
        },
        None,
    ) {
        Ok(results) => {
            let mut channels = vec![];
            for item in results {
                let channel: Channel = from_bson(bson::Bson::Document(item.unwrap()))
                    .expect("Failed to unwrap channel.");

                channels.push(json!({
                    "id": channel.id,
                    "last_message": channel.last_message,
                    "name": channel.name,
                    "description": channel.description,
                }));
            }

            Some(Response::Success(json!({
                "id": target.id,
                "name": target.name,
                "description": target.description,
                "owner": target.owner,
                "channels": channels,
            })))
        }
        Err(_) => Some(Response::InternalServerError(
            json!({ "error": "Failed to fetch channels." }),
        )),
    }
}

#[derive(Serialize, Deserialize)]
pub struct CreateGuild {
    name: String,
    description: Option<String>,
    nonce: String,
}

/// create a new guild
#[post("/create", data = "<info>")]
pub fn create_guild(user: UserRef, info: Json<CreateGuild>) -> Response {
    if !user.email_verified {
        return Response::Unauthorized(json!({ "error": "Email not verified!" }));
    }

    let name: String = info.name.chars().take(32).collect();
    let description: String = info
        .description
        .clone()
        .unwrap_or("No description.".to_string())
        .chars()
        .take(255)
        .collect();
    let nonce: String = info.nonce.chars().take(32).collect();

    let channels = database::get_collection("channels");
    let col = database::get_collection("guilds");
    if col
        .find_one(doc! { "nonce": nonce.clone() }, None)
        .unwrap()
        .is_some()
    {
        return Response::BadRequest(json!({ "error": "Guild already created!" }));
    }

    let id = Ulid::new().to_string();
    let channel_id = Ulid::new().to_string();
    if channels
        .insert_one(
            doc! {
                "_id": channel_id.clone(),
                "type": ChannelType::GUILDCHANNEL as u32,
                "name": "general",
                "guild": id.clone(),
            },
            None,
        )
        .is_err()
    {
        return Response::InternalServerError(
            json!({ "error": "Failed to create guild channel." }),
        );
    }

    if col
        .insert_one(
            doc! {
                "_id": id.clone(),
                "nonce": nonce,
                "name": name,
                "description": description,
                "owner": user.id.clone(),
                "channels": [
                    channel_id.clone()
                ],
                "members": [
                    {
                        "id": user.id,
                    }
                ],
                "invites": [],
                "default_permissions": 51,
            },
            None,
        )
        .is_ok()
    {
        Response::Success(json!({ "id": id }))
    } else {
        channels
            .delete_one(doc! { "_id": channel_id }, None)
            .expect("Failed to delete the channel we just made.");

        Response::InternalServerError(json!({ "error": "Failed to create guild." }))
    }
}