package groups

import (
	"fmt"
	"time"

	"eq2emu/internal/entity"
)

// NewGroupManager creates a new group manager with the given configuration
func NewGroupManager(config GroupManagerConfig) *GroupManager {
	manager := &GroupManager{
		groups:             make(map[int32]*Group),
		nextGroupID:        1,
		pendingInvites:     make(map[string]*GroupInvite),
		raidPendingInvites: make(map[string]*GroupInvite),
		eventHandlers:      make([]GroupEventHandler, 0),
		config:             config,
		stopChan:           make(chan struct{}),
	}

	return manager
}

// Start starts the group manager background processes
func (gm *GroupManager) Start() error {
	// Start background processes
	if gm.config.UpdateInterval > 0 {
		gm.wg.Add(1)
		go gm.updateGroupsLoop()
	}

	if gm.config.BuffUpdateInterval > 0 {
		gm.wg.Add(1)
		go gm.updateBuffsLoop()
	}

	gm.wg.Add(1)
	go gm.cleanupExpiredInvitesLoop()

	if gm.config.EnableStatistics {
		gm.wg.Add(1)
		go gm.updateStatsLoop()
	}

	return nil
}

// Stop stops the group manager and all background processes
func (gm *GroupManager) Stop() error {
	close(gm.stopChan)
	gm.wg.Wait()
	return nil
}

// NewGroup creates a new group with the given leader and options
func (gm *GroupManager) NewGroup(leader entity.Entity, options *GroupOptions, overrideGroupID int32) (int32, error) {
	if leader == nil {
		return 0, fmt.Errorf("leader cannot be nil")
	}

	var groupID int32
	if overrideGroupID > 0 {
		groupID = overrideGroupID
	} else {
		groupID = gm.generateNextGroupID()
	}

	// Check if group ID already exists
	gm.groupsMutex.RLock()
	if _, exists := gm.groups[groupID]; exists && overrideGroupID == 0 {
		gm.groupsMutex.RUnlock()
		return 0, fmt.Errorf("group ID %d already exists", groupID)
	}
	gm.groupsMutex.RUnlock()

	// Create new group
	group := NewGroup(groupID, options)

	// Add leader to the group
	if err := group.AddMember(leader, true); err != nil {
		group.Disband()
		return 0, fmt.Errorf("failed to add leader to group: %v", err)
	}

	// Add group to manager
	gm.groupsMutex.Lock()
	gm.groups[groupID] = group
	gm.groupsMutex.Unlock()

	// Update statistics
	gm.updateStatsForNewGroup()

	// Fire event
	gm.fireGroupCreatedEvent(group, leader)

	return groupID, nil
}

// RemoveGroup removes a group from the manager
func (gm *GroupManager) RemoveGroup(groupID int32) error {
	gm.groupsMutex.Lock()
	group, exists := gm.groups[groupID]
	if !exists {
		gm.groupsMutex.Unlock()
		return fmt.Errorf("group %d not found", groupID)
	}
	delete(gm.groups, groupID)
	gm.groupsMutex.Unlock()

	// Disband the group
	group.Disband()

	// Update statistics
	gm.updateStatsForRemovedGroup()

	// Fire event
	gm.fireGroupDisbandedEvent(group)

	return nil
}

// GetGroup returns a group by ID
func (gm *GroupManager) GetGroup(groupID int32) *Group {
	gm.groupsMutex.RLock()
	defer gm.groupsMutex.RUnlock()

	return gm.groups[groupID]
}

// IsGroupIDValid checks if a group ID is valid and exists
func (gm *GroupManager) IsGroupIDValid(groupID int32) bool {
	gm.groupsMutex.RLock()
	defer gm.groupsMutex.RUnlock()

	_, exists := gm.groups[groupID]
	return exists
}

// AddGroupMember adds a member to an existing group
func (gm *GroupManager) AddGroupMember(groupID int32, member entity.Entity, isLeader bool) error {
	group := gm.GetGroup(groupID)
	if group == nil {
		return fmt.Errorf("group %d not found", groupID)
	}

	return group.AddMember(member, isLeader)
}

