package languages

import (
	"fmt"
	"sync"
)

// Manager provides high-level management of the language system
type Manager struct {
	masterLanguagesList *MasterLanguagesList
	database            Database
	logger              Logger
	mutex               sync.RWMutex

	// Statistics
	languageLookups      int64
	playersWithLanguages int64
	languageUsageCount   map[int32]int64 // Language ID -> usage count
}

// NewManager creates a new language manager
func NewManager(database Database, logger Logger) *Manager {
	return &Manager{
		masterLanguagesList: NewMasterLanguagesList(),
		database:            database,
		logger:              logger,
		languageUsageCount:  make(map[int32]int64),
	}
}

// Initialize loads languages from database
func (m *Manager) Initialize() error {
	if m.logger != nil {
		m.logger.LogInfo("Initializing language manager...")
	}

	if m.database == nil {
		if m.logger != nil {
			m.logger.LogWarning("No database provided, starting with empty language list")
		}
		return nil
	}

	// Load languages from database
	languages, err := m.database.LoadAllLanguages()
	if err != nil {
		return fmt.Errorf("failed to load languages from database: %w", err)
	}

	for _, language := range languages {
		if err := m.masterLanguagesList.AddLanguage(language); err != nil {
			if m.logger != nil {
				m.logger.LogError("Failed to add language %d (%s): %v", language.GetID(), language.GetName(), err)
			}
		}
	}

	if m.logger != nil {
		m.logger.LogInfo("Loaded %d languages from database", len(languages))
	}

	return nil
}

// GetMasterLanguagesList returns the master language list
func (m *Manager) GetMasterLanguagesList() *MasterLanguagesList {
	return m.masterLanguagesList
}

// CreatePlayerLanguagesList creates a new player language list
func (m *Manager) CreatePlayerLanguagesList() *PlayerLanguagesList {
	m.mutex.Lock()
	m.playersWithLanguages++
	m.mutex.Unlock()

	return NewPlayerLanguagesList()
}

// GetLanguage returns a language by ID
func (m *Manager) GetLanguage(id int32) *Language {
	m.mutex.Lock()
	m.languageLookups++
	m.mutex.Unlock()

	return m.masterLanguagesList.GetLanguage(id)
}

// GetLanguageByName returns a language by name
func (m *Manager) GetLanguageByName(name string) *Language {
	m.mutex.Lock()
	m.languageLookups++
	m.mutex.Unlock()

	return m.masterLanguagesList.GetLanguageByName(name)
}

// AddLanguage adds a new language to the system
func (m *Manager) AddLanguage(language *Language) error {
	if language == nil {
		return fmt.Errorf("language cannot be nil")
	}

	// Add to master list
	if err := m.masterLanguagesList.AddLanguage(language); err != nil {
		return fmt.Errorf("failed to add language to master list: %w", err)
	}

	// Save to database if available
	if m.database != nil {
		if err := m.database.SaveLanguage(language); err != nil {
			// Remove from master list if database save failed
			m.masterLanguagesList.RemoveLanguage(language.GetID())
			return fmt.Errorf("failed to save language to database: %w", err)
		}
	}

	if m.logger != nil {
		m.logger.LogInfo("Added language %d: %s", language.GetID(), language.GetName())
	}

	return nil
}

// UpdateLanguage updates an existing language
func (m *Manager) UpdateLanguage(language *Language) error {
	if language == nil {
		return fmt.Errorf("language cannot be nil")
	}

	// Update in master list
	if err := m.masterLanguagesList.UpdateLanguage(language); err != nil {
		return fmt.Errorf("failed to update language in master list: %w", err)
	}

	// Save to database if available
	if m.database != nil {
		if err := m.database.SaveLanguage(language); err != nil {
			return fmt.Errorf("failed to save language to database: %w", err)
		}
	}

	if m.logger != nil {
		m.logger.LogInfo("Updated language %d: %s", language.GetID(), language.GetName())
	}

	return nil
}

// RemoveLanguage removes a language from the system
func (m *Manager) RemoveLanguage(id int32) error {
	// Check if language exists
	if !m.masterLanguagesList.HasLanguage(id) {
		return fmt.Errorf("language with ID %d does not exist", id)
	}

	// Remove from database first if available
	if m.database != nil {
		if err := m.database.DeleteLanguage(id); err != nil {
			return fmt.Errorf("failed to delete language from database: %w", err)
		}
	}

	// Remove from master list
	if !m.masterLanguagesList.RemoveLanguage(id) {
		return fmt.Errorf("failed to remove language from master list")
	}

	if m.logger != nil {
		m.logger.LogInfo("Removed language %d", id)
	}

	return nil
}

