import logging

from app.extensions import db, redis_controller
from app.util import redislock

from app.models.user import User
from app.models.groups import Group, GroupIcon, GroupSettings, GroupRole, GroupRolePermission, GroupMember, GroupEconomy, GroupJoinRequest, GroupStatus, GroupWallPost

class GroupExceptions:
    class RolesetDoesNotExist(Exception):
        pass
    class GroupDoesNotExist(Exception):
        pass
    class UserDoesNotExist(Exception):
        pass
    class RolesetNameNotUnique(Exception):
        pass
    class InvalidRankNumber(Exception):
        pass
    class UserNotInGroup(Exception):
        pass
    class UserAlreadyInsideGroup(Exception):
        pass
    class CorruptedGroup(Exception):
        pass
    class GroupNameAlreadyTaken(Exception):
        pass
    class GroupNameNotAllowed(Exception):
        pass
    class InsufficientPermssions(Exception):
        pass

class UserGroupEntry:
    group_id : int = 0
    group_name : str = ""
    group_description : str = ""
    group_owner_id : int = 0

    group_member_count : int = 0

    user_rank : int = 0
    user_roleset : GroupRole | None = None

    def __init__(self, Group : Group, UserRoleset : GroupRole | None):
        self.group_id = Group.id
        self.group_name = Group.name
        self.group_description = Group.description
        self.group_owner_id = Group.owner_id

        self.user_rank = UserRoleset.rank
        self.user_roleset = UserRoleset

        self.group_member_count = GetGroupMemberCount(Group)

def GetUserFromId( UserObj : User | int ) -> User | None:
    """
    Returns a User object from a User ID.
    """
    if isinstance(UserObj, User):
        return UserObj
    else:
        TargetUser : User | None = User.query.filter_by(id=UserObj).first()
        if TargetUser is None:
            raise GroupExceptions.UserDoesNotExist("User does not exist.")
        return TargetUser

def GetRolesetFromId( Role : GroupRole | int ) -> GroupRole | None:
    """
    Returns a GroupRole object from a GroupRole ID.
    """
    if isinstance(Role, GroupRole):
        return Role
    else:
        TargetGroupRole : GroupRole | None = GroupRole.query.filter_by(id=Role).first()
        if TargetGroupRole is None:
            raise GroupExceptions.RolesetDoesNotExist("Roleset does not exist.")
        return TargetGroupRole

def GetGroupFromId( GroupObj : Group | int ) -> Group | None:
    """
    Returns a Group object from a Group ID.
    """
    if isinstance(GroupObj, Group):
        return GroupObj
    else:
        TargetGroup : Group | None = Group.query.filter_by(id=GroupObj).first()
        if TargetGroup is None:
            raise GroupExceptions.GroupDoesNotExist("Group does not exist.")
        return TargetGroup

def RefreshRolesetCount( Role : GroupRole | int ) -> int:
    """
        Recounts the amount of users in a GroupRole and updates the database.
        then returns the amount of users in the role.
    """
    Role : GroupRole | None = GetRolesetFromId(Role)

    TotalMembers : int = GroupMember.query.filter_by(group_id=Role.group_id, group_role_id=Role.id).count()
    Role.member_count = TotalMembers
    db.session.commit()

    return TotalMembers

def GetGroupMemberCount( Group : Group | int ) -> int:
    """
        Returns the amount of members in a group.
    """
    Group : Group | None = GetGroupFromId(Group)
    TotalMembers : int = 0
    for Role in GroupRole.query.filter_by(group_id=Group.id).all():
        TotalMembers += Role.member_count

    return TotalMembers