// AddGroupMemberFromPeer adds a member from a peer server to an existing group
func (gm *GroupManager) AddGroupMemberFromPeer(groupID int32, info *GroupMemberInfo) error {
	group := gm.GetGroup(groupID)
	if group == nil {
		return fmt.Errorf("group %d not found", groupID)
	}

	return group.AddMemberFromPeer(
		info.Name, info.Leader, info.IsClient, info.ClassID,
		info.HPCurrent, info.HPMax, info.LevelCurrent, info.LevelMax,
		info.PowerCurrent, info.PowerMax, info.RaceID, info.Zone,
		info.MentorTargetCharID, info.ZoneID, info.InstanceID,
		info.ClientPeerAddress, info.ClientPeerPort, info.IsRaidLooter,
	)
}

// RemoveGroupMember removes a member from a group
func (gm *GroupManager) RemoveGroupMember(groupID int32, member entity.Entity) error {
	group := gm.GetGroup(groupID)
	if group == nil {
		return fmt.Errorf("group %d not found", groupID)
	}

	err := group.RemoveMember(member)
	if err != nil {
		return err
	}

	// If group is now empty, remove it
	if group.GetSize() == 0 {
		gm.RemoveGroup(groupID)
	}

	return nil
}

// RemoveGroupMemberByName removes a member by name from a group
func (gm *GroupManager) RemoveGroupMemberByName(groupID int32, name string, isClient bool, charID int32) error {
	group := gm.GetGroup(groupID)
	if group == nil {
		return fmt.Errorf("group %d not found", groupID)
	}

	err := group.RemoveMemberByName(name, isClient, charID)
	if err != nil {
		return err
	}

	// If group is now empty, remove it
	if group.GetSize() == 0 {
		gm.RemoveGroup(groupID)
	}

	return nil
}

// SendGroupUpdate sends an update to all members of a group
func (gm *GroupManager) SendGroupUpdate(groupID int32, excludeClient interface{}, forceRaidUpdate bool) {
	group := gm.GetGroup(groupID)
	if group != nil {
		group.SendGroupUpdate(excludeClient, forceRaidUpdate)
	}
}

// Group invitation handling

// Invite handles inviting a player to a group
func (gm *GroupManager) Invite(leader entity.Entity, member entity.Entity) int8 {
	if leader == nil || member == nil {
		return GROUP_INVITE_TARGET_NOT_FOUND
	}

	// Check if inviting self
	if leader == member {
		return GROUP_INVITE_SELF_INVITE
	}

	// Check if member already has an invite
	inviteKey := member.GetName()
	if gm.hasPendingInvite(inviteKey) {
		return GROUP_INVITE_ALREADY_HAS_INVITE
	}

	// Check if member is already in a group
	// TODO: Check if member already in group
	// if member.GetGroupMemberInfo() != nil {
	//     return GROUP_INVITE_ALREADY_IN_GROUP
	// }

	// Add the invite
	if !gm.addInvite(leader, member) {
		return GROUP_INVITE_PERMISSION_DENIED
	}

	// Fire event
	gm.fireGroupInviteSentEvent(leader, member)

	return GROUP_INVITE_SUCCESS
}

// AddInvite adds a group invitation
func (gm *GroupManager) AddInvite(leader entity.Entity, member entity.Entity) bool {
	return gm.addInvite(leader, member)
}

// addInvite internal method to add an invitation
func (gm *GroupManager) addInvite(leader entity.Entity, member entity.Entity) bool {
	if leader == nil || member == nil {
		return false
	}

	inviteKey := member.GetName()
	leaderName := leader.GetName()

	invite := &GroupInvite{
		InviterName:  leaderName,
		InviteeName:  inviteKey,
		GroupID:      0, // Will be set when group is created
		IsRaidInvite: false,
		CreatedTime:  time.Now(),
		ExpiresTime:  time.Now().Add(gm.config.InviteTimeout),
	}

	gm.invitesMutex.Lock()
	gm.pendingInvites[inviteKey] = invite
	gm.invitesMutex.Unlock()

	// Update statistics
	gm.updateStatsForInvite()

	return true
}

