use super::mutual::has_mutual_connection;
use crate::database::get_collection;
use crate::database::user::UserRelationship;
use crate::guards::auth::UserRef;
use crate::guards::channel::ChannelRef;
use crate::guards::guild::GuildRef;

use bson::doc;
use num_enum::TryFromPrimitive;

#[derive(Debug, PartialEq, Eq, TryFromPrimitive)]
#[repr(u8)]
pub enum Relationship {
    Friend = 0,
    Outgoing = 1,
    Incoming = 2,
    Blocked = 3,
    BlockedOther = 4,
    NONE = 5,
    SELF = 6,
}

#[derive(Debug, PartialEq, Eq, TryFromPrimitive)]
#[repr(u32)]
pub enum Permission {
    Access = 1,
    CreateInvite = 2,
    KickMembers = 4,
    BanMembers = 8,
    ReadMessages = 16,
    SendMessages = 32,
    ManageMessages = 64,
    ManageChannels = 128,
    ManageServer = 256,
    ManageRoles = 512,
    SendDirectMessages = 1024,
}

bitfield! {
    pub struct MemberPermissions(MSB0 [u32]);
    u8;
    pub get_access, set_access: 31;
    pub get_create_invite, set_create_invite: 30;
    pub get_kick_members, set_kick_members: 29;
    pub get_ban_members, set_ban_members: 28;
    pub get_read_messages, set_read_messages: 27;
    pub get_send_messages, set_send_messages: 26;
    pub get_manage_messages, set_manage_messages: 25;
    pub get_manage_channels, set_manage_channels: 24;
    pub get_manage_server, set_manage_server: 23;
    pub get_manage_roles, set_manage_roles: 22;
    pub get_send_direct_messages, set_send_direct_messages: 21;
}

pub fn get_relationship_internal(
    user_id: &str,
    target_id: &str,
    relationships: &Option<Vec<UserRelationship>>,
) -> Relationship {
    if user_id == target_id {
        return Relationship::SELF;
    }

    if let Some(arr) = &relationships {
        for entry in arr {
            if entry.id == target_id {
                match entry.status {
                    0 => return Relationship::Friend,
                    1 => return Relationship::Outgoing,
                    2 => return Relationship::Incoming,
                    3 => return Relationship::Blocked,
                    4 => return Relationship::BlockedOther,
                    _ => return Relationship::NONE,
                }
            }
        }
    }

    Relationship::NONE
}

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

    get_relationship_internal(&a.id, &b.id, &a.fetch_relationships())
}

pub struct PermissionCalculator {
    pub user: UserRef,
    pub channel: Option<ChannelRef>,
    pub guild: Option<GuildRef>,
}

impl PermissionCalculator {
    pub fn new(user: UserRef) -> PermissionCalculator {
        PermissionCalculator {
            user,
            channel: None,
            guild: None,
        }
    }

    pub fn channel(self, channel: ChannelRef) -> PermissionCalculator {
        PermissionCalculator {
            channel: Some(channel),
            ..self
        }
    }

    pub fn guild(self, guild: GuildRef) -> PermissionCalculator {
        PermissionCalculator {
            guild: Some(guild),
            ..self
        }
    }

    pub fn calculate(self) -> u32 {
        let guild = if let Some(value) = self.guild {
            Some(value)
        } else if let Some(channel) = &self.channel {
            match channel.channel_type {
                0..=1 => None,
                2 => {
                    if let Some(id) = &channel.guild {
                        GuildRef::from(id.clone())
                    } else {
                        None
                    }
                }
                _ => None,
            }
        } else {
            None
        };

        let mut permissions: u32 = 0;
        if let Some(guild) = guild {
            if let Ok(result) = get_collection("members").find_one(
                doc! {
                    "_id.user": &self.user.id,
                    "_id.guild": &guild.id,
                },
                None,
            ) {
                if result.is_some() {
                    if guild.owner == self.user.id {
                        return u32::MAX;
                    }

                    permissions = guild.default_permissions as u32;
                }
            }
        }

        if let Some(channel) = &self.channel {
            match channel.channel_type {
                0 => {
                    if let Some(arr) = &channel.recipients {
                        let mut other_user = "";
                        for item in arr {
                            if item == &self.user.id {
                                permissions = 177;
                            } else {
                                other_user = item;
                            }
                        }

                        let relationships = self.user.fetch_relationships();
                        let relationship =
                            get_relationship_internal(&self.user.id, &other_user, &relationships);

                        if relationship == Relationship::Blocked
                            || relationship == Relationship::BlockedOther
                        {
                            permissions = 1;
                        } else if has_mutual_connection(&self.user.id, other_user) {
                            permissions = 177;
                        }
                    }
                }
                1 => {
                    if let Some(id) = &channel.owner {
                        if &self.user.id == id {
                            return u32::MAX;
                        }
                    }

                    if let Some(arr) = &channel.recipients {
                        for item in arr {
                            if item == &self.user.id {
                                permissions = 177;
                                break;
                            }
                        }
                    }
                }
                2 => {
                    // nothing implemented yet
                }
                _ => {}
            }
        }

        permissions
    }

    pub fn as_permission(self) -> MemberPermissions<[u32; 1]> {
        MemberPermissions([self.calculate()])
    }
}