def GetUserRankInGroup( TargetUser : User | int, TargetGroup : Group | int ) -> int:
    """
        Returns a integer between 0-255 representing the user rank in the group.
        0 - Guest ( Not in the group )
        1 - 244 - Custom ranks
        255 - Group Owner
    """
    TargetUser : User = GetUserFromId(TargetUser)
    TargetGroup : Group = GetGroupFromId(TargetGroup)
    
    UserGroupMembership : GroupMember | None = GroupMember.query.filter_by(user_id=TargetUser.id, group_id=TargetGroup.id).first()
    if UserGroupMembership is None:
        return 0 # Guest
    try:
        UserAssignedRoleset : GroupRole = GetRolesetFromId(UserGroupMembership.group_role_id)
    except GroupExceptions.RolesetDoesNotExist:
        logging.warn(f"User {TargetUser.id} is in group {TargetGroup.id} but the roleset {UserGroupMembership.group_role_id} does not exist.")
        return 0 # Weird edge case where the user is in the group but the roleset doesn't exist
    return UserAssignedRoleset.rank

def GetUserRolesetInGroup( TargetUser : User | int, TargetGroup : Group | int ) -> GroupRole | None:
    """
        Returns a GroupRole object representing the user's roleset in the group.
    """
    TargetUser : User = GetUserFromId(TargetUser)
    TargetGroup : Group = GetGroupFromId(TargetGroup)

    UserGroupMembership : GroupMember | None = GroupMember.query.filter_by(user_id=TargetUser.id, group_id=TargetGroup.id).first()
    if UserGroupMembership is None:
        GuestRoleset : GroupRole | None = GroupRole.query.filter_by(group_id=TargetGroup.id, rank=0).first()
        if GuestRoleset is None:
            raise GroupExceptions.CorruptedGroup("Group has no guest roleset.")
        return GuestRoleset
    try:
        UserAssignedRoleset : GroupRole = GetRolesetFromId(UserGroupMembership.group_role_id)
    except GroupExceptions.RolesetDoesNotExist:
        logging.warn(f"User {TargetUser.id} is in group {TargetGroup.id} but the roleset {UserGroupMembership.group_role_id} does not exist.")
        return None
    return UserAssignedRoleset

def GetUserGroups( TargetUser : User | int ) -> list[UserGroupEntry]:
    """
        Returns a list of UserGroupEntry objects representing the user's groups.
    """
    TargetUser : User = GetUserFromId(TargetUser)

    UserGroups : list[UserGroupEntry] = []
    for UserGroupMembership in GroupMember.query.filter_by(user_id=TargetUser.id).all():
        try:
            UserAssignedRoleset : GroupRole = GetRolesetFromId(UserGroupMembership.group_role_id)
        except GroupExceptions.RolesetDoesNotExist:
            logging.warn(f"User {TargetUser.id} is in group {UserGroupMembership.group_id} but the roleset {UserGroupMembership.group_role_id} does not exist.")
            continue
        GroupObj : Group = GetGroupFromId(UserGroupMembership.group_id)
        UserGroups.append(UserGroupEntry(
            Group=GroupObj,
            UserRoleset=UserAssignedRoleset
        ))
    return UserGroups

def GetGroupRolesets( TargetGroup : Group | int ) -> list[GroupRole]:
    """
        Returns a list of GroupRole objects representing the group's rolesets.
    """
    TargetGroup : Group = GetGroupFromId(TargetGroup)

    return GroupRole.query.filter_by(group_id=TargetGroup.id).order_by(GroupRole.rank.asc()).all()

def CreateGroupRoleset( TargetGroup : Group | int, Name : str, Description : str, Rank : int ) -> GroupRole:
    """
        Creates a new roleset in the group and returns the GroupRole object.
    """
    TargetGroup : Group = GetGroupFromId(TargetGroup)
    if GroupRole.query.filter_by(group_id=TargetGroup.id, name=Name).first() is not None:
        raise GroupExceptions.RolesetNameNotUnique("A roleset with that name already exists.")
    if Rank < 0 or Rank > 255:
        raise GroupExceptions.InvalidRankNumber("Rank must be between 0 and 255.")
    if len(Name) > 255:
        raise ValueError("Name must be less than 255 characters.")
    if len(Description) > 255:
        raise ValueError("Description must be less than 255 characters.")
    
    NewRoleset : GroupRole = GroupRole(
        group_id=TargetGroup.id,
        name=Name,
        description=Description,
        rank=Rank,
        member_count=0
    )
    db.session.add(NewRoleset)
    db.session.commit()

    RolesetPermission : GroupRolePermission = GroupRolePermission(
        group_role_id=NewRoleset.id
    )
    RolesetPermission.view_status = True
    db.session.add(RolesetPermission)
    db.session.commit()

    return NewRoleset