// AcceptInvite handles accepting of a group invite
func (gm *GroupManager) AcceptInvite(member entity.Entity, groupOverrideID *int32, autoAddGroup bool) int8 {
	if member == nil {
		return GROUP_INVITE_TARGET_NOT_FOUND
	}

	inviteKey := member.GetName()

	gm.invitesMutex.Lock()
	invite, exists := gm.pendingInvites[inviteKey]
	if !exists {
		gm.invitesMutex.Unlock()
		return GROUP_INVITE_TARGET_NOT_FOUND
	}

	// Check if invite has expired
	if invite.IsExpired() {
		delete(gm.pendingInvites, inviteKey)
		gm.invitesMutex.Unlock()
		gm.updateStatsForExpiredInvite()
		return GROUP_INVITE_DECLINED
	}

	// Remove the invite
	delete(gm.pendingInvites, inviteKey)
	gm.invitesMutex.Unlock()

	if !autoAddGroup {
		return GROUP_INVITE_SUCCESS
	}

	// Find the leader
	var leader entity.Entity
	// TODO: Find leader entity by name
	// leader = world.GetPlayerByName(invite.InviterName)

	if leader == nil {
		return GROUP_INVITE_TARGET_NOT_FOUND
	}

	var groupID int32
	if groupOverrideID != nil {
		groupID = *groupOverrideID
	}

	// Check if leader already has a group
	// TODO: Get leader's group ID
	// leaderGroupID := leader.GetGroupID()

	leaderGroupID := int32(0) // Placeholder

	if leaderGroupID == 0 {
		// Create new group with leader
		var err error
		if groupID != 0 {
			groupID, err = gm.NewGroup(leader, nil, groupID)
		} else {
			groupID, err = gm.NewGroup(leader, nil, 0)
		}
		if err != nil {
			return GROUP_INVITE_PERMISSION_DENIED
		}
	} else {
		groupID = leaderGroupID
	}

	// Add member to the group
	if err := gm.AddGroupMember(groupID, member, false); err != nil {
		return GROUP_INVITE_GROUP_FULL
	}

	// Update statistics
	gm.updateStatsForAcceptedInvite()

	// Fire event
	gm.fireGroupInviteAcceptedEvent(leader, member, groupID)

	return GROUP_INVITE_SUCCESS
}

// DeclineInvite handles declining of a group invite
func (gm *GroupManager) DeclineInvite(member entity.Entity) {
	if member == nil {
		return
	}

	inviteKey := member.GetName()

	gm.invitesMutex.Lock()
	invite, exists := gm.pendingInvites[inviteKey]
	if exists {
		delete(gm.pendingInvites, inviteKey)
	}
	gm.invitesMutex.Unlock()

	if exists {
		// Update statistics
		gm.updateStatsForDeclinedInvite()

		// Fire event
		var leader entity.Entity
		// TODO: Find leader entity by name
		// leader = world.GetPlayerByName(invite.InviterName)
		gm.fireGroupInviteDeclinedEvent(leader, member)
	}
}

// ClearPendingInvite clears a pending invite for a member
func (gm *GroupManager) ClearPendingInvite(member entity.Entity) {
	if member == nil {
		return
	}

	inviteKey := member.GetName()

	gm.invitesMutex.Lock()
	delete(gm.pendingInvites, inviteKey)
	gm.invitesMutex.Unlock()
}

// HasPendingInvite checks if a member has a pending invite and returns the inviter name
func (gm *GroupManager) HasPendingInvite(member entity.Entity) string {
	if member == nil {
		return ""
	}

	inviteKey := member.GetName()
	return gm.hasPendingInvite(inviteKey)
}

