package groups

import (
	"eq2emu/internal/entity"
	"time"
)

// GroupAware interface for entities that can be part of groups
type GroupAware interface {
	// GetGroupMemberInfo returns the group member info for this entity
	GetGroupMemberInfo() *GroupMemberInfo

	// SetGroupMemberInfo sets the group member info for this entity
	SetGroupMemberInfo(info *GroupMemberInfo)

	// GetGroupID returns the current group ID
	GetGroupID() int32

	// SetGroupID sets the current group ID
	SetGroupID(groupID int32)

	// IsInGroup returns true if the entity is in a group
	IsInGroup() bool
}

// GroupManager interface for managing groups
type GroupManagerInterface interface {
	// Group creation and management
	NewGroup(leader entity.Entity, options *GroupOptions, overrideGroupID int32) (int32, error)
	RemoveGroup(groupID int32) error
	GetGroup(groupID int32) *Group
	IsGroupIDValid(groupID int32) bool

	// Member management
	AddGroupMember(groupID int32, member entity.Entity, isLeader bool) error
	AddGroupMemberFromPeer(groupID int32, info *GroupMemberInfo) error
	RemoveGroupMember(groupID int32, member entity.Entity) error
	RemoveGroupMemberByName(groupID int32, name string, isClient bool, charID int32) error

	// Group updates
	SendGroupUpdate(groupID int32, excludeClient interface{}, forceRaidUpdate bool)

	// Invitations
	Invite(leader entity.Entity, member entity.Entity) int8
	AddInvite(leader entity.Entity, member entity.Entity) bool
	AcceptInvite(member entity.Entity, groupOverrideID *int32, autoAddGroup bool) int8
	DeclineInvite(member entity.Entity)
	ClearPendingInvite(member entity.Entity)
	HasPendingInvite(member entity.Entity) string

	// Group utilities
	GetGroupSize(groupID int32) int32
	IsInGroup(groupID int32, member entity.Entity) bool
	IsPlayerInGroup(groupID int32, charID int32) entity.Entity
	IsSpawnInGroup(groupID int32, name string) bool
	GetGroupLeader(groupID int32) entity.Entity
	MakeLeader(groupID int32, newLeader entity.Entity) bool

	// Messaging
	SimpleGroupMessage(groupID int32, message string)
	SendGroupMessage(groupID int32, msgType int8, message string)
	GroupMessage(groupID int32, message string)
	GroupChatMessage(groupID int32, from entity.Entity, language int32, message string, channel int16)
	GroupChatMessageFromName(groupID int32, fromName string, language int32, message string, channel int16)
	SendGroupChatMessage(groupID int32, channel int16, message string)

	// Raid functionality
	ClearGroupRaid(groupID int32)
	RemoveGroupFromRaid(groupID, targetGroupID int32)
	IsInRaidGroup(groupID, targetGroupID int32, isLeaderGroup bool) bool
	GetRaidGroups(groupID int32) []int32
	ReplaceRaidGroups(groupID int32, newGroups []int32)

	// Group options
	GetDefaultGroupOptions(groupID int32) (GroupOptions, bool)
	SetGroupOptions(groupID int32, options *GroupOptions) error

	// Statistics
	GetStats() GroupManagerStats
	GetGroupCount() int32
	GetAllGroups() []*Group
}

// GroupEventHandler interface for handling group events
type GroupEventHandler interface {
	// Group lifecycle events
	OnGroupCreated(group *Group, leader entity.Entity) error
	OnGroupDisbanded(group *Group) error
	OnGroupMemberJoined(group *Group, member entity.Entity) error
	OnGroupMemberLeft(group *Group, member entity.Entity) error
	OnGroupLeaderChanged(group *Group, oldLeader, newLeader entity.Entity) error

	// Invitation events
	OnGroupInviteSent(leader, member entity.Entity) error
	OnGroupInviteAccepted(leader, member entity.Entity, groupID int32) error
	OnGroupInviteDeclined(leader, member entity.Entity) error
	OnGroupInviteExpired(leader, member entity.Entity) error

	// Raid events
	OnRaidFormed(groups []*Group) error
	OnRaidDisbanded(groups []*Group) error
	OnRaidInviteSent(leaderGroup *Group, targetGroup *Group) error
	OnRaidInviteAccepted(leaderGroup *Group, targetGroup *Group) error
	OnRaidInviteDeclined(leaderGroup *Group, targetGroup *Group) error

	// Group activity events
	OnGroupMessage(group *Group, from entity.Entity, message string, channel int16) error
	OnGroupOptionsChanged(group *Group, oldOptions, newOptions *GroupOptions) error
	OnGroupMemberUpdate(group *Group, member *GroupMemberInfo) error
}

