package factions

import (
	"fmt"
	"sync"
)

// MasterFactionList manages all factions in the game
type MasterFactionList struct {
	globalFactionList map[int32]*Faction  // Factions by ID
	factionNameList   map[string]*Faction // Factions by name
	hostileFactions   map[int32][]int32   // Hostile faction relationships
	friendlyFactions  map[int32][]int32   // Friendly faction relationships
	mutex             sync.RWMutex        // Thread safety
}

// NewMasterFactionList creates a new master faction list
func NewMasterFactionList() *MasterFactionList {
	return &MasterFactionList{
		globalFactionList: make(map[int32]*Faction),
		factionNameList:   make(map[string]*Faction),
		hostileFactions:   make(map[int32][]int32),
		friendlyFactions:  make(map[int32][]int32),
	}
}

// Clear removes all factions and relationships
func (mfl *MasterFactionList) Clear() {
	mfl.mutex.Lock()
	defer mfl.mutex.Unlock()

	// Clear all maps - Go's garbage collector will handle cleanup
	mfl.globalFactionList = make(map[int32]*Faction)
	mfl.factionNameList = make(map[string]*Faction)
	mfl.hostileFactions = make(map[int32][]int32)
	mfl.friendlyFactions = make(map[int32][]int32)
}

// GetDefaultFactionValue returns the default value for a faction
func (mfl *MasterFactionList) GetDefaultFactionValue(factionID int32) int32 {
	mfl.mutex.RLock()
	defer mfl.mutex.RUnlock()

	if faction, exists := mfl.globalFactionList[factionID]; exists && faction != nil {
		return faction.DefaultValue
	}

	return 0
}

// GetFaction returns a faction by name
func (mfl *MasterFactionList) GetFactionByName(name string) *Faction {
	mfl.mutex.RLock()
	defer mfl.mutex.RUnlock()

	return mfl.factionNameList[name]
}

// GetFaction returns a faction by ID
func (mfl *MasterFactionList) GetFaction(id int32) *Faction {
	mfl.mutex.RLock()
	defer mfl.mutex.RUnlock()

	if faction, exists := mfl.globalFactionList[id]; exists {
		return faction
	}

	return nil
}

// AddFaction adds a faction to the master list
func (mfl *MasterFactionList) AddFaction(faction *Faction) error {
	if faction == nil {
		return fmt.Errorf("faction cannot be nil")
	}

	if !faction.IsValid() {
		return fmt.Errorf("faction is not valid")
	}

	mfl.mutex.Lock()
	defer mfl.mutex.Unlock()

	mfl.globalFactionList[faction.ID] = faction
	mfl.factionNameList[faction.Name] = faction

	return nil
}

// GetIncreaseAmount returns the default increase amount for a faction
func (mfl *MasterFactionList) GetIncreaseAmount(factionID int32) int32 {
	mfl.mutex.RLock()
	defer mfl.mutex.RUnlock()

	if faction, exists := mfl.globalFactionList[factionID]; exists && faction != nil {
		return int32(faction.PositiveChange)
	}

	return 0
}

// GetDecreaseAmount returns the default decrease amount for a faction
func (mfl *MasterFactionList) GetDecreaseAmount(factionID int32) int32 {
	mfl.mutex.RLock()
	defer mfl.mutex.RUnlock()

	if faction, exists := mfl.globalFactionList[factionID]; exists && faction != nil {
		return int32(faction.NegativeChange)
	}

	return 0
}

// GetFactionCount returns the total number of factions
func (mfl *MasterFactionList) GetFactionCount() int32 {
	mfl.mutex.RLock()
	defer mfl.mutex.RUnlock()

	return int32(len(mfl.globalFactionList))
}

// AddHostileFaction adds a hostile relationship between factions
func (mfl *MasterFactionList) AddHostileFaction(factionID, hostileFactionID int32) {
	mfl.mutex.Lock()
	defer mfl.mutex.Unlock()

	mfl.hostileFactions[factionID] = append(mfl.hostileFactions[factionID], hostileFactionID)
}

