package languages

import "fmt"

// Database interface for language persistence
type Database interface {
	LoadAllLanguages() ([]*Language, error)
	SaveLanguage(language *Language) error
	DeleteLanguage(languageID int32) error
	LoadPlayerLanguages(playerID int32) ([]*Language, error)
	SavePlayerLanguage(playerID int32, languageID int32) error
	DeletePlayerLanguage(playerID int32, languageID int32) error
}

// Logger interface for language logging
type Logger interface {
	LogInfo(message string, args ...interface{})
	LogError(message string, args ...interface{})
	LogDebug(message string, args ...interface{})
	LogWarning(message string, args ...interface{})
}

// Player interface for language-related player operations
type Player interface {
	GetCharacterID() int32
	GetName() string
	GetLanguages() *PlayerLanguagesList
	KnowsLanguage(languageID int32) bool
	LearnLanguage(languageID int32) error
	ForgetLanguage(languageID int32) error
	SendMessage(message string)
}

// Client interface for language-related client operations
type Client interface {
	GetPlayer() *Player
	GetVersion() int16
	SendLanguageUpdate(languageData []byte) error
}

// LanguageProvider interface for systems that provide language functionality
type LanguageProvider interface {
	GetMasterLanguagesList() *MasterLanguagesList
	GetLanguage(languageID int32) *Language
	GetLanguageByName(name string) *Language
	CreatePlayerLanguagesList() *PlayerLanguagesList
}

// LanguageAware interface for entities that can understand languages
type LanguageAware interface {
	GetKnownLanguages() *PlayerLanguagesList
	KnowsLanguage(languageID int32) bool
	GetPrimaryLanguage() int32
	SetPrimaryLanguage(languageID int32)
	CanUnderstand(languageID int32) bool
}

// LanguageHandler interface for handling language events
type LanguageHandler interface {
	OnLanguageLearned(player *Player, languageID int32) error
	OnLanguageForgotten(player *Player, languageID int32) error
	OnLanguageUsed(player *Player, languageID int32, message string) error
}

// ChatProcessor interface for processing multilingual chat
type ChatProcessor interface {
	ProcessMessage(speaker *Player, message string, languageID int32) (string, error)
	FilterMessage(listener *Player, message string, languageID int32) string
	GetLanguageSkramble(message string, comprehension float32) string
}

// PlayerLanguageAdapter provides language functionality for players
type PlayerLanguageAdapter struct {
	player      *Player
	languages   *PlayerLanguagesList
	primaryLang int32
	manager     *Manager
	logger      Logger
}

// NewPlayerLanguageAdapter creates a new player language adapter
func NewPlayerLanguageAdapter(player *Player, manager *Manager, logger Logger) *PlayerLanguageAdapter {
	return &PlayerLanguageAdapter{
		player:      player,
		languages:   manager.CreatePlayerLanguagesList(),
		primaryLang: LanguageIDCommon, // Default to common
		manager:     manager,
		logger:      logger,
	}
}

// GetKnownLanguages returns the player's known languages
func (pla *PlayerLanguageAdapter) GetKnownLanguages() *PlayerLanguagesList {
	return pla.languages
}

// KnowsLanguage checks if the player knows a specific language
func (pla *PlayerLanguageAdapter) KnowsLanguage(languageID int32) bool {
	return pla.languages.HasLanguage(languageID)
}

// GetPrimaryLanguage returns the player's primary language
func (pla *PlayerLanguageAdapter) GetPrimaryLanguage() int32 {
	return pla.primaryLang
}

// SetPrimaryLanguage sets the player's primary language
func (pla *PlayerLanguageAdapter) SetPrimaryLanguage(languageID int32) {
	// Only allow setting to a known language
	if pla.languages.HasLanguage(languageID) {
		pla.primaryLang = languageID

		if pla.logger != nil {
			lang := pla.manager.GetLanguage(languageID)
			langName := "Unknown"
			if lang != nil {
				langName = lang.GetName()
			}
			pla.logger.LogDebug("Player %s set primary language to %s (%d)",
				pla.player.GetName(), langName, languageID)
		}
	}
}

