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]interface{} {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	stats := make(map[string]interface{})
	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 := fmt.Sprintf("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
}
