package appearances

import (
	"fmt"
	"sync"
)

// Database interface for appearance persistence
type Database interface {
	LoadAllAppearances() ([]*Appearance, error)
	SaveAppearance(appearance *Appearance) error
	DeleteAppearance(id int32) error
	LoadAppearancesByClientVersion(minClientVersion int16) ([]*Appearance, error)
}

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

// AppearanceProvider interface for entities that provide appearances
type AppearanceProvider interface {
	GetAppearanceID() int32
	SetAppearanceID(id int32)
	GetAppearance() *Appearance
	IsCompatibleWithClient(clientVersion int16) bool
}

// AppearanceAware interface for entities that use appearances
type AppearanceAware interface {
	GetAppearanceManager() *Manager
	FindAppearanceByID(id int32) *Appearance
	GetCompatibleAppearances(clientVersion int16) []*Appearance
}

// Client interface for appearance-related client operations
type Client interface {
	GetVersion() int16
	SendAppearanceUpdate(appearanceID int32) error
}

// AppearanceCache interface for caching appearance data
type AppearanceCache interface {
	Get(id int32) *Appearance
	Set(id int32, appearance *Appearance)
	Remove(id int32)
	Clear()
	GetSize() int
}

// EntityAppearanceAdapter provides appearance functionality for entities
type EntityAppearanceAdapter struct {
	entity       Entity
	appearanceID int32
	manager      *Manager
	logger       Logger
}

// Entity interface for things that can have appearances
type Entity interface {
	GetID() int32
	GetName() string
	GetDatabaseID() int32
}

// NewEntityAppearanceAdapter creates a new entity appearance adapter
func NewEntityAppearanceAdapter(entity Entity, manager *Manager, logger Logger) *EntityAppearanceAdapter {
	return &EntityAppearanceAdapter{
		entity:       entity,
		appearanceID: 0,
		manager:      manager,
		logger:       logger,
	}
}

// GetAppearanceID returns the entity's appearance ID
func (eaa *EntityAppearanceAdapter) GetAppearanceID() int32 {
	return eaa.appearanceID
}

// SetAppearanceID sets the entity's appearance ID
func (eaa *EntityAppearanceAdapter) SetAppearanceID(id int32) {
	eaa.appearanceID = id

	if eaa.logger != nil {
		eaa.logger.LogDebug("Entity %d (%s): Set appearance ID to %d",
			eaa.entity.GetID(), eaa.entity.GetName(), id)
	}
}

// GetAppearance returns the entity's appearance object
func (eaa *EntityAppearanceAdapter) GetAppearance() *Appearance {
	if eaa.appearanceID == 0 {
		return nil
	}

	if eaa.manager == nil {
		if eaa.logger != nil {
			eaa.logger.LogError("Entity %d (%s): No appearance manager available",
				eaa.entity.GetID(), eaa.entity.GetName())
		}
		return nil
	}

	return eaa.manager.FindAppearanceByID(eaa.appearanceID)
}

// IsCompatibleWithClient checks if the entity's appearance is compatible with client version
func (eaa *EntityAppearanceAdapter) IsCompatibleWithClient(clientVersion int16) bool {
	appearance := eaa.GetAppearance()
	if appearance == nil {
		return true // No appearance means compatible with all clients
	}

	return appearance.IsCompatibleWithClient(clientVersion)
}

// GetAppearanceName returns the name of the entity's appearance
func (eaa *EntityAppearanceAdapter) GetAppearanceName() string {
	appearance := eaa.GetAppearance()
	if appearance == nil {
		return ""
	}

	return appearance.GetName()
}

// ValidateAppearance validates that the entity's appearance exists and is valid
func (eaa *EntityAppearanceAdapter) ValidateAppearance() error {
	if eaa.appearanceID == 0 {
		return nil // No appearance is valid
	}

	appearance := eaa.GetAppearance()
	if appearance == nil {
		return fmt.Errorf("appearance ID %d not found", eaa.appearanceID)
	}

	return nil
}

// UpdateAppearance updates the entity's appearance from the manager
func (eaa *EntityAppearanceAdapter) UpdateAppearance(id int32) error {
	if eaa.manager == nil {
		return fmt.Errorf("no appearance manager available")
	}

	appearance := eaa.manager.FindAppearanceByID(id)
	if appearance == nil {
		return fmt.Errorf("appearance ID %d not found", id)
	}

	eaa.SetAppearanceID(id)

	if eaa.logger != nil {
		eaa.logger.LogInfo("Entity %d (%s): Updated appearance to %d (%s)",
			eaa.entity.GetID(), eaa.entity.GetName(), id, appearance.GetName())
	}

	return nil
}