// hasPendingInvite internal method to check for pending invites
func (gm *GroupManager) hasPendingInvite(inviteKey string) string {
	gm.invitesMutex.RLock()
	defer gm.invitesMutex.RUnlock()

	if invite, exists := gm.pendingInvites[inviteKey]; exists {
		if !invite.IsExpired() {
			return invite.InviterName
		}
	}

	return ""
}

// Group utility methods

// GetGroupSize returns the size of a group
func (gm *GroupManager) GetGroupSize(groupID int32) int32 {
	group := gm.GetGroup(groupID)
	if group == nil {
		return 0
	}
	return group.GetSize()
}

// IsInGroup checks if an entity is in a specific group
func (gm *GroupManager) IsInGroup(groupID int32, member entity.Entity) bool {
	group := gm.GetGroup(groupID)
	if group == nil || member == nil {
		return false
	}

	members := group.GetMembers()
	for _, gmi := range members {
		if gmi.Member == member {
			return true
		}
	}

	return false
}

// IsPlayerInGroup checks if a player with the given character ID is in a group
func (gm *GroupManager) IsPlayerInGroup(groupID int32, charID int32) entity.Entity {
	group := gm.GetGroup(groupID)
	if group == nil {
		return nil
	}

	members := group.GetMembers()
	for _, gmi := range members {
		if gmi.IsClient && gmi.Member != nil {
			// TODO: Check character ID
			// if gmi.Member.GetCharacterID() == charID {
			//     return gmi.Member
			// }
		}
	}

	return nil
}

// IsSpawnInGroup checks if a spawn with the given name is in a group
func (gm *GroupManager) IsSpawnInGroup(groupID int32, name string) bool {
	group := gm.GetGroup(groupID)
	if group == nil {
		return false
	}

	members := group.GetMembers()
	for _, gmi := range members {
		if gmi.Name == name {
			return true
		}
	}

	return false
}

// GetGroupLeader returns the leader of a group
func (gm *GroupManager) GetGroupLeader(groupID int32) entity.Entity {
	group := gm.GetGroup(groupID)
	if group == nil {
		return nil
	}

	members := group.GetMembers()
	for _, gmi := range members {
		if gmi.Leader {
			return gmi.Member
		}
	}

	return nil
}

// MakeLeader changes the leader of a group
func (gm *GroupManager) MakeLeader(groupID int32, newLeader entity.Entity) bool {
	group := gm.GetGroup(groupID)
	if group == nil {
		return false
	}

	err := group.MakeLeader(newLeader)
	return err == nil
}

// Group messaging

// SimpleGroupMessage sends a simple message to all members of a group
func (gm *GroupManager) SimpleGroupMessage(groupID int32, message string) {
	group := gm.GetGroup(groupID)
	if group != nil {
		group.SimpleGroupMessage(message)
	}
}

// SendGroupMessage sends a formatted message to all members of a group
func (gm *GroupManager) SendGroupMessage(groupID int32, msgType int8, message string) {
	group := gm.GetGroup(groupID)
	if group != nil {
		group.SendGroupMessage(msgType, message)
	}
}

// GroupMessage sends a message to all members of a group (alias for SimpleGroupMessage)
func (gm *GroupManager) GroupMessage(groupID int32, message string) {
	gm.SimpleGroupMessage(groupID, message)
}

// GroupChatMessage sends a chat message from a member to the group
func (gm *GroupManager) GroupChatMessage(groupID int32, from entity.Entity, language int32, message string, channel int16) {
	group := gm.GetGroup(groupID)
	if group != nil {
		group.GroupChatMessage(from, language, message, channel)
	}
}

// GroupChatMessageFromName sends a chat message from a named sender to the group
func (gm *GroupManager) GroupChatMessageFromName(groupID int32, fromName string, language int32, message string, channel int16) {
	group := gm.GetGroup(groupID)
	if group != nil {
		group.GroupChatMessageFromName(fromName, language, message, channel)
	}
}