// GroupDatabase interface for database operations
type GroupDatabase interface {
	// Group persistence
	SaveGroup(group *Group) error
	LoadGroup(groupID int32) (*Group, error)
	DeleteGroup(groupID int32) error

	// Group member persistence
	SaveGroupMember(groupID int32, member *GroupMemberInfo) error
	LoadGroupMembers(groupID int32) ([]*GroupMemberInfo, error)
	DeleteGroupMember(groupID int32, memberName string) error

	// Group options persistence
	SaveGroupOptions(groupID int32, options *GroupOptions) error
	LoadGroupOptions(groupID int32) (*GroupOptions, error)

	// Raid persistence
	SaveRaidGroups(groupID int32, raidGroups []int32) error
	LoadRaidGroups(groupID int32) ([]int32, error)

	// Statistics persistence
	SaveGroupStats(stats *GroupManagerStats) error
	LoadGroupStats() (*GroupManagerStats, error)

	// Cleanup operations
	CleanupExpiredGroups() error
	CleanupOrphanedMembers() error
}

// GroupPacketHandler interface for handling group-related packets
type GroupPacketHandler interface {
	// Group update packets
	SendGroupUpdate(members []*GroupMemberInfo, excludeClient interface{}) error
	SendGroupMemberUpdate(member *GroupMemberInfo, excludeClient interface{}) error
	SendGroupOptionsUpdate(groupID int32, options *GroupOptions, excludeClient interface{}) error

	// Group invitation packets
	SendGroupInvite(inviter, invitee entity.Entity) error
	SendGroupInviteResponse(inviter, invitee entity.Entity, accepted bool) error

	// Group messaging packets
	SendGroupMessage(members []*GroupMemberInfo, message *GroupMessage) error
	SendGroupChatMessage(members []*GroupMemberInfo, from string, message string, channel int16, language int32) error

	// Raid packets
	SendRaidUpdate(raidGroups []*Group, excludeClient interface{}) error
	SendRaidInvite(leaderGroup, targetGroup *Group) error
	SendRaidInviteResponse(leaderGroup, targetGroup *Group, accepted bool) error

	// Group UI packets
	SendGroupWindowUpdate(client interface{}, group *Group) error
	SendRaidWindowUpdate(client interface{}, raidGroups []*Group) error

	// Group member packets
	SendGroupMemberStats(member *GroupMemberInfo, excludeClient interface{}) error
	SendGroupMemberZoneChange(member *GroupMemberInfo, oldZoneID, newZoneID int32) error
}

// GroupValidator interface for validating group operations
type GroupValidator interface {
	// Group creation validation
	ValidateGroupCreation(leader entity.Entity, options *GroupOptions) error
	ValidateGroupJoin(group *Group, member entity.Entity) error
	ValidateGroupLeave(group *Group, member entity.Entity) error

	// Invitation validation
	ValidateGroupInvite(leader, member entity.Entity) error
	ValidateRaidInvite(leaderGroup, targetGroup *Group) error

	// Group operation validation
	ValidateLeadershipChange(group *Group, oldLeader, newLeader entity.Entity) error
	ValidateGroupOptions(group *Group, options *GroupOptions) error
	ValidateGroupMessage(group *Group, from entity.Entity, message string) error

	// Raid validation
	ValidateRaidFormation(groups []*Group) error
	ValidateRaidOperation(raidGroups []*Group, operation string) error
}