// AddFriendlyFaction adds a friendly relationship between factions
func (mfl *MasterFactionList) AddFriendlyFaction(factionID, friendlyFactionID int32) {
	mfl.mutex.Lock()
	defer mfl.mutex.Unlock()

	mfl.friendlyFactions[factionID] = append(mfl.friendlyFactions[factionID], friendlyFactionID)
}

// GetFriendlyFactions returns all friendly factions for a given faction
func (mfl *MasterFactionList) GetFriendlyFactions(factionID int32) []int32 {
	mfl.mutex.RLock()
	defer mfl.mutex.RUnlock()

	if factions, exists := mfl.friendlyFactions[factionID]; exists {
		// Return a copy to prevent external modification
		result := make([]int32, len(factions))
		copy(result, factions)
		return result
	}

	return nil
}

// GetHostileFactions returns all hostile factions for a given faction
func (mfl *MasterFactionList) GetHostileFactions(factionID int32) []int32 {
	mfl.mutex.RLock()
	defer mfl.mutex.RUnlock()

	if factions, exists := mfl.hostileFactions[factionID]; exists {
		// Return a copy to prevent external modification
		result := make([]int32, len(factions))
		copy(result, factions)
		return result
	}

	return nil
}

// GetFactionNameByID returns the faction name for a given ID
func (mfl *MasterFactionList) GetFactionNameByID(factionID int32) string {
	if factionID > 0 {
		mfl.mutex.RLock()
		defer mfl.mutex.RUnlock()

		if faction, exists := mfl.globalFactionList[factionID]; exists {
			return faction.Name
		}
	}

	return ""
}

// HasFaction checks if a faction exists by ID
func (mfl *MasterFactionList) HasFaction(factionID int32) bool {
	mfl.mutex.RLock()
	defer mfl.mutex.RUnlock()

	_, exists := mfl.globalFactionList[factionID]
	return exists
}

// HasFactionByName checks if a faction exists by name
func (mfl *MasterFactionList) HasFactionByName(name string) bool {
	mfl.mutex.RLock()
	defer mfl.mutex.RUnlock()

	_, exists := mfl.factionNameList[name]
	return exists
}

// GetAllFactions returns a copy of all factions
func (mfl *MasterFactionList) GetAllFactions() map[int32]*Faction {
	mfl.mutex.RLock()
	defer mfl.mutex.RUnlock()

	result := make(map[int32]*Faction)
	for id, faction := range mfl.globalFactionList {
		result[id] = faction
	}

	return result
}

// GetFactionIDs returns all faction IDs
func (mfl *MasterFactionList) GetFactionIDs() []int32 {
	mfl.mutex.RLock()
	defer mfl.mutex.RUnlock()

	ids := make([]int32, 0, len(mfl.globalFactionList))
	for id := range mfl.globalFactionList {
		ids = append(ids, id)
	}

	return ids
}

// GetFactionsByType returns all factions of a specific type
func (mfl *MasterFactionList) GetFactionsByType(factionType string) []*Faction {
	mfl.mutex.RLock()
	defer mfl.mutex.RUnlock()

	var result []*Faction

	for _, faction := range mfl.globalFactionList {
		if faction.Type == factionType {
			result = append(result, faction)
		}
	}

	return result
}

// RemoveFaction removes a faction by ID
func (mfl *MasterFactionList) RemoveFaction(factionID int32) bool {
	mfl.mutex.Lock()
	defer mfl.mutex.Unlock()

	faction, exists := mfl.globalFactionList[factionID]
	if !exists {
		return false
	}

	// Remove from both maps
	delete(mfl.globalFactionList, factionID)
	delete(mfl.factionNameList, faction.Name)

	// Remove from relationship maps
	delete(mfl.hostileFactions, factionID)
	delete(mfl.friendlyFactions, factionID)

	// Remove references to this faction in other faction's relationships
	for id, hostiles := range mfl.hostileFactions {
		newHostiles := make([]int32, 0, len(hostiles))
		for _, hostileID := range hostiles {
			if hostileID != factionID {
				newHostiles = append(newHostiles, hostileID)
			}
		}
		mfl.hostileFactions[id] = newHostiles
	}

	for id, friendlies := range mfl.friendlyFactions {
		newFriendlies := make([]int32, 0, len(friendlies))
		for _, friendlyID := range friendlies {
			if friendlyID != factionID {
				newFriendlies = append(newFriendlies, friendlyID)
			}
		}
		mfl.friendlyFactions[id] = newFriendlies
	}

	return true
}