def ChangeUserRole( TargetUser : User | int , TargetRoleset : GroupRole | int ) -> None:
    """
        Changes the user's roleset in the group if possible.
    """
    TargetUser : User = GetUserFromId(TargetUser)
    TargetRoleset : GroupRole = GetRolesetFromId(TargetRoleset)
    TargetGroup : Group = GetGroupFromId(TargetRoleset.group_id)

    if GetUserRankInGroup(TargetUser, TargetRoleset.group_id) == 0:
        raise GroupExceptions.UserNotInGroup("User is not in the group.")
    
    if GetUserRolesetInGroup(TargetUser, TargetRoleset.group_id) == TargetRoleset:
        return
    
    if TargetGroup.owner_id == TargetUser.id and TargetRoleset.rank != 255:
        raise ValueError("Cannot change the owner's roleset.")
    
    UserGroupMembership : GroupMember = GroupMember.query.filter_by(user_id=TargetUser.id, group_id=TargetGroup.id).first()
    OldRolesetId : int = UserGroupMembership.group_role.id
    UserGroupMembership.group_role_id = TargetRoleset.id
    db.session.commit()
    RefreshRolesetCount(OldRolesetId)
    RefreshRolesetCount(TargetRoleset.id)

    return

def AddUserToGroup( TargetUser : User | int , TargetGroup : Group | int, ForceJoin : bool = False ) -> GroupMember | GroupJoinRequest | None:
    """
        Adds the user to the group if possible.
        ForceJoin : bool ( If the group requires manual approval, this will bypass it. )
    """
    TargetUser : User = GetUserFromId(TargetUser)
    TargetGroup : Group = GetGroupFromId(TargetGroup)

    if GetUserRankInGroup(TargetUser, TargetGroup) != 0:
        raise GroupExceptions.UserAlreadyInsideGroup("User is already in the group.")
    
    TargetGroupSettings : GroupSettings | None = GroupSettings.query.filter_by(group_id=TargetGroup.id).first()
    if TargetGroupSettings is None:
        raise ValueError("Group settings do not exist.")
    if TargetGroupSettings.approval_required and not ForceJoin:
        ExisitingJoinRequest : GroupJoinRequest | None = GroupJoinRequest.query.filter_by(user_id=TargetUser.id, group_id=TargetGroup.id).first()
        if ExisitingJoinRequest is not None:
            return ExisitingJoinRequest
        NewJoinRequest : GroupJoinRequest = GroupJoinRequest(
            user_id=TargetUser.id,
            group_id=TargetGroup.id
        )
        db.session.add(NewJoinRequest)
        db.session.commit()
        return GroupJoinRequest
    
    LowestRankRoleset : GroupRole | None = GroupRole.query.filter_by(group_id=TargetGroup.id).filter(GroupRole.rank > 0).order_by(GroupRole.rank.asc()).first()
    if LowestRankRoleset is None:
        raise GroupExceptions.CorruptedGroup("Group has no rolesets.")
    if LowestRankRoleset.rank == 255:
        raise GroupExceptions.CorruptedGroup("Group has no member roleset.")
    
    NewGroupMember : GroupMember = GroupMember(
        user_id=TargetUser.id,
        group_id=TargetGroup.id,
        group_role_id=LowestRankRoleset.id
    )

    db.session.add(NewGroupMember)
    db.session.commit()

    RefreshRolesetCount(LowestRankRoleset)

    return NewGroupMember

