package guilds

import (
	"fmt"
	"strings"
	"time"
)

// NewGuild creates a new guild instance
func NewGuild() *Guild {
	guild := &Guild{
		members:             make(map[int32]*GuildMember),
		guildEvents:         make([]GuildEvent, 0),
		permissions:         make(map[int8]map[int8]int8),
		eventFilters:        make(map[int8]map[int8]int8),
		recruitingFlags:     make(map[int8]int8),
		recruitingDescTags:  make(map[int8]int8),
		ranks:               make(map[int8]string),
		level:               1,
		expCurrent:          111,
		expToNextLevel:      2521,
		recruitingMinLevel:  1,
		recruitingPlayStyle: RecruitingPlayStyleNone,
		nextEventID:         1,
		lastModified:        time.Now(),
	}

	// Initialize default recruiting flags
	guild.recruitingFlags[RecruitingFlagTraining] = 0
	guild.recruitingFlags[RecruitingFlagFighters] = 0
	guild.recruitingFlags[RecruitingFlagPriests] = 0
	guild.recruitingFlags[RecruitingFlagScouts] = 0
	guild.recruitingFlags[RecruitingFlagMages] = 0
	guild.recruitingFlags[RecruitingFlagTradeskillers] = 0

	// Initialize default description tags
	guild.recruitingDescTags[0] = RecruitingDescTagNone
	guild.recruitingDescTags[1] = RecruitingDescTagNone
	guild.recruitingDescTags[2] = RecruitingDescTagNone
	guild.recruitingDescTags[3] = RecruitingDescTagNone

	// Initialize default bank names
	guild.banks[0].Name = "Bank 1"
	guild.banks[1].Name = "Bank 2"
	guild.banks[2].Name = "Bank 3"
	guild.banks[3].Name = "Bank 4"

	// Initialize default rank names
	for rank, name := range DefaultRankNames {
		guild.ranks[rank] = name
	}

	return guild
}

// SetID sets the guild ID
func (g *Guild) SetID(id int32) {
	g.mu.Lock()
	defer g.mu.Unlock()
	g.id = id
}

// SetName sets the guild name
func (g *Guild) SetName(name string, sendPacket bool) {
	g.mu.Lock()
	defer g.mu.Unlock()

	if len(name) > MaxGuildNameLength {
		name = name[:MaxGuildNameLength]
	}
	g.name = name
	g.lastModified = time.Now()

	if sendPacket {
		g.saveNeeded = true
	}
}

// SetLevel sets the guild level
func (g *Guild) SetLevel(level int8, sendPacket bool) {
	g.mu.Lock()
	defer g.mu.Unlock()

	if level > MaxGuildLevel {
		level = MaxGuildLevel
	}
	if level < 1 {
		level = 1
	}

	g.level = level
	g.lastModified = time.Now()

	if sendPacket {
		g.saveNeeded = true
	}
}

// SetFormedDate sets the guild formation date
func (g *Guild) SetFormedDate(formedDate time.Time) {
	g.mu.Lock()
	defer g.mu.Unlock()
	g.formedDate = formedDate
}

// SetMOTD sets the guild message of the day
func (g *Guild) SetMOTD(motd string, sendPacket bool) {
	g.mu.Lock()
	defer g.mu.Unlock()

	if len(motd) > MaxMOTDLength {
		motd = motd[:MaxMOTDLength]
	}
	g.motd = motd
	g.lastModified = time.Now()

	if sendPacket {
		g.saveNeeded = true
	}
}

// GetID returns the guild ID
func (g *Guild) GetID() int32 {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.id
}

// GetName returns the guild name
func (g *Guild) GetName() string {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.name
}

// GetLevel returns the guild level
func (g *Guild) GetLevel() int8 {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.level
}

// GetFormedDate returns the guild formation date
func (g *Guild) GetFormedDate() time.Time {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.formedDate
}

// GetMOTD returns the guild message of the day
func (g *Guild) GetMOTD() string {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.motd
}

// SetEXPCurrent sets the current guild experience
func (g *Guild) SetEXPCurrent(exp int64, sendPacket bool) {
	g.mu.Lock()
	defer g.mu.Unlock()

	g.expCurrent = exp
	g.lastModified = time.Now()

	if sendPacket {
		g.saveNeeded = true
	}
}