// CanUnderstand checks if the player can understand a language
func (pla *PlayerLanguageAdapter) CanUnderstand(languageID int32) bool {
	// Common language is always understood
	if languageID == LanguageIDCommon {
		return true
	}

	// Check if player knows the language
	return pla.languages.HasLanguage(languageID)
}

// LearnLanguage teaches the player a new language
func (pla *PlayerLanguageAdapter) LearnLanguage(languageID int32) error {
	// Get the language from master list
	language := pla.manager.GetLanguage(languageID)
	if language == nil {
		return fmt.Errorf("language with ID %d does not exist", languageID)
	}

	// Check if already known
	if pla.languages.HasLanguage(languageID) {
		return fmt.Errorf("player already knows language %s", language.GetName())
	}

	// Create a copy for the player
	playerLang := language.Copy()
	playerLang.SetSaveNeeded(true)

	// Add to player's languages
	if err := pla.languages.Add(playerLang); err != nil {
		return fmt.Errorf("failed to add language to player: %w", err)
	}

	// Record usage statistics
	pla.manager.RecordLanguageUsage(languageID)

	if pla.logger != nil {
		pla.logger.LogInfo("Player %s learned language %s (%d)",
			pla.player.GetName(), language.GetName(), languageID)
	}

	return nil
}

// ForgetLanguage makes the player forget a language
func (pla *PlayerLanguageAdapter) ForgetLanguage(languageID int32) error {
	// Cannot forget common language
	if languageID == LanguageIDCommon {
		return fmt.Errorf("cannot forget common language")
	}

	// Check if player knows the language
	if !pla.languages.HasLanguage(languageID) {
		return fmt.Errorf("player does not know language %d", languageID)
	}

	// Get language name for logging
	language := pla.manager.GetLanguage(languageID)
	langName := "Unknown"
	if language != nil {
		langName = language.GetName()
	}

	// Remove from player's languages
	if !pla.languages.RemoveLanguage(languageID) {
		return fmt.Errorf("failed to remove language from player")
	}

	// Reset primary language if this was it
	if pla.primaryLang == languageID {
		pla.primaryLang = LanguageIDCommon
	}

	if pla.logger != nil {
		pla.logger.LogInfo("Player %s forgot language %s (%d)",
			pla.player.GetName(), langName, languageID)
	}

	return nil
}

// LoadPlayerLanguages loads the player's languages from database
func (pla *PlayerLanguageAdapter) LoadPlayerLanguages(database Database) error {
	if database == nil {
		return fmt.Errorf("database is nil")
	}

	playerID := pla.player.GetCharacterID()
	languages, err := database.LoadPlayerLanguages(playerID)
	if err != nil {
		return fmt.Errorf("failed to load player languages: %w", err)
	}

	// Clear current languages
	pla.languages.Clear()

	// Add loaded languages
	for _, lang := range languages {
		if err := pla.languages.Add(lang); err != nil && pla.logger != nil {
			pla.logger.LogWarning("Failed to add loaded language %d to player %s: %v",
				lang.GetID(), pla.player.GetName(), err)
		}
	}

	// Ensure player knows common language
	if !pla.languages.HasLanguage(LanguageIDCommon) {
		commonLang := pla.manager.GetLanguage(LanguageIDCommon)
		if commonLang != nil {
			playerCommon := commonLang.Copy()
			pla.languages.Add(playerCommon)
		}
	}

	if pla.logger != nil {
		pla.logger.LogDebug("Loaded %d languages for player %s",
			len(languages), pla.player.GetName())
	}

	return nil
}

// SavePlayerLanguages saves the player's languages to database
func (pla *PlayerLanguageAdapter) SavePlayerLanguages(database Database) error {
	if database == nil {
		return fmt.Errorf("database is nil")
	}

	playerID := pla.player.GetCharacterID()
	languages := pla.languages.GetAllLanguages()

	// Save each language that needs saving
	for _, lang := range languages {
		if lang.GetSaveNeeded() {
			if err := database.SavePlayerLanguage(playerID, lang.GetID()); err != nil {
				return fmt.Errorf("failed to save player language %d: %w", lang.GetID(), err)
			}
			lang.SetSaveNeeded(false)
		}
	}

	if pla.logger != nil {
		pla.logger.LogDebug("Saved languages for player %s", pla.player.GetName())
	}

	return nil
}