def GetJoinRequest( TargetUser : User | int, TargetGroup : Group | int ) -> GroupJoinRequest | None:
    """
        Returns a GroupJoinRequest object from a user and group.
    """
    TargetUser : User = GetUserFromId(TargetUser)
    TargetGroup : Group = GetGroupFromId(TargetGroup)

    return GroupJoinRequest.query.filter_by(user_id=TargetUser.id, group_id=TargetGroup.id).first()

def AcceptJoinRequest( TargetUser : User | int, TargetGroup : Group | int ) -> GroupMember | None:
    """
        Accepts the user's join request.
    """
    ExisitingJoinRequest : GroupJoinRequest | None = GroupJoinRequest.query.filter_by(user_id=TargetUser.id, group_id=TargetGroup.id).first()
    if ExisitingJoinRequest is None:
        raise ValueError("Join request does not exist.")
    db.session.delete(ExisitingJoinRequest)
    db.session.commit()
    return AddUserToGroup(TargetUser, TargetGroup, ForceJoin=True)

def GetRolesetPermission( GroupRoleset : GroupRole | int ) -> GroupRolePermission:
    """
        Returns a GroupRolePermission object from a GroupRole.
    """
    GroupRoleset : GroupRole = GetRolesetFromId(GroupRoleset)
    RolesetPermission : GroupRolePermission | None = GroupRolePermission.query.filter_by(group_role_id=GroupRoleset.id).first()
    if RolesetPermission is None:
        RolesetPermission : GroupRolePermission = GroupRolePermission(
            group_role_id=GroupRoleset.id
        )
        db.session.add(RolesetPermission)
        db.session.commit()
    return RolesetPermission

def ModifyRolesetPermission(
        GroupRoleset : GroupRole, 
        DeleteFromWall : bool = None,
        PostToWall : bool = None,
        InviteMembers : bool = None,
        PostToStatus : bool = None,
        RemoveMembers : bool = None,
        ViewStatus : bool = None,
        ViewWall : bool = None,
        ChangeRank : bool = None,
        AdvertiseGroup : bool = None,
        ManageRelationships : bool = None,
        AddGroupPlaces : bool = None,
        ViewAuditLogs : bool = None,
        CreateItems : bool = None,
        ManageItems : bool = None,
        SpendGroupFunds : bool = None,
        ManageClan : bool = None,
        ManageGroupGames : bool = None
    ) -> None:
    """
        Modifies the roleset's permissions.
    """
    RolesetPermission : GroupRolePermission = GetRolesetPermission(GroupRoleset)
    
    if DeleteFromWall is not None:
        RolesetPermission.delete_from_wall = DeleteFromWall
    if PostToWall is not None:
        RolesetPermission.post_to_wall = PostToWall
    if InviteMembers is not None:
        RolesetPermission.invite_members = InviteMembers
    if PostToStatus is not None:
        RolesetPermission.post_to_status = PostToStatus
    if RemoveMembers is not None:
        RolesetPermission.remove_members = RemoveMembers
    if ViewStatus is not None:
        RolesetPermission.view_status = ViewStatus
    if ViewWall is not None:
        RolesetPermission.view_wall = ViewWall
    if ChangeRank is not None:
        RolesetPermission.change_rank = ChangeRank
    if AdvertiseGroup is not None:
        RolesetPermission.advertise_group = AdvertiseGroup
    if ManageRelationships is not None:
        RolesetPermission.manage_relationships = ManageRelationships
    if AddGroupPlaces is not None:
        RolesetPermission.add_group_places = AddGroupPlaces
    if ViewAuditLogs is not None:
        RolesetPermission.view_audit_logs = ViewAuditLogs
    if CreateItems is not None:
        RolesetPermission.create_items = CreateItems
    if ManageItems is not None:
        RolesetPermission.manage_items = ManageItems
    if SpendGroupFunds is not None:
        RolesetPermission.spend_group_funds = SpendGroupFunds
    if ManageClan is not None:
        RolesetPermission.manage_clan = ManageClan
    if ManageGroupGames is not None:
        RolesetPermission.manage_group_games = ManageGroupGames
    
    db.session.commit()
    return

