use crate::database::{ self, user::User, channel::Channel };
use crate::routes::channel;

use rocket_contrib::json::{ Json, JsonValue };
use serde::{ Serialize, Deserialize };
use bson::{ bson, doc, from_bson };
use mongodb::options::FindOptions;
use ulid::Ulid;

/// retrieve your user information
#[get("/@me")]
pub fn me(user: User) -> JsonValue {
	json!({
		"id": user.id,
		"username": user.username,
		"email": user.email,
		"verified": user.email_verification.verified,
		"created_timestamp": Ulid::from_string(&user.id).unwrap().datetime().timestamp(),
	})
}

/// retrieve another user's information
#[get("/<target>")]
pub fn user(user: User, target: User) -> JsonValue {
	json!([])
}

#[derive(Serialize, Deserialize)]
pub struct Query {
	username: String,
}

/// lookup a user on Revolt
/// currently only supports exact username searches
#[post("/lookup", data = "<query>")]
pub fn lookup(_user: User, query: Json<Query>) -> JsonValue {
	let col = database::get_collection("users");

	let users = col.find(
		doc! { "username": query.username.clone() },
		FindOptions::builder().limit(10).build()
	).expect("Failed user lookup");

	let mut results = Vec::new();
	for user in users {
		let u: User = from_bson(bson::Bson::Document(user.unwrap())).expect("Failed to unwrap user.");
		results.push(
			json!({
				"id": u.id,
				"username": u.username
			})
		);
	}

	json!(results)
}

/// retrieve all of your DMs
#[get("/@me/dms")]
pub fn dms(user: User) -> JsonValue {
	let col = database::get_collection("channels");

	let results = col.find(
		doc! {
			"$or": [
				{
					"type": channel::ChannelType::DM as i32
				},
				{
					"type": channel::ChannelType::GROUP_DM as i32
				}
			],
			"recipients": user.id
		},
		None
	).expect("Failed channel lookup");

	let mut channels = Vec::new();
	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,
				"type": channel.channel_type,
				"recipients": channel.recipients,
				"active": channel.active.unwrap()
			})
		);
	}

	json!(channels)
}

/// open a DM with a user
#[get("/<target>/dm")]
pub fn dm(user: User, target: User) -> JsonValue {
	let col = database::get_collection("channels");

	match col.find_one(
		doc! { "type": channel::ChannelType::DM as i32, "recipients": [ user.id.clone(), target.id.clone() ] },
		None
	).expect("Failed channel lookup") {
		Some(channel) =>
			json!({
				"id": channel.get_str("_id").unwrap()
			}),
		None => {
			let id = Ulid::new();

			col.insert_one(
				doc! {
					"_id": id.to_string(),
					"type": channel::ChannelType::DM as i32,
					"recipients": [ user.id, target.id ],
					"active": false
				},
				None
			).expect("Failed insert query.");

			json!({
				"id": id.to_string()
			})
		}
	}
}

enum Relationship {
	FRIEND = 0,
	OUTGOING = 1,
	INCOMING = 2,
	BLOCKED = 3,
	BLOCKED_OTHER = 4,
	NONE = 5,
	SELF = 6,
}

fn get_relationship(a: &User, b: &User) -> Relationship {
	if a.id == b.id {
		return Relationship::SELF
	}

	if let Some(arr) = &b.relations {
		for entry in arr {
			if entry.id == a.id {
				match entry.status {
					0 => {
						return Relationship::FRIEND
					},
					1 => {
						return Relationship::INCOMING
					},
					2 => {
						return Relationship::OUTGOING
					},
					3 => {
						return Relationship::BLOCKED_OTHER
					}
					_ => {
						return Relationship::NONE
					}
				}
			}
		}
	}

	Relationship::NONE
}

/// retrieve all of your friends
#[get("/@me/friend")]
pub fn get_friends(user: User) -> JsonValue {
	let mut results = Vec::new();
	if let Some(arr) = user.relations {
		for item in arr {
			results.push(
				json!({
					"id": item.id,
					"status": item.status
				})
			)
		}
	}
	
	json!(results)
}

/// retrieve friend status with user
#[get("/<target>/friend")]
pub fn get_friend(user: User, target: User) -> JsonValue {
	let relationship = get_relationship(&user, &target);

	json!({
		"id": target.id,
		"status": relationship as u8
	})
}

/// create or accept a friend request
#[put("/<target>/friend")]
pub fn add_friend(user: User, target: User) -> JsonValue {
	let col = database::get_collection("users");
	let relationship = get_relationship(&user, &target);

	match relationship {
		Relationship::FRIEND =>
			json!({
				"success": false,
				"error": "Already friends."
			}),
		Relationship::OUTGOING =>
			json!({
				"success": false,
				"error": "Already sent a friend request."
			}),
		Relationship::INCOMING => {
			col.update_one(
				doc! {
					"_id": user.id.clone(),
					"relations.id": target.id.clone()
				},
				doc! {
					"$set": {
						"relations.$.status": Relationship::FRIEND as i32
					}
				},
				None
			).expect("Failed update query.");
			
			col.update_one(
				doc! {
					"_id": target.id,
					"relations.id": user.id
				},
				doc! {
					"$set": {
						"relations.$.status": Relationship::FRIEND as i32
					}
				},
				None
			).expect("Failed update query.");

			json!({
				"success": true
			})
		},
		Relationship::BLOCKED =>
			json!({
				"success": false,
				"error": "You have blocked this person."
			}),
		Relationship::BLOCKED_OTHER =>
			json!({
				"success": false,
				"error": "You have been blocked by this person."
			}),
		Relationship::NONE => {
			col.update_one(
				doc! {
					"_id": user.id.clone()
				},
				doc! {
					"$push": {
						"relations": {
							"id": target.id.clone(),
							"status": Relationship::OUTGOING as i32
						}
					}
				},
				None
			).expect("Failed update query.");
			
			col.update_one(
				doc! {
					"_id": target.id
				},
				doc! {
					"$push": {
						"relations": {
							"id": user.id,
							"status": Relationship::INCOMING as i32
						}
					}
				},
				None
			).expect("Failed update query.");

			json!({
				"success": true
			})
		},
		Relationship::SELF =>
			json!({
				"success": false,
				"error": "Cannot add yourself as a friend."
			})
	}
}

/// remove a friend or deny a request
#[delete("/<target>/friend")]
pub fn remove_friend(user: User, target: User) -> JsonValue {
	let col = database::get_collection("users");
	let relationship = get_relationship(&user, &target);

	match relationship {
		Relationship::FRIEND |
		Relationship::OUTGOING |
		Relationship::INCOMING => {
			col.update_one(
				doc! {
					"_id": user.id.clone()
				},
				doc! {
					"$pull": {
						"relations": {
							"id": target.id.clone()
						}
					}
				},
				None
			).expect("Failed update query.");

			col.update_one(
				doc! {
					"_id": target.id
				},
				doc! {
					"$pull": {
						"relations": {
							"id": user.id
						}
					}
				},
				None
			).expect("Failed update query.");

			json!({
				"success": true
			})
		},
		Relationship::BLOCKED |
		Relationship::BLOCKED_OTHER |
		Relationship::NONE |
		Relationship::SELF =>
			json!({
				"success": false,
				"error": "This has no effect."
			})
	}
}