// AddEXPCurrent adds experience to the guild
func (g *Guild) AddEXPCurrent(exp int64, sendPacket bool) {
	g.mu.Lock()
	defer g.mu.Unlock()

	g.expCurrent += exp
	g.lastModified = time.Now()

	if sendPacket {
		g.saveNeeded = true
	}
}

// GetEXPCurrent returns the current guild experience
func (g *Guild) GetEXPCurrent() int64 {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.expCurrent
}

// SetEXPToNextLevel sets the experience needed for next level
func (g *Guild) SetEXPToNextLevel(exp int64, sendPacket bool) {
	g.mu.Lock()
	defer g.mu.Unlock()

	g.expToNextLevel = exp
	g.lastModified = time.Now()

	if sendPacket {
		g.saveNeeded = true
	}
}

// GetEXPToNextLevel returns the experience needed for next level
func (g *Guild) GetEXPToNextLevel() int64 {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.expToNextLevel
}

// SetRecruitingShortDesc sets the short recruiting description
func (g *Guild) SetRecruitingShortDesc(desc string, sendPacket bool) {
	g.mu.Lock()
	defer g.mu.Unlock()

	g.recruitingShortDesc = desc
	g.lastModified = time.Now()

	if sendPacket {
		g.recruitingSaveNeeded = true
	}
}

// GetRecruitingShortDesc returns the short recruiting description
func (g *Guild) GetRecruitingShortDesc() string {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.recruitingShortDesc
}

// SetRecruitingFullDesc sets the full recruiting description
func (g *Guild) SetRecruitingFullDesc(desc string, sendPacket bool) {
	g.mu.Lock()
	defer g.mu.Unlock()

	if len(desc) > MaxRecruitingDescLength {
		desc = desc[:MaxRecruitingDescLength]
	}
	g.recruitingFullDesc = desc
	g.lastModified = time.Now()

	if sendPacket {
		g.recruitingSaveNeeded = true
	}
}

// GetRecruitingFullDesc returns the full recruiting description
func (g *Guild) GetRecruitingFullDesc() string {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.recruitingFullDesc
}

// SetRecruitingMinLevel sets the minimum level for recruiting
func (g *Guild) SetRecruitingMinLevel(level int8, sendPacket bool) {
	g.mu.Lock()
	defer g.mu.Unlock()

	g.recruitingMinLevel = level
	g.lastModified = time.Now()

	if sendPacket {
		g.recruitingSaveNeeded = true
	}
}

// GetRecruitingMinLevel returns the minimum level for recruiting
func (g *Guild) GetRecruitingMinLevel() int8 {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.recruitingMinLevel
}

// SetRecruitingPlayStyle sets the recruiting play style
func (g *Guild) SetRecruitingPlayStyle(playStyle int8, sendPacket bool) {
	g.mu.Lock()
	defer g.mu.Unlock()

	g.recruitingPlayStyle = playStyle
	g.lastModified = time.Now()

	if sendPacket {
		g.recruitingSaveNeeded = true
	}
}

// GetRecruitingPlayStyle returns the recruiting play style
func (g *Guild) GetRecruitingPlayStyle() int8 {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.recruitingPlayStyle
}

// SetRecruitingDescTag sets a recruiting description tag
func (g *Guild) SetRecruitingDescTag(index, tag int8, sendPacket bool) bool {
	g.mu.Lock()
	defer g.mu.Unlock()

	if index < 0 || index > 3 {
		return false
	}

	g.recruitingDescTags[index] = tag
	g.lastModified = time.Now()

	if sendPacket {
		g.recruitingSaveNeeded = true
	}

	return true
}

// GetRecruitingDescTag returns a recruiting description tag
func (g *Guild) GetRecruitingDescTag(index int8) int8 {
	g.mu.RLock()
	defer g.mu.RUnlock()

	if tag, exists := g.recruitingDescTags[index]; exists {
		return tag
	}
	return RecruitingDescTagNone
}

// SetPermission sets a guild permission for a rank
func (g *Guild) SetPermission(rank, permission, value int8, sendPacket, saveNeeded bool) bool {
	g.mu.Lock()
	defer g.mu.Unlock()

	if rank < RankLeader || rank > RankRecruit {
		return false
	}

	if g.permissions[rank] == nil {
		g.permissions[rank] = make(map[int8]int8)
	}

	g.permissions[rank][permission] = value
	g.lastModified = time.Now()

	if saveNeeded {
		g.ranksSaveNeeded = true
	}

	return true
}

