ReadingGroup.java

package qwerty.chaekit.domain.group;

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.BatchSize;
import org.springframework.lang.Nullable;
import qwerty.chaekit.domain.BaseEntity;
import qwerty.chaekit.domain.group.activity.Activity;
import qwerty.chaekit.domain.group.chat.GroupChat;
import qwerty.chaekit.domain.group.groupmember.GroupMember;
import qwerty.chaekit.domain.group.grouptag.GroupTag;
import qwerty.chaekit.domain.member.user.UserProfile;
import qwerty.chaekit.dto.group.enums.MyMemberShipStatus;

import java.util.ArrayList;
import java.util.List;

@Entity
@Getter
@Table(name = "reading_group")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@BatchSize(size = 20)
public class ReadingGroup extends BaseEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String name;

    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "group_leader_id", nullable = false)
    private UserProfile groupLeader;
    
    @Column(length = 5000)
    private String description;
    
    @Column(name = "is_auto_approval")
    private boolean autoApproval = true;

    @OneToMany(mappedBy = "readingGroup", cascade = CascadeType.ALL, orphanRemoval = true)
    @BatchSize(size = 20)
    private List<GroupTag> groupTags = new ArrayList<>();

    @OneToMany(mappedBy = "readingGroup", cascade = CascadeType.ALL, orphanRemoval = true)
    @BatchSize(size = 20)
    private final List<GroupMember> groupMembers = new ArrayList<>();

    @OneToMany(mappedBy = "group", cascade = CascadeType.ALL, orphanRemoval = true)
    @BatchSize(size = 100)
    private final List<GroupChat> groupChats = new ArrayList<>();
    
    @OneToMany(mappedBy = "group", cascade = CascadeType.ALL, orphanRemoval = true)
    @BatchSize(size = 20)
    private final List<Activity> activities = new ArrayList<>();

    private String groupImageKey;

    public void changeName(String name) {
        this.name = name;
    }

    public void addTags(List<String> tagNames) {
        for (String tagName : tagNames) {
            addTag(tagName);
        }
    }

    public void addTag(String tagName) {
        GroupTag groupTag = new GroupTag(this, tagName);
        groupTags.add(groupTag);
    }
    
    public void removeAllTags() {
        groupTags.clear();
    }

    public GroupMember addMember(UserProfile user) {
        GroupMember groupMember = new GroupMember(this, user);
        groupMembers.add(groupMember);
        return groupMember;
    }

    public GroupMember approveMember(UserProfile user){
        return groupMembers.stream()
                .filter(groupMember -> groupMember.getMember().getId().equals(user.getId()))
                .findFirst()
                .map(groupMember -> {groupMember.approve(); return groupMember;})
                .orElse(null);
    }

    public void removeMember(UserProfile user) {
        groupMembers.removeIf(member -> member.getMember().getId().equals(user.getId()));
    }

    public void rejectMember(UserProfile user) {
        groupMembers.stream()
                .filter(member -> member.getMember().getId().equals(user.getId()))
                .findFirst()
                .ifPresent(GroupMember::reject);
        removeMember(user);
    }

    public void updateDescription(String description) {
        this.description = description;
    }

    public void updateGroupImageKey(String groupImageKey) {
        this.groupImageKey = groupImageKey;
    }

    public MyMemberShipStatus getMemberShipStatus(@Nullable Long userId) {
        if(userId == null) {
            return MyMemberShipStatus.NONE;
        }
        if (isLeader(userId)) {
            return MyMemberShipStatus.OWNED;
        }
        return groupMembers.stream()
                .filter(member -> member.getMember().getId().equals(userId))
                .findFirst()
                .map(member -> member.isAccepted() ? MyMemberShipStatus.JOINED : MyMemberShipStatus.PENDING)
                .orElse(MyMemberShipStatus.NONE);
    }

    public long memberCount() {
        return groupMembers.stream()
                .filter(GroupMember::isAccepted)
                .count();
    }
    
    public boolean isLeader(UserProfile user) {
        return isLeader(user.getId());
    }

    public boolean isLeader(Long userId) {
        return groupLeader.getId().equals(userId);
    }

    public boolean isNotAcceptedMember(UserProfile user) {
        return groupMembers.stream()
                .noneMatch(member -> member.matchesUser(user) && member.isAccepted());
    }

    public boolean isMemberAlreadyRequested(UserProfile user) {
        return groupMembers.stream()
                .anyMatch(gm -> gm.matchesUser(user));
    }

    public boolean isPendingMember(Long userId) {
        return groupMembers.stream()
                .anyMatch(gm -> gm.matchesUserId(userId) && !gm.isAccepted());
    }

    public void changeAutoApproval(boolean autoApproval) {
        this.autoApproval = autoApproval;
    }

    @Builder
    public ReadingGroup(
            Long id, 
            String name, 
            UserProfile groupLeader, 
            List<GroupTag> groupTags, 
            String description, 
            String groupImageKey, 
            boolean isAutoApproval
    ) {
        this.id = id;
        this.name = name;
        this.groupLeader = groupLeader;
        this.groupTags = (groupTags != null) ? new ArrayList<>(groupTags) : new ArrayList<>();
        this.description = description;
        this.groupImageKey = groupImageKey;
        this.autoApproval = isAutoApproval;
    }
}