from app.util.textfilter import FilterText, TextNotAllowedException
from sqlalchemy import func

def SearchGroupByName( GroupName : str ) -> Group | None :
    """
        Returns a Group object from a group name.
    """
    GroupName = GroupName.lower()
    return Group.query.filter(func.lower(Group.name) == GroupName).first()

def isGroupNameAllowed( GroupName : str ) -> bool:
    """
        Returns True if the group name is allowed.
    """
    if len(GroupName) > 255:
        return False
    if len(GroupName) < 3:
        return False
    
    AlphanumericCharacters : int = 0
    for Character in GroupName:
        if Character.isalnum():
            AlphanumericCharacters += 1
    if AlphanumericCharacters < 3:
        return False
    try:
        FilterText( Text = GroupName, ThrowException = True)
    except TextNotAllowedException:
        return False
    return True

def AssertUserHasPermission( TargetUser : User | int, TargetGroup : Group | int, Permission ) -> None: # Permission should be like GroupRolePermission.post_to_status
    """
        Raises an exception if the user does not have the permission.
    """
    TargetUser : User = GetUserFromId(TargetUser)
    TargetGroup : Group = GetGroupFromId(TargetGroup)

    UserRoleset : GroupRole | None = GetUserRolesetInGroup(TargetUser, TargetGroup)
    if UserRoleset is None:
        raise GroupExceptions.CorruptedGroup("Group does not have a guest roleset.")
    RolesetPermission : GroupRolePermission = GetRolesetPermission(UserRoleset)

    if not getattr(RolesetPermission, Permission.name):
        raise GroupExceptions.InsufficientPermssions("User does not have permission.")
    return


def CreateGroup( GroupName : str, GroupDescription : str, GroupOwner : User | int, GroupIconContentHash : str ) -> Group | None :
    """
        Creates a new group and returns the Group object.
    """

    CreateLock = redislock.acquire_lock("group_creation_lock", 30, 5)
    if not CreateLock:
        raise ValueError("Failed to acquire global creation lock.")

    if SearchGroupByName(GroupName) is not None:
        redislock.release_lock("group_creation_lock", CreateLock)
        raise GroupExceptions.GroupNameAlreadyTaken("Group name is already taken.")

    if isGroupNameAllowed(GroupName) is False:
        redislock.release_lock("group_creation_lock", CreateLock)
        raise GroupExceptions.GroupNameNotAllowed("Group name is not allowed.")
    if len(GroupDescription) > 1024:
        redislock.release_lock("group_creation_lock", CreateLock)
        raise ValueError("Group description must be less than 1024 characters.")
    GroupOwner : User = GetUserFromId(GroupOwner)

    NewGroup : Group = Group(
        name=GroupName,
        description=GroupDescription,
        owner_id=GroupOwner.id
    )
    db.session.add(NewGroup)
    db.session.commit()
    redislock.release_lock("group_creation_lock", CreateLock)
    NewGroupSettings : GroupSettings = GroupSettings(
        group_id=NewGroup.id
    )
    db.session.add(NewGroupSettings)

    NewGroupIcon : GroupIcon = GroupIcon(
        group_id=NewGroup.id,
        content_hash=GroupIconContentHash,
        moderation_status=1,
        creator_id=GroupOwner.id
    )
    db.session.add(NewGroupIcon)

    NewGroupEconomy : GroupEconomy = GroupEconomy(
        group_id=NewGroup.id
    )
    db.session.add(NewGroupEconomy)
    db.session.commit()

    GuestGroupRoleset : GroupRole = CreateGroupRoleset(
        TargetGroup=NewGroup,
        Name="Guest",
        Description="A non-group member.",
        Rank=0
    )
    ModifyRolesetPermission(GuestGroupRoleset, ViewStatus=True, ViewWall=True)
    MemberGroupRoleset : GroupRole = CreateGroupRoleset(
        TargetGroup=NewGroup,
        Name="Member",
        Description="A regular group member.",
        Rank=1
    )
    ModifyRolesetPermission(MemberGroupRoleset, ViewStatus=True, ViewWall=True, PostToWall=True)
    AdminGroupRoleset : GroupRole = CreateGroupRoleset(
        TargetGroup=NewGroup,
        Name="Admin",
        Description="A group administrator.",
        Rank=254
    )
    ModifyRolesetPermission(AdminGroupRoleset, ViewStatus=True, ViewWall=True, PostToWall=True)
    OwnerGroupRoleset : GroupRole = CreateGroupRoleset(
        TargetGroup=NewGroup,
        Name="Owner",
        Description="The group's owner.",
        Rank=255
    )
    ModifyRolesetPermission(
        OwnerGroupRoleset,
        DeleteFromWall=True,
        PostToWall=True,
        InviteMembers=True,
        PostToStatus=True,
        RemoveMembers=True,
        ViewStatus=True,
        ViewWall=True,
        ChangeRank=True,
        AdvertiseGroup=True,
        ManageRelationships=True,
        AddGroupPlaces=True,
        ViewAuditLogs=True,
        CreateItems=True,
        ManageItems=True,
        SpendGroupFunds=True,
        ManageClan=True,
        ManageGroupGames=True
    )

    AddUserToGroup(GroupOwner, NewGroup, ForceJoin=True)
    ChangeUserRole(GroupOwner, OwnerGroupRoleset)
    
    return NewGroup