// RecordLanguageUsage records language usage for statistics
func (m *Manager) RecordLanguageUsage(languageID int32) {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	m.languageUsageCount[languageID]++
}

// GetStatistics returns language system statistics
func (m *Manager) GetStatistics() *LanguageStatistics {
	m.mutex.RLock()
	defer m.mutex.RUnlock()

	// Create language name mapping
	languagesByName := make(map[string]int32)
	allLanguages := m.masterLanguagesList.GetAllLanguages()
	for _, lang := range allLanguages {
		languagesByName[lang.GetName()] = lang.GetID()
	}

	// Copy usage count
	usageCount := make(map[int32]int64)
	for id, count := range m.languageUsageCount {
		usageCount[id] = count
	}

	return &LanguageStatistics{
		TotalLanguages:       len(allLanguages),
		PlayersWithLanguages: int(m.playersWithLanguages),
		LanguageUsageCount:   usageCount,
		LanguageLookups:      m.languageLookups,
		LanguagesByName:      languagesByName,
	}
}

// ResetStatistics resets all statistics
func (m *Manager) ResetStatistics() {
	m.mutex.Lock()
	defer m.mutex.Unlock()

	m.languageLookups = 0
	m.playersWithLanguages = 0
	m.languageUsageCount = make(map[int32]int64)
}

// ValidateAllLanguages validates all languages in the system
func (m *Manager) ValidateAllLanguages() []string {
	allLanguages := m.masterLanguagesList.GetAllLanguages()
	var issues []string

	for _, lang := range allLanguages {
		if !lang.IsValid() {
			issues = append(issues, fmt.Sprintf("Language %d (%s) is invalid", lang.GetID(), lang.GetName()))
		}
	}

	return issues
}

// ReloadFromDatabase reloads all languages from database
func (m *Manager) ReloadFromDatabase() error {
	if m.database == nil {
		return fmt.Errorf("no database available")
	}

	// Clear current languages
	m.masterLanguagesList.Clear()

	// Reload from database
	return m.Initialize()
}

// GetLanguageCount returns the total number of languages
func (m *Manager) GetLanguageCount() int32 {
	return m.masterLanguagesList.Size()
}

// ProcessCommand handles language-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 "add":
		return m.handleAddCommand(args)
	case "remove":
		return m.handleRemoveCommand(args)
	case "search":
		return m.handleSearchCommand(args)
	default:
		return "", fmt.Errorf("unknown language command: %s", command)
	}
}

// handleStatsCommand shows language system statistics
func (m *Manager) handleStatsCommand(args []string) (string, error) {
	stats := m.GetStatistics()

	result := "Language System Statistics:\n"
	result += fmt.Sprintf("Total Languages: %d\n", stats.TotalLanguages)
	result += fmt.Sprintf("Players with Languages: %d\n", stats.PlayersWithLanguages)
	result += fmt.Sprintf("Language Lookups: %d\n", stats.LanguageLookups)

	if len(stats.LanguageUsageCount) > 0 {
		result += "\nMost Used Languages:\n"
		// Show top 5 most used languages
		count := 0
		for langID, usage := range stats.LanguageUsageCount {
			if count >= 5 {
				break
			}
			lang := m.GetLanguage(langID)
			name := "Unknown"
			if lang != nil {
				name = lang.GetName()
			}
			result += fmt.Sprintf("  %s (ID: %d): %d uses\n", name, langID, usage)
			count++
		}
	}

	return result, nil
}

