package factions import ( "fmt" "sync" ) // Manager provides high-level management of the faction system type Manager struct { masterFactionList *MasterFactionList database Database logger Logger mutex sync.RWMutex // Statistics totalFactionChanges int64 factionIncreases int64 factionDecreases int64 factionLookups int64 playersWithFactions int64 changesByFaction map[int32]int64 // Faction ID -> total changes } // NewManager creates a new faction manager func NewManager(database Database, logger Logger) *Manager { return &Manager{ masterFactionList: NewMasterFactionList(), database: database, logger: logger, changesByFaction: make(map[int32]int64), } } // Initialize loads factions from database func (m *Manager) Initialize() error { if m.logger != nil { m.logger.LogInfo("Initializing faction manager...") } if m.database == nil { if m.logger != nil { m.logger.LogWarning("No database provided, starting with empty faction list") } return nil } // Load factions factions, err := m.database.LoadAllFactions() if err != nil { return fmt.Errorf("failed to load factions from database: %w", err) } for _, faction := range factions { if err := m.masterFactionList.AddFaction(faction); err != nil { if m.logger != nil { m.logger.LogError("Failed to add faction %d (%s): %v", faction.ID, faction.Name, err) } } } // Load faction relationships if err := m.loadFactionRelationships(); err != nil { if m.logger != nil { m.logger.LogWarning("Failed to load faction relationships: %v", err) } } if m.logger != nil { m.logger.LogInfo("Loaded %d factions from database", len(factions)) } return nil } // loadFactionRelationships loads hostile and friendly faction relationships func (m *Manager) loadFactionRelationships() error { if m.database == nil { return nil } // Load hostile relationships hostileRelations, err := m.database.LoadHostileFactionRelations() if err != nil { return fmt.Errorf("failed to load hostile faction relations: %w", err) } for _, relation := range hostileRelations { m.masterFactionList.AddHostileFaction(relation.FactionID, relation.HostileFactionID) } // Load friendly relationships friendlyRelations, err := m.database.LoadFriendlyFactionRelations() if err != nil { return fmt.Errorf("failed to load friendly faction relations: %w", err) } for _, relation := range friendlyRelations { m.masterFactionList.AddFriendlyFaction(relation.FactionID, relation.FriendlyFactionID) } if m.logger != nil { m.logger.LogInfo("Loaded %d hostile and %d friendly faction relationships", len(hostileRelations), len(friendlyRelations)) } return nil } // GetMasterFactionList returns the master faction list func (m *Manager) GetMasterFactionList() *MasterFactionList { return m.masterFactionList } // CreatePlayerFaction creates a new player faction system func (m *Manager) CreatePlayerFaction() *PlayerFaction { m.mutex.Lock() m.playersWithFactions++ m.mutex.Unlock() return NewPlayerFaction(m.masterFactionList) } // GetFaction returns a faction by ID func (m *Manager) GetFaction(factionID int32) *Faction { m.mutex.Lock() m.factionLookups++ m.mutex.Unlock() return m.masterFactionList.GetFaction(factionID) } // GetFactionByName returns a faction by name func (m *Manager) GetFactionByName(name string) *Faction { m.mutex.Lock() m.factionLookups++ m.mutex.Unlock() return m.masterFactionList.GetFactionByName(name) } // AddFaction adds a new faction func (m *Manager) AddFaction(faction *Faction) error { if faction == nil { return fmt.Errorf("faction cannot be nil") } // Add to master list if err := m.masterFactionList.AddFaction(faction); err != nil { return fmt.Errorf("failed to add faction to master list: %w", err) } // Save to database if available if m.database != nil { if err := m.database.SaveFaction(faction); err != nil { // Remove from master list if database save failed m.masterFactionList.RemoveFaction(faction.ID) return fmt.Errorf("failed to save faction to database: %w", err) } } if m.logger != nil { m.logger.LogInfo("Added faction %d: %s (%s)", faction.ID, faction.Name, faction.Type) } return nil } // UpdateFaction updates an existing faction func (m *Manager) UpdateFaction(faction *Faction) error { if faction == nil { return fmt.Errorf("faction cannot be nil") } // Update in master list if err := m.masterFactionList.UpdateFaction(faction); err != nil { return fmt.Errorf("failed to update faction in master list: %w", err) } // Save to database if available if m.database != nil { if err := m.database.SaveFaction(faction); err != nil { return fmt.Errorf("failed to save faction to database: %w", err) } } if m.logger != nil { m.logger.LogInfo("Updated faction %d: %s", faction.ID, faction.Name) } return nil } // RemoveFaction removes a faction func (m *Manager) RemoveFaction(factionID int32) error { // Check if faction exists if !m.masterFactionList.HasFaction(factionID) { return fmt.Errorf("faction with ID %d does not exist", factionID) } // Remove from database first if available if m.database != nil { if err := m.database.DeleteFaction(factionID); err != nil { return fmt.Errorf("failed to delete faction from database: %w", err) } } // Remove from master list if !m.masterFactionList.RemoveFaction(factionID) { return fmt.Errorf("failed to remove faction from master list") } if m.logger != nil { m.logger.LogInfo("Removed faction %d", factionID) } return nil } // RecordFactionIncrease records a faction increase for statistics func (m *Manager) RecordFactionIncrease(factionID int32) { m.mutex.Lock() defer m.mutex.Unlock() m.totalFactionChanges++ m.factionIncreases++ m.changesByFaction[factionID]++ } // RecordFactionDecrease records a faction decrease for statistics func (m *Manager) RecordFactionDecrease(factionID int32) { m.mutex.Lock() defer m.mutex.Unlock() m.totalFactionChanges++ m.factionDecreases++ m.changesByFaction[factionID]++ } // GetStatistics returns faction system statistics func (m *Manager) GetStatistics() map[string]any { m.mutex.RLock() defer m.mutex.RUnlock() stats := make(map[string]any) stats["total_factions"] = m.masterFactionList.GetFactionCount() stats["total_faction_changes"] = m.totalFactionChanges stats["faction_increases"] = m.factionIncreases stats["faction_decreases"] = m.factionDecreases stats["faction_lookups"] = m.factionLookups stats["players_with_factions"] = m.playersWithFactions // Copy changes by faction changeStats := make(map[int32]int64) for factionID, count := range m.changesByFaction { changeStats[factionID] = count } stats["changes_by_faction"] = changeStats return stats } // ResetStatistics resets all statistics func (m *Manager) ResetStatistics() { m.mutex.Lock() defer m.mutex.Unlock() m.totalFactionChanges = 0 m.factionIncreases = 0 m.factionDecreases = 0 m.factionLookups = 0 m.playersWithFactions = 0 m.changesByFaction = make(map[int32]int64) } // ValidateAllFactions validates all factions in the system func (m *Manager) ValidateAllFactions() []string { return m.masterFactionList.ValidateFactions() } // ReloadFromDatabase reloads all factions from database func (m *Manager) ReloadFromDatabase() error { if m.database == nil { return fmt.Errorf("no database available") } // Clear current factions m.masterFactionList.Clear() // Reload from database return m.Initialize() } // GetFactionCount returns the total number of factions func (m *Manager) GetFactionCount() int32 { return m.masterFactionList.GetFactionCount() } // ProcessCommand handles faction-related commands func (m *Manager) ProcessCommand(command string, args []string) (string, error) { switch command { case "stats": return m.handleStatsCommand(args) case "validate": return m.handleValidateCommand(args) case "list": return m.handleListCommand(args) case "info": return m.handleInfoCommand(args) case "reload": return m.handleReloadCommand(args) case "search": return m.handleSearchCommand(args) default: return "", fmt.Errorf("unknown faction command: %s", command) } } // handleStatsCommand shows faction system statistics func (m *Manager) handleStatsCommand(args []string) (string, error) { stats := m.GetStatistics() result := "Faction System Statistics:\n" result += fmt.Sprintf("Total Factions: %d\n", stats["total_factions"]) result += fmt.Sprintf("Total Faction Changes: %d\n", stats["total_faction_changes"]) result += fmt.Sprintf("Faction Increases: %d\n", stats["faction_increases"]) result += fmt.Sprintf("Faction Decreases: %d\n", stats["faction_decreases"]) result += fmt.Sprintf("Faction Lookups: %d\n", stats["faction_lookups"]) result += fmt.Sprintf("Players with Factions: %d\n", stats["players_with_factions"]) return result, nil } // handleValidateCommand validates all factions func (m *Manager) handleValidateCommand(args []string) (string, error) { issues := m.ValidateAllFactions() if len(issues) == 0 { return "All factions are valid.", nil } result := fmt.Sprintf("Found %d issues with factions:\n", len(issues)) for i, issue := range issues { if i >= 10 { // Limit output result += "... (and more)\n" break } result += fmt.Sprintf("%d. %s\n", i+1, issue) } return result, nil } // handleListCommand lists factions func (m *Manager) handleListCommand(args []string) (string, error) { factions := m.masterFactionList.GetAllFactions() if len(factions) == 0 { return "No factions loaded.", nil } result := fmt.Sprintf("Factions (%d):\n", len(factions)) count := 0 for _, faction := range factions { if count >= 20 { // Limit output result += "... (and more)\n" break } result += fmt.Sprintf(" %d: %s (%s)\n", faction.ID, faction.Name, faction.Type) count++ } return result, nil } // handleInfoCommand shows information about a specific faction func (m *Manager) handleInfoCommand(args []string) (string, error) { if len(args) == 0 { return "", fmt.Errorf("faction ID or name required") } var faction *Faction // Try to parse as ID first var factionID int32 if _, err := fmt.Sscanf(args[0], "%d", &factionID); err == nil { faction = m.GetFaction(factionID) } else { // Try as name faction = m.GetFactionByName(args[0]) } if faction == nil { return fmt.Sprintf("Faction '%s' not found.", args[0]), nil } result := "Faction Information:\n" result += fmt.Sprintf("ID: %d\n", faction.ID) result += fmt.Sprintf("Name: %s\n", faction.Name) result += fmt.Sprintf("Type: %s\n", faction.Type) result += fmt.Sprintf("Description: %s\n", faction.Description) result += fmt.Sprintf("Default Value: %d\n", faction.DefaultValue) result += fmt.Sprintf("Positive Change: %d\n", faction.PositiveChange) result += fmt.Sprintf("Negative Change: %d\n", faction.NegativeChange) // Show relationships if any hostiles := m.masterFactionList.GetHostileFactions(faction.ID) if len(hostiles) > 0 { result += fmt.Sprintf("Hostile Factions: %v\n", hostiles) } friendlies := m.masterFactionList.GetFriendlyFactions(faction.ID) if len(friendlies) > 0 { result += fmt.Sprintf("Friendly Factions: %v\n", friendlies) } return result, nil } // handleReloadCommand reloads factions from database func (m *Manager) handleReloadCommand(args []string) (string, error) { if err := m.ReloadFromDatabase(); err != nil { return "", fmt.Errorf("failed to reload factions: %w", err) } count := m.GetFactionCount() return fmt.Sprintf("Successfully reloaded %d factions from database.", count), nil } // handleSearchCommand searches for factions by name or type func (m *Manager) handleSearchCommand(args []string) (string, error) { if len(args) == 0 { return "", fmt.Errorf("search term required") } searchTerm := args[0] factions := m.masterFactionList.GetAllFactions() var results []*Faction // Search by name or type for _, faction := range factions { if contains(faction.Name, searchTerm) || contains(faction.Type, searchTerm) { results = append(results, faction) } } if len(results) == 0 { return fmt.Sprintf("No factions found matching '%s'.", searchTerm), nil } result := fmt.Sprintf("Found %d factions matching '%s':\n", len(results), searchTerm) for i, faction := range results { if i >= 20 { // Limit output result += "... (and more)\n" break } result += fmt.Sprintf(" %d: %s (%s)\n", faction.ID, faction.Name, faction.Type) } return result, nil } // Shutdown gracefully shuts down the manager func (m *Manager) Shutdown() { if m.logger != nil { m.logger.LogInfo("Shutting down faction manager...") } // Clear factions m.masterFactionList.Clear() } // contains checks if a string contains a substring (case-sensitive) func contains(str, substr string) bool { if len(substr) == 0 { return true } if len(str) < len(substr) { return false } for i := 0; i <= len(str)-len(substr); i++ { if str[i:i+len(substr)] == substr { return true } } return false }