package groups

import (
	"fmt"
	"sync"
	"time"

	"eq2emu/internal/entity"
)

// Service provides a high-level interface for group management
type Service struct {
	manager    *GroupManager
	config     ServiceConfig
	started    bool
	startMutex sync.Mutex
}

// ServiceConfig holds configuration for the group service
type ServiceConfig struct {
	// Group manager configuration
	ManagerConfig GroupManagerConfig `json:"manager_config"`

	// Service-specific settings
	AutoCreateGroups     bool    `json:"auto_create_groups"`
	AllowCrossZoneGroups bool    `json:"allow_cross_zone_groups"`
	AllowBotMembers      bool    `json:"allow_bot_members"`
	AllowNPCMembers      bool    `json:"allow_npc_members"`
	MaxInviteDistance    float32 `json:"max_invite_distance"`
	GroupLevelRange      int8    `json:"group_level_range"`
	EnableGroupPvP       bool    `json:"enable_group_pvp"`
	EnableGroupBuffs     bool    `json:"enable_group_buffs"`
	LogLevel             string  `json:"log_level"`

	// Integration settings
	DatabaseEnabled   bool `json:"database_enabled"`
	EventsEnabled     bool `json:"events_enabled"`
	StatisticsEnabled bool `json:"statistics_enabled"`
	ValidationEnabled bool `json:"validation_enabled"`
}

// DefaultServiceConfig returns default service configuration
func DefaultServiceConfig() ServiceConfig {
	return ServiceConfig{
		ManagerConfig: GroupManagerConfig{
			MaxGroups:          1000,
			MaxRaidGroups:      4,
			InviteTimeout:      30 * time.Second,
			UpdateInterval:     1 * time.Second,
			BuffUpdateInterval: 5 * time.Second,
			EnableCrossServer:  false,
			EnableRaids:        true,
			EnableQuestSharing: true,
			EnableAutoInvite:   false,
			EnableStatistics:   true,
		},
		AutoCreateGroups:     true,
		AllowCrossZoneGroups: true,
		AllowBotMembers:      true,
		AllowNPCMembers:      false,
		MaxInviteDistance:    100.0,
		GroupLevelRange:      10,
		EnableGroupPvP:       false,
		EnableGroupBuffs:     true,
		LogLevel:             "info",
		DatabaseEnabled:      true,
		EventsEnabled:        true,
		StatisticsEnabled:    true,
		ValidationEnabled:    true,
	}
}

// NewService creates a new group service
func NewService(config ServiceConfig) *Service {
	return &Service{
		manager: NewGroupManager(config.ManagerConfig),
		config:  config,
		started: false,
	}
}

// Start starts the group service
func (s *Service) Start() error {
	s.startMutex.Lock()
	defer s.startMutex.Unlock()

	if s.started {
		return fmt.Errorf("service already started")
	}

	if err := s.manager.Start(); err != nil {
		return fmt.Errorf("failed to start group manager: %v", err)
	}

	s.started = true
	return nil
}

// Stop stops the group service
func (s *Service) Stop() error {
	s.startMutex.Lock()
	defer s.startMutex.Unlock()

	if !s.started {
		return nil
	}

	if err := s.manager.Stop(); err != nil {
		return fmt.Errorf("failed to stop group manager: %v", err)
	}

	s.started = false
	return nil
}

// IsStarted returns true if the service is started
func (s *Service) IsStarted() bool {
	s.startMutex.Lock()
	defer s.startMutex.Unlock()
	return s.started
}

// GetManager returns the underlying group manager
func (s *Service) GetManager() GroupManagerInterface {
	return s.manager
}

// High-level group operations