// SendGroupChatMessage sends a formatted chat message to the group
func (gm *GroupManager) SendGroupChatMessage(groupID int32, channel int16, message string) {
	gm.GroupChatMessageFromName(groupID, "System", 0, message, channel)
}

// Raid functionality

// ClearGroupRaid clears raid associations for a group
func (gm *GroupManager) ClearGroupRaid(groupID int32) {
	group := gm.GetGroup(groupID)
	if group != nil {
		group.ClearGroupRaid()
	}
}

// RemoveGroupFromRaid removes a group from a raid
func (gm *GroupManager) RemoveGroupFromRaid(groupID, targetGroupID int32) {
	group := gm.GetGroup(groupID)
	if group != nil {
		group.RemoveGroupFromRaid(targetGroupID)
	}
}

// IsInRaidGroup checks if two groups are in the same raid
func (gm *GroupManager) IsInRaidGroup(groupID, targetGroupID int32, isLeaderGroup bool) bool {
	group := gm.GetGroup(groupID)
	if group == nil {
		return false
	}
	return group.IsInRaidGroup(targetGroupID, isLeaderGroup)
}

// GetRaidGroups returns the raid groups for a specific group
func (gm *GroupManager) GetRaidGroups(groupID int32) []int32 {
	group := gm.GetGroup(groupID)
	if group == nil {
		return []int32{}
	}
	return group.GetRaidGroups()
}

// ReplaceRaidGroups replaces the raid groups for a specific group
func (gm *GroupManager) ReplaceRaidGroups(groupID int32, newGroups []int32) {
	group := gm.GetGroup(groupID)
	if group != nil {
		group.ReplaceRaidGroups(newGroups)
	}
}

// Group options

// GetDefaultGroupOptions returns the default group options for a group
func (gm *GroupManager) GetDefaultGroupOptions(groupID int32) (GroupOptions, bool) {
	group := gm.GetGroup(groupID)
	if group == nil {
		return GroupOptions{}, false
	}
	return group.GetGroupOptions(), true
}

// SetGroupOptions sets group options for a specific group
func (gm *GroupManager) SetGroupOptions(groupID int32, options *GroupOptions) error {
	group := gm.GetGroup(groupID)
	if group == nil {
		return fmt.Errorf("group %d not found", groupID)
	}
	return group.SetGroupOptions(options)
}

// Utility methods

// generateNextGroupID generates the next available group ID
func (gm *GroupManager) generateNextGroupID() int32 {
	gm.nextGroupIDMutex.Lock()
	defer gm.nextGroupIDMutex.Unlock()

	id := gm.nextGroupID
	gm.nextGroupID++

	// Handle overflow
	if gm.nextGroupID <= 0 {
		gm.nextGroupID = 1
	}

	return id
}

// GetGroupCount returns the number of active groups
func (gm *GroupManager) GetGroupCount() int32 {
	gm.groupsMutex.RLock()
	defer gm.groupsMutex.RUnlock()

	return int32(len(gm.groups))
}

// GetAllGroups returns all active groups
func (gm *GroupManager) GetAllGroups() []*Group {
	gm.groupsMutex.RLock()
	defer gm.groupsMutex.RUnlock()

	groups := make([]*Group, 0, len(gm.groups))
	for _, group := range gm.groups {
		groups = append(groups, group)
	}

	return groups
}

// Background processing loops

// updateGroupsLoop periodically updates all groups
func (gm *GroupManager) updateGroupsLoop() {
	defer gm.wg.Done()

	ticker := time.NewTicker(gm.config.UpdateInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			gm.processGroupUpdates()
		case <-gm.stopChan:
			return
		}
	}
}

// updateBuffsLoop periodically updates group buffs
func (gm *GroupManager) updateBuffsLoop() {
	defer gm.wg.Done()

	ticker := time.NewTicker(gm.config.BuffUpdateInterval)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			gm.updateGroupBuffs()
		case <-gm.stopChan:
			return
		}
	}
}