// GroupNotifier interface for sending notifications
type GroupNotifier interface {
	// Group notifications
	NotifyGroupCreated(group *Group, leader entity.Entity) error
	NotifyGroupDisbanded(group *Group, reason string) error
	NotifyGroupMemberJoined(group *Group, member entity.Entity) error
	NotifyGroupMemberLeft(group *Group, member entity.Entity, reason string) error
	NotifyGroupLeaderChanged(group *Group, oldLeader, newLeader entity.Entity) error

	// Invitation notifications
	NotifyGroupInviteSent(leader, member entity.Entity) error
	NotifyGroupInviteReceived(leader, member entity.Entity) error
	NotifyGroupInviteAccepted(leader, member entity.Entity, groupID int32) error
	NotifyGroupInviteDeclined(leader, member entity.Entity) error
	NotifyGroupInviteExpired(leader, member entity.Entity) error

	// Raid notifications
	NotifyRaidFormed(groups []*Group) error
	NotifyRaidDisbanded(groups []*Group, reason string) error
	NotifyRaidInviteSent(leaderGroup, targetGroup *Group) error
	NotifyRaidInviteReceived(leaderGroup, targetGroup *Group) error
	NotifyRaidInviteAccepted(leaderGroup, targetGroup *Group) error
	NotifyRaidInviteDeclined(leaderGroup, targetGroup *Group) error

	// System notifications
	NotifyGroupSystemMessage(group *Group, message string, msgType int8) error
	NotifyGroupError(group *Group, error string, errorCode int8) error
}

// GroupStatistics interface for tracking group statistics
type GroupStatistics interface {
	// Group statistics
	RecordGroupCreated(group *Group, leader entity.Entity)
	RecordGroupDisbanded(group *Group, duration int64)
	RecordGroupMemberJoined(group *Group, member entity.Entity)
	RecordGroupMemberLeft(group *Group, member entity.Entity, duration int64)

	// Invitation statistics
	RecordInviteSent(leader, member entity.Entity)
	RecordInviteAccepted(leader, member entity.Entity, responseTime int64)
	RecordInviteDeclined(leader, member entity.Entity, responseTime int64)
	RecordInviteExpired(leader, member entity.Entity)

	// Raid statistics
	RecordRaidFormed(groups []*Group)
	RecordRaidDisbanded(groups []*Group, duration int64)

	// Activity statistics
	RecordGroupMessage(group *Group, from entity.Entity, messageType int8)
	RecordGroupActivity(group *Group, activityType string)

	// Performance statistics
	RecordGroupProcessingTime(operation string, duration int64)
	RecordGroupMemoryUsage(groups int32, members int32)

	// Statistics retrieval
	GetGroupStatistics(groupID int32) map[string]interface{}
	GetOverallStatistics() map[string]interface{}
	GetStatisticsSummary() *GroupManagerStats
}

// GroupAdapter adapts group functionality for other systems
type GroupAdapter struct {
	group *Group
}

// NewGroupAdapter creates a new group adapter
func NewGroupAdapter(group *Group) *GroupAdapter {
	return &GroupAdapter{group: group}
}

// GetGroup returns the wrapped group
func (ga *GroupAdapter) GetGroup() *Group {
	return ga.group
}

// GetGroupID returns the group ID
func (ga *GroupAdapter) GetGroupID() int32 {
	return ga.group.GetID()
}

// GetGroupSize returns the group size
func (ga *GroupAdapter) GetGroupSize() int32 {
	return ga.group.GetSize()
}

// GetMembers returns group members
func (ga *GroupAdapter) GetMembers() []*GroupMemberInfo {
	return ga.group.GetMembers()
}

// GetLeader returns the group leader
func (ga *GroupAdapter) GetLeader() entity.Entity {
	members := ga.group.GetMembers()
	for _, member := range members {
		if member.Leader {
			return member.Member
		}
	}
	return nil
}

// GetLeaderName returns the group leader's name
func (ga *GroupAdapter) GetLeaderName() string {
	return ga.group.GetLeaderName()
}

// IsInRaid returns true if the group is part of a raid
func (ga *GroupAdapter) IsInRaid() bool {
	return ga.group.IsGroupRaid()
}

// GetRaidGroups returns the raid groups
func (ga *GroupAdapter) GetRaidGroups() []int32 {
	return ga.group.GetRaidGroups()
}