// GetPermission returns a guild permission for a rank
func (g *Guild) GetPermission(rank, permission int8) int8 {
	g.mu.RLock()
	defer g.mu.RUnlock()

	if rankPerms, exists := g.permissions[rank]; exists {
		if value, exists := rankPerms[permission]; exists {
			return value
		}
	}

	// Return default permission based on rank
	return g.getDefaultPermission(rank, permission)
}

// SetEventFilter sets an event filter
func (g *Guild) SetEventFilter(eventID, category, value int8, sendPacket, saveNeeded bool) bool {
	g.mu.Lock()
	defer g.mu.Unlock()

	if g.eventFilters[eventID] == nil {
		g.eventFilters[eventID] = make(map[int8]int8)
	}

	g.eventFilters[eventID][category] = value
	g.lastModified = time.Now()

	if saveNeeded {
		g.eventFiltersSaveNeeded = true
	}

	return true
}

// GetEventFilter returns an event filter
func (g *Guild) GetEventFilter(eventID, category int8) int8 {
	g.mu.RLock()
	defer g.mu.RUnlock()

	if eventFilters, exists := g.eventFilters[eventID]; exists {
		if value, exists := eventFilters[category]; exists {
			return value
		}
	}

	return 1 // Default to enabled
}

// GetNumUniqueAccounts returns the number of unique accounts in the guild
func (g *Guild) GetNumUniqueAccounts() int32 {
	g.mu.RLock()
	defer g.mu.RUnlock()

	accounts := make(map[int32]bool)
	for _, member := range g.members {
		accounts[member.AccountID] = true
	}

	return int32(len(accounts))
}

// GetNumRecruiters returns the number of recruiters in the guild
func (g *Guild) GetNumRecruiters() int32 {
	g.mu.RLock()
	defer g.mu.RUnlock()

	count := int32(0)
	for _, member := range g.members {
		if member.MemberFlags&MemberFlagRecruitingForGuild != 0 {
			count++
		}
	}

	return count
}

// GetNextRecruiterID returns the next available recruiter ID
func (g *Guild) GetNextRecruiterID() int32 {
	g.mu.RLock()
	defer g.mu.RUnlock()

	maxID := int32(0)
	for _, member := range g.members {
		if member.RecruiterID > maxID {
			maxID = member.RecruiterID
		}
	}

	return maxID + 1
}

// GetNextEventID returns the next available event ID
func (g *Guild) GetNextEventID() int64 {
	g.mu.Lock()
	defer g.mu.Unlock()

	eventID := g.nextEventID
	g.nextEventID++
	return eventID
}

// GetGuildMember returns a guild member by character ID
func (g *Guild) GetGuildMember(characterID int32) *GuildMember {
	g.mu.RLock()
	defer g.mu.RUnlock()

	return g.members[characterID]
}

// GetGuildMemberByName returns a guild member by name
func (g *Guild) GetGuildMemberByName(playerName string) *GuildMember {
	g.mu.RLock()
	defer g.mu.RUnlock()

	for _, member := range g.members {
		if strings.EqualFold(member.Name, playerName) {
			return member
		}
	}

	return nil
}

// GetGuildRecruiters returns all guild recruiters
func (g *Guild) GetGuildRecruiters() []*GuildMember {
	g.mu.RLock()
	defer g.mu.RUnlock()

	var recruiters []*GuildMember
	for _, member := range g.members {
		if member.MemberFlags&MemberFlagRecruitingForGuild != 0 {
			recruiters = append(recruiters, member)
		}
	}

	return recruiters
}

// GetGuildEvent returns a guild event by ID
func (g *Guild) GetGuildEvent(eventID int64) *GuildEvent {
	g.mu.RLock()
	defer g.mu.RUnlock()

	for i := range g.guildEvents {
		if g.guildEvents[i].EventID == eventID {
			return &g.guildEvents[i]
		}
	}

	return nil
}

// SetRankName sets a custom rank name
func (g *Guild) SetRankName(rank int8, name string, sendPacket bool) bool {
	g.mu.Lock()
	defer g.mu.Unlock()

	if rank < RankLeader || rank > RankRecruit {
		return false
	}

	g.ranks[rank] = name
	g.lastModified = time.Now()

	if sendPacket {
		g.ranksSaveNeeded = true
	}

	return true
}