// cleanupExpiredInvitesLoop periodically cleans up expired invites
func (gm *GroupManager) cleanupExpiredInvitesLoop() {
	defer gm.wg.Done()

	ticker := time.NewTicker(30 * time.Second) // Check every 30 seconds
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			gm.cleanupExpiredInvites()
		case <-gm.stopChan:
			return
		}
	}
}

// updateStatsLoop periodically updates statistics
func (gm *GroupManager) updateStatsLoop() {
	defer gm.wg.Done()

	ticker := time.NewTicker(1 * time.Minute) // Update stats every minute
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			gm.updateStatistics()
		case <-gm.stopChan:
			return
		}
	}
}

// processGroupUpdates processes periodic group updates
func (gm *GroupManager) processGroupUpdates() {
	groups := gm.GetAllGroups()

	for _, group := range groups {
		if !group.IsDisbanded() {
			// Update member information
			members := group.GetMembers()
			for _, gmi := range members {
				if gmi.Member != nil {
					group.UpdateGroupMemberInfo(gmi.Member, false)
				}
			}
		}
	}
}

// updateGroupBuffs updates group buffs for all groups
func (gm *GroupManager) updateGroupBuffs() {
	// TODO: Implement group buff updates
	// This would require integration with the spell/buff system
}

// cleanupExpiredInvites removes expired invitations
func (gm *GroupManager) cleanupExpiredInvites() {
	gm.invitesMutex.Lock()
	defer gm.invitesMutex.Unlock()

	now := time.Now()
	expiredCount := 0

	// Clean up regular invites
	for key, invite := range gm.pendingInvites {
		if now.After(invite.ExpiresTime) {
			delete(gm.pendingInvites, key)
			expiredCount++
		}
	}

	// Clean up raid invites
	for key, invite := range gm.raidPendingInvites {
		if now.After(invite.ExpiresTime) {
			delete(gm.raidPendingInvites, key)
			expiredCount++
		}
	}

	// Update statistics
	if expiredCount > 0 {
		gm.statsMutex.Lock()
		gm.stats.ExpiredInvites += int64(expiredCount)
		gm.statsMutex.Unlock()
	}
}

// updateStatistics updates manager statistics
func (gm *GroupManager) updateStatistics() {
	if !gm.config.EnableStatistics {
		return
	}

	gm.statsMutex.Lock()
	defer gm.statsMutex.Unlock()

	gm.groupsMutex.RLock()
	activeGroups := int64(len(gm.groups))

	var totalMembers int64
	var raidCount int64

	for _, group := range gm.groups {
		totalMembers += int64(group.GetSize())
		if group.IsGroupRaid() {
			raidCount++
		}
	}
	gm.groupsMutex.RUnlock()

	gm.stats.ActiveGroups = activeGroups
	gm.stats.ActiveRaids = raidCount

	if activeGroups > 0 {
		gm.stats.AverageGroupSize = float64(totalMembers) / float64(activeGroups)
	} else {
		gm.stats.AverageGroupSize = 0
	}

	gm.stats.LastStatsUpdate = time.Now()
}

// Statistics update methods

// updateStatsForNewGroup updates statistics when a new group is created
func (gm *GroupManager) updateStatsForNewGroup() {
	if !gm.config.EnableStatistics {
		return
	}

	gm.statsMutex.Lock()
	defer gm.statsMutex.Unlock()

	gm.stats.TotalGroups++
}

// updateStatsForRemovedGroup updates statistics when a group is removed
func (gm *GroupManager) updateStatsForRemovedGroup() {
	// Statistics are primarily tracked in updateStatistics()
}

// updateStatsForInvite updates statistics when an invite is sent
func (gm *GroupManager) updateStatsForInvite() {
	if !gm.config.EnableStatistics {
		return
	}

	gm.statsMutex.Lock()
	defer gm.statsMutex.Unlock()

	gm.stats.TotalInvites++
}