def PostToGroupStatus( Poster : User | int, TargetGroup : Group | int, StatusMessage : str, ForcePost : bool = False ) -> GroupStatus:
    """
        Posts a status to the group.
        ForcePost : bool ( If True this function will not check for the user permissions. )
    """
    Poster : User = GetUserFromId(Poster)
    TargetGroup : Group = GetGroupFromId(TargetGroup)
    if not ForcePost:
        AssertUserHasPermission(Poster, TargetGroup, GroupRolePermission.post_to_status)
    
    FilteredMessage : str = FilterText(Text=StatusMessage)
    if len(FilteredMessage) > 1024:
        raise ValueError("Status message must be less than 1024 characters.")
    NewStatus : GroupStatus = GroupStatus(
        group_id=TargetGroup.id,
        poster_id=Poster.id,
        content=FilteredMessage
    )
    db.session.add(NewStatus)
    db.session.commit()

    return NewStatus

def PostToGroupWall( Poster : User | int, TargetGroup : Group | int, WallMessage : str, ForcePost : bool = False ) -> GroupWallPost:
    """
        Posts a message to the group wall.
        ForcePost : bool ( If True this function will not check for the user permissions. )
    """
    Poster : User = GetUserFromId(Poster)
    TargetGroup : Group = GetGroupFromId(TargetGroup)
    if not ForcePost:
        AssertUserHasPermission(Poster, TargetGroup, GroupRolePermission.post_to_wall)
    
    FilteredMessage : str = FilterText(Text=WallMessage)
    if len(FilteredMessage) > 1024:
        raise ValueError("Wall message must be less than 1024 characters.")
    NewWallPost : GroupWallPost = GroupWallPost(
        group_id=TargetGroup.id,
        poster_id=Poster.id,
        content=FilteredMessage
    )
    db.session.add(NewWallPost)
    db.session.commit()

    return NewWallPost

def GetGroupWallPosts( TargetGroup : Group | int, Page : int = 1, PerPage : int= 10 ) -> list[GroupWallPost]:
    """
        Returns a list of GroupWallPost objects representing the group's wall posts.
    """
    TargetGroup : Group = GetGroupFromId(TargetGroup)

    return GroupWallPost.query.filter_by(group_id=TargetGroup.id).order_by(GroupWallPost.created_at.desc()).paginate(Page, PerPage, False).items