// UpdateFaction updates an existing faction
func (mfl *MasterFactionList) UpdateFaction(faction *Faction) error {
	if faction == nil {
		return fmt.Errorf("faction cannot be nil")
	}

	if !faction.IsValid() {
		return fmt.Errorf("faction is not valid")
	}

	mfl.mutex.Lock()
	defer mfl.mutex.Unlock()

	// Check if faction exists
	oldFaction, exists := mfl.globalFactionList[faction.ID]
	if !exists {
		return fmt.Errorf("faction with ID %d does not exist", faction.ID)
	}

	// If name changed, update name map
	if oldFaction.Name != faction.Name {
		delete(mfl.factionNameList, oldFaction.Name)
		mfl.factionNameList[faction.Name] = faction
	}

	// Update faction
	mfl.globalFactionList[faction.ID] = faction

	return nil
}

// ValidateFactions checks all factions for consistency
func (mfl *MasterFactionList) ValidateFactions() []string {
	mfl.mutex.RLock()
	defer mfl.mutex.RUnlock()

	var issues []string

	// Check for nil factions
	for id, faction := range mfl.globalFactionList {
		if faction == nil {
			issues = append(issues, fmt.Sprintf("Faction ID %d is nil", id))
			continue
		}

		if !faction.IsValid() {
			issues = append(issues, fmt.Sprintf("Faction ID %d is invalid", id))
		}

		if faction.ID != id {
			issues = append(issues, fmt.Sprintf("Faction ID mismatch: map key %d != faction ID %d", id, faction.ID))
		}
	}

	// Check name map consistency
	for name, faction := range mfl.factionNameList {
		if faction == nil {
			issues = append(issues, fmt.Sprintf("Faction name '%s' maps to nil", name))
			continue
		}

		if faction.Name != name {
			issues = append(issues, fmt.Sprintf("Faction name mismatch: map key '%s' != faction name '%s'", name, faction.Name))
		}

		// Check if this faction exists in the ID map
		if _, exists := mfl.globalFactionList[faction.ID]; !exists {
			issues = append(issues, fmt.Sprintf("Faction '%s' (ID %d) exists in name map but not in ID map", name, faction.ID))
		}
	}

	// Check relationship consistency
	for factionID, hostiles := range mfl.hostileFactions {
		if _, exists := mfl.globalFactionList[factionID]; !exists {
			issues = append(issues, fmt.Sprintf("Hostile relationship defined for non-existent faction %d", factionID))
		}

		for _, hostileID := range hostiles {
			if _, exists := mfl.globalFactionList[hostileID]; !exists {
				issues = append(issues, fmt.Sprintf("Faction %d has hostile relationship with non-existent faction %d", factionID, hostileID))
			}
		}
	}

	for factionID, friendlies := range mfl.friendlyFactions {
		if _, exists := mfl.globalFactionList[factionID]; !exists {
			issues = append(issues, fmt.Sprintf("Friendly relationship defined for non-existent faction %d", factionID))
		}

		for _, friendlyID := range friendlies {
			if _, exists := mfl.globalFactionList[friendlyID]; !exists {
				issues = append(issues, fmt.Sprintf("Faction %d has friendly relationship with non-existent faction %d", factionID, friendlyID))
			}
		}
	}

	return issues
}

// IsValid returns true if all factions are valid
func (mfl *MasterFactionList) IsValid() bool {
	issues := mfl.ValidateFactions()
	return len(issues) == 0
}