// SendAppearanceToClient sends the appearance to a client
func (eaa *EntityAppearanceAdapter) SendAppearanceToClient(client Client) error {
	if client == nil {
		return fmt.Errorf("client is nil")
	}

	if eaa.appearanceID == 0 {
		return nil // No appearance to send
	}

	// Check client compatibility
	if !eaa.IsCompatibleWithClient(client.GetVersion()) {
		if eaa.logger != nil {
			eaa.logger.LogWarning("Entity %d (%s): Appearance %d not compatible with client version %d",
				eaa.entity.GetID(), eaa.entity.GetName(), eaa.appearanceID, client.GetVersion())
		}
		return fmt.Errorf("appearance not compatible with client version %d", client.GetVersion())
	}

	return client.SendAppearanceUpdate(eaa.appearanceID)
}

// SimpleAppearanceCache is a basic in-memory appearance cache
type SimpleAppearanceCache struct {
	cache map[int32]*Appearance
	mutex sync.RWMutex
}

// NewSimpleAppearanceCache creates a new simple appearance cache
func NewSimpleAppearanceCache() *SimpleAppearanceCache {
	return &SimpleAppearanceCache{
		cache: make(map[int32]*Appearance),
	}
}

// Get retrieves an appearance from cache
func (sac *SimpleAppearanceCache) Get(id int32) *Appearance {
	sac.mutex.RLock()
	defer sac.mutex.RUnlock()

	return sac.cache[id]
}

// Set stores an appearance in cache
func (sac *SimpleAppearanceCache) Set(id int32, appearance *Appearance) {
	sac.mutex.Lock()
	defer sac.mutex.Unlock()

	sac.cache[id] = appearance
}

// Remove removes an appearance from cache
func (sac *SimpleAppearanceCache) Remove(id int32) {
	sac.mutex.Lock()
	defer sac.mutex.Unlock()

	delete(sac.cache, id)
}

// Clear removes all appearances from cache
func (sac *SimpleAppearanceCache) Clear() {
	sac.mutex.Lock()
	defer sac.mutex.Unlock()

	sac.cache = make(map[int32]*Appearance)
}

// GetSize returns the number of cached appearances
func (sac *SimpleAppearanceCache) GetSize() int {
	sac.mutex.RLock()
	defer sac.mutex.RUnlock()

	return len(sac.cache)
}

// CachedAppearanceManager wraps a Manager with caching functionality
type CachedAppearanceManager struct {
	*Manager
	cache AppearanceCache
}

// NewCachedAppearanceManager creates a new cached appearance manager
func NewCachedAppearanceManager(manager *Manager, cache AppearanceCache) *CachedAppearanceManager {
	return &CachedAppearanceManager{
		Manager: manager,
		cache:   cache,
	}
}

// FindAppearanceByID finds an appearance with caching
func (cam *CachedAppearanceManager) FindAppearanceByID(id int32) *Appearance {
	// Check cache first
	if appearance := cam.cache.Get(id); appearance != nil {
		return appearance
	}

	// Load from manager
	appearance := cam.Manager.FindAppearanceByID(id)
	if appearance != nil {
		// Cache the result
		cam.cache.Set(id, appearance)
	}

	return appearance
}

// AddAppearance adds an appearance and updates cache
func (cam *CachedAppearanceManager) AddAppearance(appearance *Appearance) error {
	err := cam.Manager.AddAppearance(appearance)
	if err == nil {
		// Update cache
		cam.cache.Set(appearance.GetID(), appearance)
	}

	return err
}

// UpdateAppearance updates an appearance and cache
func (cam *CachedAppearanceManager) UpdateAppearance(appearance *Appearance) error {
	err := cam.Manager.UpdateAppearance(appearance)
	if err == nil {
		// Update cache
		cam.cache.Set(appearance.GetID(), appearance)
	}

	return err
}

// RemoveAppearance removes an appearance and updates cache
func (cam *CachedAppearanceManager) RemoveAppearance(id int32) error {
	err := cam.Manager.RemoveAppearance(id)
	if err == nil {
		// Remove from cache
		cam.cache.Remove(id)
	}

	return err
}

// ClearCache clears the appearance cache
func (cam *CachedAppearanceManager) ClearCache() {
	cam.cache.Clear()
}