// CreateGroup creates a new group with validation
func (s *Service) CreateGroup(leader entity.Entity, options *GroupOptions) (int32, error) {
	if leader == nil {
		return 0, fmt.Errorf("leader cannot be nil")
	}

	// Validate leader can create group
	if s.config.ValidationEnabled {
		if err := s.validateGroupCreation(leader, options); err != nil {
			return 0, fmt.Errorf("group creation validation failed: %v", err)
		}
	}

	// Use default options if none provided
	if options == nil {
		defaultOpts := DefaultGroupOptions()
		options = &defaultOpts
	}

	return s.manager.NewGroup(leader, options, 0)
}

// InviteToGroup invites a member to join a group
func (s *Service) InviteToGroup(leader entity.Entity, member entity.Entity) error {
	if leader == nil || member == nil {
		return fmt.Errorf("leader and member cannot be nil")
	}

	// Validate the invitation
	if s.config.ValidationEnabled {
		if err := s.validateGroupInvitation(leader, member); err != nil {
			return fmt.Errorf("invitation validation failed: %v", err)
		}
	}

	// Send the invitation
	result := s.manager.Invite(leader, member)

	switch result {
	case GROUP_INVITE_SUCCESS:
		return nil
	case GROUP_INVITE_ALREADY_IN_GROUP:
		return fmt.Errorf("member is already in a group")
	case GROUP_INVITE_ALREADY_HAS_INVITE:
		return fmt.Errorf("member already has a pending invite")
	case GROUP_INVITE_GROUP_FULL:
		return fmt.Errorf("group is full")
	case GROUP_INVITE_DECLINED:
		return fmt.Errorf("invitation was declined")
	case GROUP_INVITE_TARGET_NOT_FOUND:
		return fmt.Errorf("target not found")
	case GROUP_INVITE_SELF_INVITE:
		return fmt.Errorf("cannot invite yourself")
	case GROUP_INVITE_PERMISSION_DENIED:
		return fmt.Errorf("permission denied")
	case GROUP_INVITE_TARGET_BUSY:
		return fmt.Errorf("target is busy")
	default:
		return fmt.Errorf("unknown invitation error: %d", result)
	}
}

// AcceptGroupInvite accepts a group invitation
func (s *Service) AcceptGroupInvite(member entity.Entity) error {
	if member == nil {
		return fmt.Errorf("member cannot be nil")
	}

	result := s.manager.AcceptInvite(member, nil, true)

	switch result {
	case GROUP_INVITE_SUCCESS:
		return nil
	case GROUP_INVITE_TARGET_NOT_FOUND:
		return fmt.Errorf("no pending invitation found")
	case GROUP_INVITE_GROUP_FULL:
		return fmt.Errorf("group is full")
	case GROUP_INVITE_PERMISSION_DENIED:
		return fmt.Errorf("permission denied")
	default:
		return fmt.Errorf("unknown acceptance error: %d", result)
	}
}

// DeclineGroupInvite declines a group invitation
func (s *Service) DeclineGroupInvite(member entity.Entity) {
	if member != nil {
		s.manager.DeclineInvite(member)
	}
}

// LeaveGroup removes a member from their current group
func (s *Service) LeaveGroup(member entity.Entity) error {
	if member == nil {
		return fmt.Errorf("member cannot be nil")
	}

	// TODO: Get member's current group ID
	// groupID := member.GetGroupID()
	groupID := int32(0) // Placeholder

	if groupID == 0 {
		return fmt.Errorf("member is not in a group")
	}

	return s.manager.RemoveGroupMember(groupID, member)
}

// DisbandGroup disbands a group
func (s *Service) DisbandGroup(groupID int32) error {
	return s.manager.RemoveGroup(groupID)
}

// TransferLeadership transfers group leadership
func (s *Service) TransferLeadership(groupID int32, newLeader entity.Entity) error {
	if newLeader == nil {
		return fmt.Errorf("new leader cannot be nil")
	}

	if !s.manager.IsGroupIDValid(groupID) {
		return fmt.Errorf("invalid group ID")
	}

	if !s.manager.MakeLeader(groupID, newLeader) {
		return fmt.Errorf("failed to transfer leadership")
	}

	return nil
}

// Group information methods