// GetRankName returns the name for a rank
func (g *Guild) GetRankName(rank int8) string {
	g.mu.RLock()
	defer g.mu.RUnlock()

	if name, exists := g.ranks[rank]; exists {
		return name
	}

	// Return default rank name
	if defaultName, exists := DefaultRankNames[rank]; exists {
		return defaultName
	}

	return "Unknown"
}

// SetRecruitingFlag sets a recruiting flag
func (g *Guild) SetRecruitingFlag(flag, value int8, sendPacket bool) bool {
	g.mu.Lock()
	defer g.mu.Unlock()

	if flag < RecruitingFlagTraining || flag > RecruitingFlagTradeskillers {
		return false
	}

	g.recruitingFlags[flag] = value
	g.lastModified = time.Now()

	if sendPacket {
		g.recruitingSaveNeeded = true
	}

	return true
}

// GetRecruitingFlag returns a recruiting flag
func (g *Guild) GetRecruitingFlag(flag int8) int8 {
	g.mu.RLock()
	defer g.mu.RUnlock()

	if value, exists := g.recruitingFlags[flag]; exists {
		return value
	}

	return 0
}

// AddNewGuildMember adds a new member to the guild
func (g *Guild) AddNewGuildMember(characterID int32, invitedBy string, joinDate time.Time, rank int8) bool {
	g.mu.Lock()
	defer g.mu.Unlock()

	// Check if member already exists
	if _, exists := g.members[characterID]; exists {
		return false
	}

	member := &GuildMember{
		CharacterID:  characterID,
		Rank:         rank,
		JoinDate:     joinDate,
		PointHistory: make([]PointHistory, 0),
	}

	g.members[characterID] = member
	g.memberSaveNeeded = true
	g.lastModified = time.Now()

	// Add guild event
	g.addNewGuildEventNoLock(EventMemberJoins, fmt.Sprintf("%s has joined the guild", member.Name), time.Now(), true)

	return true
}

// RemoveGuildMember removes a member from the guild
func (g *Guild) RemoveGuildMember(characterID int32, sendPacket bool) {
	g.mu.Lock()
	defer g.mu.Unlock()

	if member, exists := g.members[characterID]; exists {
		// Add guild event
		g.addNewGuildEventNoLock(EventMemberLeaves, fmt.Sprintf("%s has left the guild", member.Name), time.Now(), sendPacket)

		delete(g.members, characterID)
		g.memberSaveNeeded = true
		g.lastModified = time.Now()
	}
}

// PromoteGuildMember promotes a guild member
func (g *Guild) PromoteGuildMember(characterID int32, promoterName string, sendPacket bool) bool {
	g.mu.Lock()
	defer g.mu.Unlock()

	member, exists := g.members[characterID]
	if !exists || member.Rank <= RankLeader {
		return false
	}

	oldRank := member.Rank
	member.Rank--
	g.memberSaveNeeded = true
	g.lastModified = time.Now()

	// Add guild event
	g.addNewGuildEventNoLock(EventMemberPromoted,
		fmt.Sprintf("%s has been promoted from %s to %s by %s",
			member.Name, g.getRankNameNoLock(oldRank), g.getRankNameNoLock(member.Rank), promoterName),
		time.Now(), sendPacket)

	return true
}

// DemoteGuildMember demotes a guild member
func (g *Guild) DemoteGuildMember(characterID int32, demoterName string, sendPacket bool) bool {
	g.mu.Lock()
	defer g.mu.Unlock()

	member, exists := g.members[characterID]
	if !exists || member.Rank >= RankRecruit {
		return false
	}

	oldRank := member.Rank
	member.Rank++
	g.memberSaveNeeded = true
	g.lastModified = time.Now()

	// Add guild event
	g.addNewGuildEventNoLock(EventMemberDemoted,
		fmt.Sprintf("%s has been demoted from %s to %s by %s",
			member.Name, g.getRankNameNoLock(oldRank), g.getRankNameNoLock(member.Rank), demoterName),
		time.Now(), sendPacket)

	return true
}