// ChatLanguageProcessor handles multilingual chat processing
type ChatLanguageProcessor struct {
	manager *Manager
	logger  Logger
}

// NewChatLanguageProcessor creates a new chat language processor
func NewChatLanguageProcessor(manager *Manager, logger Logger) *ChatLanguageProcessor {
	return &ChatLanguageProcessor{
		manager: manager,
		logger:  logger,
	}
}

// ProcessMessage processes a chat message in a specific language
func (clp *ChatLanguageProcessor) ProcessMessage(speaker *Player, message string, languageID int32) (string, error) {
	if speaker == nil {
		return "", fmt.Errorf("speaker cannot be nil")
	}

	// Validate language exists
	language := clp.manager.GetLanguage(languageID)
	if language == nil {
		return "", fmt.Errorf("language %d does not exist", languageID)
	}

	// Check if speaker knows the language
	if !speaker.KnowsLanguage(languageID) {
		return "", fmt.Errorf("speaker does not know language %s", language.GetName())
	}

	// Record language usage
	clp.manager.RecordLanguageUsage(languageID)

	return message, nil
}

// FilterMessage filters a message for a listener based on language comprehension
func (clp *ChatLanguageProcessor) FilterMessage(listener *Player, message string, languageID int32) string {
	if listener == nil {
		return message
	}

	// Common language is always understood
	if languageID == LanguageIDCommon {
		return message
	}

	// Check if listener knows the language
	if listener.KnowsLanguage(languageID) {
		return message
	}

	// Scramble the message for unknown languages
	return clp.GetLanguageSkramble(message, 0.0)
}

// GetLanguageSkramble scrambles a message based on comprehension level
func (clp *ChatLanguageProcessor) GetLanguageSkramble(message string, comprehension float32) string {
	if comprehension >= 1.0 {
		return message
	}

	if comprehension <= 0.0 {
		// Complete scramble - replace with gibberish
		runes := []rune(message)
		scrambled := make([]rune, len(runes))

		for i, r := range runes {
			if r == ' ' {
				scrambled[i] = ' '
			} else if r >= 'a' && r <= 'z' {
				scrambled[i] = 'a' + rune((int(r-'a')+7)%26)
			} else if r >= 'A' && r <= 'Z' {
				scrambled[i] = 'A' + rune((int(r-'A')+7)%26)
			} else {
				scrambled[i] = r
			}
		}

		return string(scrambled)
	}

	// Partial comprehension - scramble some words
	// This is a simplified implementation
	return message
}

// LanguageEventAdapter handles language-related events
type LanguageEventAdapter struct {
	handler LanguageHandler
	logger  Logger
}

// NewLanguageEventAdapter creates a new language event adapter
func NewLanguageEventAdapter(handler LanguageHandler, logger Logger) *LanguageEventAdapter {
	return &LanguageEventAdapter{
		handler: handler,
		logger:  logger,
	}
}

// ProcessLanguageEvent processes a language-related event
func (lea *LanguageEventAdapter) ProcessLanguageEvent(eventType string, player *Player, languageID int32, data interface{}) {
	if lea.handler == nil {
		return
	}

	switch eventType {
	case "language_learned":
		if err := lea.handler.OnLanguageLearned(player, languageID); err != nil && lea.logger != nil {
			lea.logger.LogError("Language learned handler failed: %v", err)
		}

	case "language_forgotten":
		if err := lea.handler.OnLanguageForgotten(player, languageID); err != nil && lea.logger != nil {
			lea.logger.LogError("Language forgotten handler failed: %v", err)
		}

	case "language_used":
		if message, ok := data.(string); ok {
			if err := lea.handler.OnLanguageUsed(player, languageID, message); err != nil && lea.logger != nil {
				lea.logger.LogError("Language used handler failed: %v", err)
			}
		}
	}
}