// GetGroupInfo returns detailed information about a group
func (s *Service) GetGroupInfo(groupID int32) (*GroupInfo, error) {
	group := s.manager.GetGroup(groupID)
	if group == nil {
		return nil, fmt.Errorf("group not found")
	}

	members := group.GetMembers()
	options := group.GetGroupOptions()
	raidGroups := group.GetRaidGroups()

	info := &GroupInfo{
		GroupID:      group.GetID(),
		Size:         int(group.GetSize()),
		Members:      members,
		Options:      options,
		RaidGroups:   raidGroups,
		IsRaid:       group.IsGroupRaid(),
		LeaderName:   group.GetLeaderName(),
		CreatedTime:  group.GetCreatedTime(),
		LastActivity: group.GetLastActivity(),
		IsDisbanded:  group.IsDisbanded(),
	}

	return info, nil
}

// GetMemberGroups returns all groups that contain any of the specified members
func (s *Service) GetMemberGroups(members []entity.Entity) []*GroupInfo {
	var groups []*GroupInfo

	allGroups := s.manager.GetAllGroups()
	for _, group := range allGroups {
		if group.IsDisbanded() {
			continue
		}

		groupMembers := group.GetMembers()
		for _, member := range members {
			for _, gmi := range groupMembers {
				if gmi.Member == member {
					if info, err := s.GetGroupInfo(group.GetID()); err == nil {
						groups = append(groups, info)
					}
					break
				}
			}
		}
	}

	return groups
}

// GetGroupsByZone returns all groups with members in the specified zone
func (s *Service) GetGroupsByZone(zoneID int32) []*GroupInfo {
	var groups []*GroupInfo

	allGroups := s.manager.GetAllGroups()
	for _, group := range allGroups {
		if group.IsDisbanded() {
			continue
		}

		members := group.GetMembers()
		hasZoneMember := false

		for _, member := range members {
			if member.ZoneID == zoneID {
				hasZoneMember = true
				break
			}
		}

		if hasZoneMember {
			if info, err := s.GetGroupInfo(group.GetID()); err == nil {
				groups = append(groups, info)
			}
		}
	}

	return groups
}

// Raid operations

// FormRaid forms a raid from multiple groups
func (s *Service) FormRaid(leaderGroupID int32, targetGroupIDs []int32) error {
	if !s.config.ManagerConfig.EnableRaids {
		return fmt.Errorf("raids are disabled")
	}

	leaderGroup := s.manager.GetGroup(leaderGroupID)
	if leaderGroup == nil {
		return fmt.Errorf("leader group not found")
	}

	// Validate all target groups exist
	for _, groupID := range targetGroupIDs {
		if !s.manager.IsGroupIDValid(groupID) {
			return fmt.Errorf("invalid target group ID: %d", groupID)
		}
	}

	// Add all groups to the raid
	allRaidGroups := append([]int32{leaderGroupID}, targetGroupIDs...)

	for _, groupID := range allRaidGroups {
		s.manager.ReplaceRaidGroups(groupID, allRaidGroups)
	}

	return nil
}

// DisbandRaid disbands a raid
func (s *Service) DisbandRaid(groupID int32) error {
	group := s.manager.GetGroup(groupID)
	if group == nil {
		return fmt.Errorf("group not found")
	}

	raidGroups := group.GetRaidGroups()
	if len(raidGroups) == 0 {
		return fmt.Errorf("group is not in a raid")
	}

	// Clear raid associations for all groups
	for _, raidGroupID := range raidGroups {
		s.manager.ClearGroupRaid(raidGroupID)
	}

	return nil
}

// Service configuration

// UpdateConfig updates the service configuration
func (s *Service) UpdateConfig(config ServiceConfig) error {
	s.config = config
	return nil
}

// GetConfig returns the current service configuration
func (s *Service) GetConfig() ServiceConfig {
	return s.config
}

// Integration methods