// AddPointsToGuildMember adds points to a specific guild member
func (g *Guild) AddPointsToGuildMember(characterID int32, points float64, modifiedBy, comment string, sendPacket bool) bool {
	g.mu.Lock()
	defer g.mu.Unlock()

	member, exists := g.members[characterID]
	if !exists {
		return false
	}

	member.Points += points

	// Add to point history
	if len(member.PointHistory) >= MaxPointHistory {
		// Remove oldest entry
		member.PointHistory = member.PointHistory[1:]
	}

	member.PointHistory = append(member.PointHistory, PointHistory{
		Date:       time.Now(),
		ModifiedBy: modifiedBy,
		Comment:    comment,
		Points:     points,
		SaveNeeded: true,
	})

	g.pointsHistorySaveNeeded = true
	g.lastModified = time.Now()

	return true
}

// AddNewGuildEvent adds a new event to the guild
func (g *Guild) AddNewGuildEvent(eventType int32, description string, date time.Time, sendPacket bool) {
	g.mu.Lock()
	defer g.mu.Unlock()
	g.addNewGuildEventNoLock(eventType, description, date, sendPacket)
}

// addNewGuildEventNoLock is the internal implementation without locking
func (g *Guild) addNewGuildEventNoLock(eventType int32, description string, date time.Time, sendPacket bool) {
	event := GuildEvent{
		EventID:     g.nextEventID,
		Date:        date,
		Type:        eventType,
		Description: description,
		Locked:      0,
		SaveNeeded:  true,
	}

	g.nextEventID++

	// Add to front of events list (newest first)
	g.guildEvents = append([]GuildEvent{event}, g.guildEvents...)

	// Limit event history
	if len(g.guildEvents) > MaxEvents {
		g.guildEvents = g.guildEvents[:MaxEvents]
	}

	g.eventsSaveNeeded = true
	g.lastModified = time.Now()
}

// GetGuildInfo returns basic guild information
func (g *Guild) GetGuildInfo() GuildInfo {
	g.mu.RLock()
	defer g.mu.RUnlock()

	return GuildInfo{
		ID:                  g.id,
		Name:                g.name,
		Level:               g.level,
		FormedDate:          g.formedDate,
		MOTD:                g.motd,
		MemberCount:         len(g.members),
		RecruiterCount:      int(g.getNumRecruitersNoLock()),
		RecruitingShortDesc: g.recruitingShortDesc,
		RecruitingFullDesc:  g.recruitingFullDesc,
		RecruitingMinLevel:  g.recruitingMinLevel,
		RecruitingPlayStyle: g.recruitingPlayStyle,
		IsRecruiting:        g.getNumRecruitersNoLock() > 0,
	}
}

// GetAllMembers returns all guild members
func (g *Guild) GetAllMembers() []*GuildMember {
	g.mu.RLock()
	defer g.mu.RUnlock()

	members := make([]*GuildMember, 0, len(g.members))
	for _, member := range g.members {
		members = append(members, member)
	}

	return members
}

// Save flag methods
func (g *Guild) SetSaveNeeded(val bool) {
	g.mu.Lock()
	defer g.mu.Unlock()
	g.saveNeeded = val
}

func (g *Guild) GetSaveNeeded() bool {
	g.mu.RLock()
	defer g.mu.RUnlock()
	return g.saveNeeded
}

// Helper methods (internal, no lock versions)

func (g *Guild) getDefaultPermission(rank, permission int8) int8 {
	// Leaders have all permissions by default
	if rank == RankLeader {
		return 1
	}

	// Default permissions based on rank and permission type
	switch permission {
	case PermissionSeeGuildChat, PermissionSpeakInGuildChat:
		return 1 // All members can see and speak in guild chat
	case PermissionReceivePoints:
		return 1 // All members can receive points
	case PermissionSeeOfficerChat, PermissionSpeakInOfficerChat:
		if rank <= RankOfficer {
			return 1
		}
	case PermissionInvite:
		if rank <= RankSeniorMember {
			return 1
		}
	}

	return 0 // Default to no permission
}

func (g *Guild) getRankNameNoLock(rank int8) string {
	if name, exists := g.ranks[rank]; exists {
		return name
	}
	if defaultName, exists := DefaultRankNames[rank]; exists {
		return defaultName
	}
	return "Unknown"
}

func (g *Guild) getNumRecruitersNoLock() int32 {
	count := int32(0)
	for _, member := range g.members {
		if member.MemberFlags&MemberFlagRecruitingForGuild != 0 {
			count++
		}
	}
	return count
}
