use super::mutual::has_mutual_connection;
use crate::database::channel::Channel;
use crate::database::guild::{fetch_guild, fetch_member, Guild, Member, MemberKey};
use crate::database::user::{User, UserRelationship};

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, Copy, Clone)]
#[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]);
    u32;
    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: &User, b: &User) -> Relationship {
    if a.id == b.id {
        return Relationship::SELF;
    }

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

pub struct PermissionCalculator {
    pub user: User,
    pub channel: Option<Channel>,
    pub guild: Option<Guild>,
    pub member: Option<Member>,
}

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

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

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

    pub fn fetch_data(mut self) -> PermissionCalculator {
        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 {
                        if let Ok(result) = fetch_guild(id) {
                            result
                        } else {
                            None
                        }
                    } else {
                        None
                    }
                }
                _ => None,
            }
        } else {
            None
        };

        if let Some(guild) = &guild {
            if let Ok(result) = fetch_member(MemberKey(guild.id.clone(), self.user.id.clone())) {
                self.member = result;
            }
        }

        self.guild = guild;
        self
    }

    pub fn calculate(&self) -> u32 {
        let mut permissions: u32 = 0;
        if let Some(guild) = &self.guild {
            if let Some(_member) = &self.member {
                // ? logic should match mutual.rs#has_mutual_connection
                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 = None;
                        for item in arr {
                            if item != &self.user.id {
                                other_user = Some(item);
                            }
                        }

                        if let Some(other) = other_user {
                            let relationship = get_relationship_internal(
                                &self.user.id,
                                &other,
                                &self.user.relations,
                            );

                            if relationship == Relationship::Friend {
                                permissions = 1024 + 128 + 32 + 16 + 1;
                            } else if relationship == Relationship::Blocked
                                || relationship == Relationship::BlockedOther
                            {
                                permissions = 1;
                            } else if has_mutual_connection(&self.user.id, other, true) {
                                permissions = 1024 + 128 + 32 + 16 + 1;
                            } else {
                                permissions = 1;
                            }
                        } else {
                            // ? In this case, it is a "self DM".
                            return 1024 + 128 + 32 + 16 + 1;
                        }
                    }
                }
                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()])
    }
}