// updateStatsForAcceptedInvite updates statistics when an invite is accepted
func (gm *GroupManager) updateStatsForAcceptedInvite() {
	if !gm.config.EnableStatistics {
		return
	}

	gm.statsMutex.Lock()
	defer gm.statsMutex.Unlock()

	gm.stats.AcceptedInvites++
}

// updateStatsForDeclinedInvite updates statistics when an invite is declined
func (gm *GroupManager) updateStatsForDeclinedInvite() {
	if !gm.config.EnableStatistics {
		return
	}

	gm.statsMutex.Lock()
	defer gm.statsMutex.Unlock()

	gm.stats.DeclinedInvites++
}

// updateStatsForExpiredInvite updates statistics when an invite expires
func (gm *GroupManager) updateStatsForExpiredInvite() {
	if !gm.config.EnableStatistics {
		return
	}

	gm.statsMutex.Lock()
	defer gm.statsMutex.Unlock()

	gm.stats.ExpiredInvites++
}

// GetStats returns current manager statistics
func (gm *GroupManager) GetStats() GroupManagerStats {
	gm.statsMutex.RLock()
	defer gm.statsMutex.RUnlock()

	return gm.stats
}

// Event system integration

// AddEventHandler adds an event handler
func (gm *GroupManager) AddEventHandler(handler GroupEventHandler) {
	gm.eventHandlersMutex.Lock()
	defer gm.eventHandlersMutex.Unlock()

	gm.eventHandlers = append(gm.eventHandlers, handler)
}

// Integration interfaces

// SetDatabase sets the database interface
func (gm *GroupManager) SetDatabase(db GroupDatabase) {
	gm.database = db
}

// SetPacketHandler sets the packet handler interface
func (gm *GroupManager) SetPacketHandler(handler GroupPacketHandler) {
	gm.packetHandler = handler
}

// SetValidator sets the validator interface
func (gm *GroupManager) SetValidator(validator GroupValidator) {
	gm.validator = validator
}

// SetNotifier sets the notifier interface
func (gm *GroupManager) SetNotifier(notifier GroupNotifier) {
	gm.notifier = notifier
}

// Event firing methods

// fireGroupCreatedEvent fires a group created event
func (gm *GroupManager) fireGroupCreatedEvent(group *Group, leader entity.Entity) {
	gm.eventHandlersMutex.RLock()
	defer gm.eventHandlersMutex.RUnlock()

	for _, handler := range gm.eventHandlers {
		go handler.OnGroupCreated(group, leader)
	}
}

// fireGroupDisbandedEvent fires a group disbanded event
func (gm *GroupManager) fireGroupDisbandedEvent(group *Group) {
	gm.eventHandlersMutex.RLock()
	defer gm.eventHandlersMutex.RUnlock()

	for _, handler := range gm.eventHandlers {
		go handler.OnGroupDisbanded(group)
	}
}

// fireGroupInviteSentEvent fires a group invite sent event
func (gm *GroupManager) fireGroupInviteSentEvent(leader, member entity.Entity) {
	gm.eventHandlersMutex.RLock()
	defer gm.eventHandlersMutex.RUnlock()

	for _, handler := range gm.eventHandlers {
		go handler.OnGroupInviteSent(leader, member)
	}
}

// fireGroupInviteAcceptedEvent fires a group invite accepted event
func (gm *GroupManager) fireGroupInviteAcceptedEvent(leader, member entity.Entity, groupID int32) {
	gm.eventHandlersMutex.RLock()
	defer gm.eventHandlersMutex.RUnlock()

	for _, handler := range gm.eventHandlers {
		go handler.OnGroupInviteAccepted(leader, member, groupID)
	}
}

// fireGroupInviteDeclinedEvent fires a group invite declined event
func (gm *GroupManager) fireGroupInviteDeclinedEvent(leader, member entity.Entity) {
	gm.eventHandlersMutex.RLock()
	defer gm.eventHandlersMutex.RUnlock()

	for _, handler := range gm.eventHandlers {
		go handler.OnGroupInviteDeclined(leader, member)
	}
}