def RemoveUserFromGroup( TargetUser : User | int, TargetGroup : Group | int, AllowOwnerRemove : bool = False ) -> bool:
    """
        Removes the user from the group.
    """
    TargetUser : User = GetUserFromId(TargetUser)
    TargetGroup : Group = GetGroupFromId(TargetGroup)

    if GetUserRankInGroup(TargetUser, TargetGroup.id) == 0:
        raise GroupExceptions.UserNotInGroup("User is not in the group.")
    
    if TargetGroup.owner_id == TargetUser.id and not AllowOwnerRemove:
        raise ValueError("Cannot remove the owner from the group.")
    
    if TargetGroup.owner_id == TargetUser.id:
        TargetGroup.owner_id = None

    UserGroupMembership : GroupMember = GroupMember.query.filter_by(user_id=TargetUser.id, group_id=TargetGroup.id).first()
    OldRolesetId : int = UserGroupMembership.group_role.id
    db.session.delete(UserGroupMembership)
    db.session.commit()

    RefreshRolesetCount(OldRolesetId)

    return True

def isGroupNameAllowed( GroupName : str ) -> tuple[bool, str]:
    """
        Returns True if the group name is allowed.
        If not it returns a bool and a string which is the reason why its not allowed
    """
    if len(GroupName) > 50:
        return False, "Group name must be less than 40 characters."
    if len(GroupName) < 3:
        return False, "Group name must be more than 3 characters."
    
    AlphanumericCharacters : int = 0
    for Character in GroupName:
        if Character.isalnum():
            AlphanumericCharacters += 1
    if AlphanumericCharacters < 3:
        return False, "Group name must contain at least 3 alphanumeric characters."
    try:
        FilterText( Text = GroupName, ThrowException = True)
    except TextNotAllowedException:
        return False, "Group name is not allowed."
    return True, ""

def DeleteGroupWallPost( TargetPost : GroupWallPost, Remover : User | int, ForceDelete : bool = False ) -> None:
    """
        Deletes a group wall post.
        ForceDelete : bool ( If True this function will not check for the user permissions. )
    """
    TargetPost : GroupWallPost = TargetPost
    Remover : User = GetUserFromId(Remover)
    TargetGroup : Group = GetGroupFromId(TargetPost.group_id)
    if not ForceDelete:
        AssertUserHasPermission(Remover, TargetGroup, GroupRolePermission.delete_from_wall)

    db.session.delete(TargetPost)
    db.session.commit()
    return

def GetLatestGroupStatus( TargetGroup : Group | int, ignoreEmpty : bool = True ) -> GroupWallPost | None: 
    """
        Returns the latest group status.
    """
    TargetGroup : Group = GetGroupFromId(TargetGroup)

    LatestGroupStatusPost : GroupStatus | None = GroupStatus.query.filter_by(group_id=TargetGroup.id).order_by(GroupStatus.created_at.desc()).first()
    if LatestGroupStatusPost is None:
        return None
    if ignoreEmpty and LatestGroupStatusPost.content == "":
        return None
    return LatestGroupStatusPost

def SetNewGroupIcon( TargetGroup : Group | int, NewIconHash : str, Uploader : User | int ) -> GroupIcon:
    """
        Sets a new group icon.
    """
    TargetGroup : Group = GetGroupFromId(TargetGroup)
    Uploader : User = GetUserFromId(Uploader)

    if TargetGroup.icon is not None:
        db.session.delete(TargetGroup.icon)
        db.session.commit()
    
    NewGroupIcon : GroupIcon = GroupIcon(
        group_id=TargetGroup.id,
        content_hash=NewIconHash,
        moderation_status=1,
        creator_id=Uploader.id
    )
    db.session.add(NewGroupIcon)
    db.session.commit()

    return NewGroupIcon

def GetUserGroupCount( UserObj : User | int ) -> int:
    """
        Gets the amount of groups a user is in.
    """
    UserObj : User = GetUserFromId(UserObj)

    return GroupMember.query.filter_by(user_id=UserObj.id).count()