// handleValidateCommand validates all languages
func (m *Manager) handleValidateCommand(args []string) (string, error) {
	issues := m.ValidateAllLanguages()

	if len(issues) == 0 {
		return "All languages are valid.", nil
	}

	result := fmt.Sprintf("Found %d issues with languages:\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 languages
func (m *Manager) handleListCommand(args []string) (string, error) {
	languages := m.masterLanguagesList.GetAllLanguages()

	if len(languages) == 0 {
		return "No languages loaded.", nil
	}

	result := fmt.Sprintf("Languages (%d):\n", len(languages))
	count := 0
	for _, language := range languages {
		if count >= 20 { // Limit output
			result += "... (and more)\n"
			break
		}
		result += fmt.Sprintf("  %d: %s\n", language.GetID(), language.GetName())
		count++
	}

	return result, nil
}

// handleInfoCommand shows information about a specific language
func (m *Manager) handleInfoCommand(args []string) (string, error) {
	if len(args) == 0 {
		return "", fmt.Errorf("language ID or name required")
	}

	var language *Language

	// Try to parse as ID first
	var languageID int32
	if _, err := fmt.Sscanf(args[0], "%d", &languageID); err == nil {
		language = m.GetLanguage(languageID)
	} else {
		// Try as name
		language = m.GetLanguageByName(args[0])
	}

	if language == nil {
		return fmt.Sprintf("Language '%s' not found.", args[0]), nil
	}

	result := fmt.Sprintf("Language Information:\n")
	result += fmt.Sprintf("ID: %d\n", language.GetID())
	result += fmt.Sprintf("Name: %s\n", language.GetName())
	result += fmt.Sprintf("Save Needed: %v\n", language.GetSaveNeeded())

	// Show usage statistics if available
	m.mutex.RLock()
	if usage, exists := m.languageUsageCount[language.GetID()]; exists {
		result += fmt.Sprintf("Usage Count: %d\n", usage)
	} else {
		result += "Usage Count: 0\n"
	}
	m.mutex.RUnlock()

	return result, nil
}

// handleReloadCommand reloads languages from database
func (m *Manager) handleReloadCommand(args []string) (string, error) {
	if err := m.ReloadFromDatabase(); err != nil {
		return "", fmt.Errorf("failed to reload languages: %w", err)
	}

	count := m.GetLanguageCount()
	return fmt.Sprintf("Successfully reloaded %d languages from database.", count), nil
}

// handleAddCommand adds a new language
func (m *Manager) handleAddCommand(args []string) (string, error) {
	if len(args) < 2 {
		return "", fmt.Errorf("usage: add <id> <name>")
	}

	var id int32
	if _, err := fmt.Sscanf(args[0], "%d", &id); err != nil {
		return "", fmt.Errorf("invalid language ID: %s", args[0])
	}

	name := args[1]

	language := NewLanguage()
	language.SetID(id)
	language.SetName(name)
	language.SetSaveNeeded(true)

	if err := m.AddLanguage(language); err != nil {
		return "", fmt.Errorf("failed to add language: %w", err)
	}

	return fmt.Sprintf("Successfully added language %d: %s", id, name), nil
}

// handleRemoveCommand removes a language
func (m *Manager) handleRemoveCommand(args []string) (string, error) {
	if len(args) == 0 {
		return "", fmt.Errorf("language ID required")
	}

	var id int32
	if _, err := fmt.Sscanf(args[0], "%d", &id); err != nil {
		return "", fmt.Errorf("invalid language ID: %s", args[0])
	}

	if err := m.RemoveLanguage(id); err != nil {
		return "", fmt.Errorf("failed to remove language: %w", err)
	}

	return fmt.Sprintf("Successfully removed language %d", id), nil
}

// handleSearchCommand searches for languages by name
func (m *Manager) handleSearchCommand(args []string) (string, error) {
	if len(args) == 0 {
		return "", fmt.Errorf("search term required")
	}

	searchTerm := args[0]
	languages := m.masterLanguagesList.GetAllLanguages()
	var results []*Language

	// Search by name (case-insensitive partial match)
	for _, language := range languages {
		if contains(language.GetName(), searchTerm) {
			results = append(results, language)
		}
	}

	if len(results) == 0 {
		return fmt.Sprintf("No languages found matching '%s'.", searchTerm), nil
	}

	result := fmt.Sprintf("Found %d languages matching '%s':\n", len(results), searchTerm)
	for i, language := range results {
		if i >= 20 { // Limit output
			result += "... (and more)\n"
			break
		}
		result += fmt.Sprintf("  %d: %s\n", language.GetID(), language.GetName())
	}

	return result, nil
}

// Shutdown gracefully shuts down the manager
func (m *Manager) Shutdown() {
	if m.logger != nil {
		m.logger.LogInfo("Shutting down language manager...")
	}

	// Clear languages
	m.masterLanguagesList.Clear()
}

// contains checks if a string contains a substring (case-insensitive)
func contains(str, substr string) bool {
	if len(substr) == 0 {
		return true
	}
	if len(str) < len(substr) {
		return false
	}

	// Convert to lowercase for case-insensitive comparison
	strLower := make([]byte, len(str))
	substrLower := make([]byte, len(substr))

	for i := 0; i < len(str); i++ {
		if str[i] >= 'A' && str[i] <= 'Z' {
			strLower[i] = str[i] + 32
		} else {
			strLower[i] = str[i]
		}
	}

	for i := 0; i < len(substr); i++ {
		if substr[i] >= 'A' && substr[i] <= 'Z' {
			substrLower[i] = substr[i] + 32
		} else {
			substrLower[i] = substr[i]
		}
	}

	for i := 0; i <= len(strLower)-len(substrLower); i++ {
		match := true
		for j := 0; j < len(substrLower); j++ {
			if strLower[i+j] != substrLower[j] {
				match = false
				break
			}
		}
		if match {
			return true
		}
	}

	return false
}