// IsMember checks if an entity is a member of the group
func (ga *GroupAdapter) IsMember(entity entity.Entity) bool {
	if entity == nil {
		return false
	}

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

// HasMemberNamed checks if the group has a member with the given name
func (ga *GroupAdapter) HasMemberNamed(name string) bool {
	members := ga.group.GetMembers()
	for _, member := range members {
		if member.Name == name {
			return true
		}
	}
	return false
}

// GetMemberByName returns a member by name
func (ga *GroupAdapter) GetMemberByName(name string) *GroupMemberInfo {
	members := ga.group.GetMembers()
	for _, member := range members {
		if member.Name == name {
			return member
		}
	}
	return nil
}

// GetMemberByEntity returns a member by entity
func (ga *GroupAdapter) GetMemberByEntity(entity entity.Entity) *GroupMemberInfo {
	if entity == nil {
		return nil
	}

	members := ga.group.GetMembers()
	for _, member := range members {
		if member.Member == entity {
			return member
		}
	}
	return nil
}

// IsLeader checks if an entity is the group leader
func (ga *GroupAdapter) IsLeader(entity entity.Entity) bool {
	if entity == nil {
		return false
	}

	members := ga.group.GetMembers()
	for _, member := range members {
		if member.Member == entity && member.Leader {
			return true
		}
	}
	return false
}

// GetOptions returns the group options
func (ga *GroupAdapter) GetOptions() GroupOptions {
	return ga.group.GetGroupOptions()
}

// IsDisbanded returns true if the group has been disbanded
func (ga *GroupAdapter) IsDisbanded() bool {
	return ga.group.IsDisbanded()
}

// GetCreatedTime returns when the group was created
func (ga *GroupAdapter) GetCreatedTime() time.Time {
	return ga.group.GetCreatedTime()
}

// GetLastActivity returns the last activity time
func (ga *GroupAdapter) GetLastActivity() time.Time {
	return ga.group.GetLastActivity()
}

// EntityGroupAdapter adapts entity functionality for group systems
type EntityGroupAdapter struct {
	entity entity.Entity
}

// NewEntityGroupAdapter creates a new entity group adapter
func NewEntityGroupAdapter(entity entity.Entity) *EntityGroupAdapter {
	return &EntityGroupAdapter{entity: entity}
}

// GetEntity returns the wrapped entity
func (ega *EntityGroupAdapter) GetEntity() entity.Entity {
	return ega.entity
}

// GetName returns the entity name
func (ega *EntityGroupAdapter) GetName() string {
	return ega.entity.GetName()
}

// GetLevel returns the entity level
func (ega *EntityGroupAdapter) GetLevel() int8 {
	return ega.entity.GetLevel()
}

// GetClass returns the entity class
func (ega *EntityGroupAdapter) GetClass() int8 {
	return ega.entity.GetClass()
}

// GetRace returns the entity race
func (ega *EntityGroupAdapter) GetRace() int8 {
	return ega.entity.GetRace()
}

// GetZoneID returns the current zone ID
func (ega *EntityGroupAdapter) GetZoneID() int32 {
	if zone := ega.entity.GetZone(); zone != nil {
		return zone.GetZoneID()
	}
	return 0
}

// GetInstanceID returns the current instance ID
func (ega *EntityGroupAdapter) GetInstanceID() int32 {
	if zone := ega.entity.GetZone(); zone != nil {
		return zone.GetInstanceID()
	}
	return 0
}

// GetZoneName returns the current zone name
func (ega *EntityGroupAdapter) GetZoneName() string {
	if zone := ega.entity.GetZone(); zone != nil {
		return zone.GetZoneName()
	}
	return ""
}

// GetHP returns current HP
func (ega *EntityGroupAdapter) GetHP() int32 {
	return ega.entity.GetHP()
}

// GetMaxHP returns maximum HP
func (ega *EntityGroupAdapter) GetMaxHP() int32 {
	return ega.entity.GetTotalHP()
}

// GetPower returns current power
func (ega *EntityGroupAdapter) GetPower() int32 {
	return ega.entity.GetPower()
}

// GetMaxPower returns maximum power
func (ega *EntityGroupAdapter) GetMaxPower() int32 {
	return ega.entity.GetTotalPower()
}

// IsPlayer returns true if the entity is a player
func (ega *EntityGroupAdapter) IsPlayer() bool {
	return ega.entity.IsPlayer()
}

// IsNPC returns true if the entity is an NPC
func (ega *EntityGroupAdapter) IsNPC() bool {
	return ega.entity.IsNPC()
}

// IsBot returns true if the entity is a bot
func (ega *EntityGroupAdapter) IsBot() bool {
	return ega.entity.IsBot()
}

// IsAlive returns true if the entity is alive
func (ega *EntityGroupAdapter) IsAlive() bool {
	return !ega.entity.IsDead()
}

// IsDead returns true if the entity is dead
func (ega *EntityGroupAdapter) IsDead() bool {
	return ega.entity.IsDead()
}

// GetDistance returns distance to another entity
func (ega *EntityGroupAdapter) GetDistance(other entity.Entity) float32 {
	return ega.entity.GetDistance(&other.Spawn)
}