// SetDatabase sets the database interface
func (s *Service) SetDatabase(db GroupDatabase) {
	s.manager.SetDatabase(db)
}

// SetPacketHandler sets the packet handler interface
func (s *Service) SetPacketHandler(handler GroupPacketHandler) {
	s.manager.SetPacketHandler(handler)
}

// SetValidator sets the validator interface
func (s *Service) SetValidator(validator GroupValidator) {
	s.manager.SetValidator(validator)
}

// SetNotifier sets the notifier interface
func (s *Service) SetNotifier(notifier GroupNotifier) {
	s.manager.SetNotifier(notifier)
}

// AddEventHandler adds an event handler
func (s *Service) AddEventHandler(handler GroupEventHandler) {
	s.manager.AddEventHandler(handler)
}

// Statistics

// GetServiceStats returns service statistics
func (s *Service) GetServiceStats() *ServiceStats {
	managerStats := s.manager.GetStats()

	return &ServiceStats{
		ManagerStats:     managerStats,
		ServiceStartTime: time.Now(), // TODO: Track actual start time
		IsStarted:        s.started,
		Config:           s.config,
	}
}

// Validation methods

// validateGroupCreation validates group creation parameters
func (s *Service) validateGroupCreation(leader entity.Entity, options *GroupOptions) error {
	// Check if leader is already in a group
	// TODO: Check leader's group status
	// if leader.GetGroupMemberInfo() != nil {
	//     return fmt.Errorf("leader is already in a group")
	// }

	// Validate options
	if options != nil && !options.IsValid() {
		return fmt.Errorf("invalid group options")
	}

	return nil
}

// validateGroupInvitation validates group invitation parameters
func (s *Service) validateGroupInvitation(leader entity.Entity, member entity.Entity) error {
	// Check distance if enabled
	if s.config.MaxInviteDistance > 0 {
		distance := leader.GetDistance(&member.Spawn)
		if distance > s.config.MaxInviteDistance {
			return fmt.Errorf("member is too far away (%.1f > %.1f)", distance, s.config.MaxInviteDistance)
		}
	}

	// Check level range if enabled
	if s.config.GroupLevelRange > 0 {
		leaderLevel := leader.GetLevel()
		memberLevel := member.GetLevel()
		levelDiff := leaderLevel - memberLevel
		if levelDiff < 0 {
			levelDiff = -levelDiff
		}

		if levelDiff > s.config.GroupLevelRange {
			return fmt.Errorf("level difference too large (%d > %d)", levelDiff, s.config.GroupLevelRange)
		}
	}

	// Check if member type is allowed
	if member.IsBot() && !s.config.AllowBotMembers {
		return fmt.Errorf("bot members are not allowed")
	}

	if member.IsNPC() && !s.config.AllowNPCMembers {
		return fmt.Errorf("NPC members are not allowed")
	}

	// Check zone restrictions
	if !s.config.AllowCrossZoneGroups {
		if leader.GetZone() != member.GetZone() {
			return fmt.Errorf("cross-zone groups are not allowed")
		}
	}

	return nil
}

// GroupInfo holds detailed information about a group
type GroupInfo struct {
	GroupID      int32              `json:"group_id"`
	Size         int                `json:"size"`
	Members      []*GroupMemberInfo `json:"members"`
	Options      GroupOptions       `json:"options"`
	RaidGroups   []int32            `json:"raid_groups"`
	IsRaid       bool               `json:"is_raid"`
	LeaderName   string             `json:"leader_name"`
	CreatedTime  time.Time          `json:"created_time"`
	LastActivity time.Time          `json:"last_activity"`
	IsDisbanded  bool               `json:"is_disbanded"`
}

// ServiceStats holds statistics about the service
type ServiceStats struct {
	ManagerStats     GroupManagerStats `json:"manager_stats"`
	ServiceStartTime time.Time         `json:"service_start_time"`
	IsStarted        bool              `json:"is_started"`
	Config           ServiceConfig     `json:"config"`
}
