package alt_advancement

import (
	"database/sql"
	"log"
	"sync"
	"time"
)

// AADatabase interface for database operations
type AADatabase interface {
	// Core data loading
	LoadAltAdvancements() error
	LoadTreeNodes() error

	// Player data operations
	LoadPlayerAA(characterID int32) (*AAPlayerState, error)
	SavePlayerAA(playerState *AAPlayerState) error
	DeletePlayerAA(characterID int32) error

	// Template operations
	LoadPlayerAADefaults(classID int8) (map[int8][]*AAEntry, error)

	// Statistics
	GetAAStatistics() (map[string]interface{}, error)
}

// AAPacketHandler interface for handling AA-related packets
type AAPacketHandler interface {
	// List packets
	GetAAListPacket(client interface{}) ([]byte, error)
	SendAAUpdate(client interface{}, playerState *AAPlayerState) error

	// Purchase packets
	HandleAAPurchase(client interface{}, nodeID int32, rank int8) error
	SendAAPurchaseResponse(client interface{}, success bool, nodeID int32, newRank int8) error

	// Template packets
	SendAATemplateList(client interface{}, templates map[int8]*AATemplate) error
	HandleAATemplateChange(client interface{}, templateID int8) error

	// Display packets
	DisplayAA(client interface{}, templateID int8, changeMode int8) error
	SendAATabUpdate(client interface{}, tabID int8, tab *AATab) error
}

// AAEventHandler interface for handling AA events
type AAEventHandler interface {
	// Purchase events
	OnAAPurchased(characterID int32, nodeID int32, newRank int8, pointsSpent int32) error
	OnAARefunded(characterID int32, nodeID int32, oldRank int8, pointsRefunded int32) error

	// Template events
	OnAATemplateChanged(characterID int32, oldTemplate, newTemplate int8) error
	OnAATemplateCreated(characterID int32, templateID int8, name string) error

	// System events
	OnAASystemLoaded(totalAAs int32, totalNodes int32) error
	OnAADataReloaded() error

	// Player events
	OnPlayerAALoaded(characterID int32, playerState *AAPlayerState) error
	OnPlayerAAPointsChanged(characterID int32, oldPoints, newPoints int32) error
}

// AAValidator interface for validating AA operations
type AAValidator interface {
	// Purchase validation
	ValidateAAPurchase(playerState *AAPlayerState, nodeID int32, targetRank int8) error
	ValidateAAPrerequisites(playerState *AAPlayerState, aaData *AltAdvanceData) error
	ValidateAAPoints(playerState *AAPlayerState, pointsRequired int32) error

	// Player validation
	ValidatePlayerLevel(playerState *AAPlayerState, aaData *AltAdvanceData) error
	ValidatePlayerClass(playerState *AAPlayerState, aaData *AltAdvanceData) error
	ValidateExpansionRequirements(playerState *AAPlayerState, aaData *AltAdvanceData) error

	// Template validation
	ValidateTemplateChange(playerState *AAPlayerState, templateID int8) error
	ValidateTemplateEntries(entries []*AAEntry) error

	// System validation
	ValidateAAData(aaData *AltAdvanceData) error
	ValidateTreeNodeData(nodeData *TreeNodeData) error
}

// AANotifier interface for sending notifications
type AANotifier interface {
	// Purchase notifications
	NotifyAAPurchaseSuccess(characterID int32, aaName string, newRank int8) error
	NotifyAAPurchaseFailure(characterID int32, reason string) error
	NotifyAARefund(characterID int32, aaName string, pointsRefunded int32) error

	// Progress notifications
	NotifyAAProgressUpdate(characterID int32, tabID int8, pointsSpent int32) error
	NotifyAAPointsAwarded(characterID int32, pointsAwarded int32, reason string) error

	// System notifications
	NotifyAASystemUpdate(message string) error
	NotifyAASystemMaintenance(maintenanceStart time.Time, duration time.Duration) error

	// Achievement notifications
	NotifyAAMilestone(characterID int32, milestone string, totalPoints int32) error
	NotifyAATreeCompleted(characterID int32, tabID int8, tabName string) error
}

// AAStatistics interface for tracking AA statistics
type AAStatistics interface {
	// Purchase statistics
	RecordAAPurchase(characterID int32, nodeID int32, pointsSpent int32)
	RecordAARefund(characterID int32, nodeID int32, pointsRefunded int32)

	// Usage statistics
	RecordAAUsage(characterID int32, nodeID int32, usageType string)
	RecordPlayerLogin(characterID int32, totalAAPoints int32)
	RecordPlayerLogout(characterID int32, sessionDuration time.Duration)

	// Performance statistics
	RecordDatabaseQuery(queryType string, duration time.Duration)
	RecordPacketSent(packetType string, size int32)
	RecordCacheHit(cacheType string)
	RecordCacheMiss(cacheType string)

	// Aggregated statistics
	GetAAPurchaseStats() map[int32]int64
	GetPopularAAs() map[int32]int64
	GetPlayerProgressStats() map[string]interface{}
	GetSystemPerformanceStats() map[string]interface{}
}

// AACache interface for caching AA data
type AACache interface {
	// AA data caching
	GetAA(nodeID int32) (*AltAdvanceData, bool)
	SetAA(nodeID int32, aaData *AltAdvanceData)
	InvalidateAA(nodeID int32)

	// Player state caching
	GetPlayerState(characterID int32) (*AAPlayerState, bool)
	SetPlayerState(characterID int32, playerState *AAPlayerState)
	InvalidatePlayerState(characterID int32)

	// Tree node caching
	GetTreeNode(treeID int32) (*TreeNodeData, bool)
	SetTreeNode(treeID int32, nodeData *TreeNodeData)
	InvalidateTreeNode(treeID int32)

	// Cache management
	Clear()
	GetStats() map[string]interface{}
	SetMaxSize(maxSize int32)
}

// Client interface for client operations (to avoid circular dependencies)
type Client interface {
	GetCharacterID() int32
	GetPlayer() Player
	SendPacket(data []byte) error
	GetClientVersion() int16
}

// Player interface for player operations (to avoid circular dependencies)
type Player interface {
	GetCharacterID() int32
	GetLevel() int8
	GetClass() int8
	GetRace() int8
	GetName() string
	GetAdventureClass() int8
	HasExpansion(expansionFlag int8) bool
}

// Transaction interface for database transactions
type Transaction interface {
	Exec(query string, args ...interface{}) (sql.Result, error)
	Query(query string, args ...interface{}) (*sql.Rows, error)
	QueryRow(query string, args ...interface{}) *sql.Row
	Commit() error
	Rollback() error
}

// DatabaseImpl provides a concrete implementation of AADatabase
type DatabaseImpl struct {
	db             *sql.DB
	masterAAList   *MasterAAList
	masterNodeList *MasterAANodeList
	logger         *log.Logger
}

// NewDatabaseImpl creates a new database implementation
func NewDatabaseImpl(db *sql.DB, masterAAList *MasterAAList, masterNodeList *MasterAANodeList, logger *log.Logger) *DatabaseImpl {
	return &DatabaseImpl{
		db:             db,
		masterAAList:   masterAAList,
		masterNodeList: masterNodeList,
		logger:         logger,
	}
}

// AAManagerInterface defines the main interface for AA management
type AAManagerInterface interface {
	// System lifecycle
	Start() error
	Stop() error
	IsRunning() bool

	// Data loading
	LoadAAData() error
	ReloadAAData() error

	// Player operations
	LoadPlayerAA(characterID int32) (*AAPlayerState, error)
	SavePlayerAA(characterID int32) error
	GetPlayerAAState(characterID int32) (*AAPlayerState, error)

	// AA operations
	PurchaseAA(characterID int32, nodeID int32, targetRank int8) error
	RefundAA(characterID int32, nodeID int32) error
	GetAvailableAAs(characterID int32, tabID int8) ([]*AltAdvanceData, error)

	// Template operations
	ChangeAATemplate(characterID int32, templateID int8) error
	SaveAATemplate(characterID int32, templateID int8, name string) error
	GetAATemplates(characterID int32) (map[int8]*AATemplate, error)

	// Point operations
	AwardAAPoints(characterID int32, points int32, reason string) error
	GetAAPoints(characterID int32) (int32, int32, int32, error) // total, spent, available

	// Query operations
	GetAA(nodeID int32) (*AltAdvanceData, error)
	GetAABySpellID(spellID int32) (*AltAdvanceData, error)
	GetAAsByGroup(group int8) ([]*AltAdvanceData, error)
	GetAAsByClass(classID int8) ([]*AltAdvanceData, error)

	// Statistics
	GetSystemStats() *AAManagerStats
	GetPlayerStats(characterID int32) map[string]interface{}

	// Configuration
	SetConfig(config AAManagerConfig) error
	GetConfig() AAManagerConfig

	// Integration
	SetDatabase(db AADatabase)
	SetPacketHandler(handler AAPacketHandler)
	SetEventHandler(handler AAEventHandler)
	SetValidator(validator AAValidator)
	SetNotifier(notifier AANotifier)
	SetStatistics(stats AAStatistics)
	SetCache(cache AACache)
}

// AAAware interface for entities that can interact with the AA system
type AAAware interface {
	// AA point management
	GetAAPoints() (total, spent, available int32)
	SetAAPoints(total, spent, available int32)
	AwardAAPoints(points int32, reason string) error
	SpendAAPoints(points int32) error

	// AA progression
	GetAAState() *AAPlayerState
	SetAAState(state *AAPlayerState)
	GetAARank(nodeID int32) int8
	SetAARank(nodeID int32, rank int8) error

	// Template management
	GetActiveAATemplate() int8
	SetActiveAATemplate(templateID int8) error
	GetAATemplate(templateID int8) *AATemplate
	SaveAATemplate(templateID int8, name string) error
}

// AAAdapter adapts AA functionality for other systems
type AAAdapter struct {
	manager     AAManagerInterface
	characterID int32
}

// NewAAAdapter creates a new AA adapter
func NewAAAdapter(manager AAManagerInterface, characterID int32) *AAAdapter {
	return &AAAdapter{
		manager:     manager,
		characterID: characterID,
	}
}

// GetManager returns the wrapped AA manager
func (aa *AAAdapter) GetManager() AAManagerInterface {
	return aa.manager
}

// GetCharacterID returns the character ID
func (aa *AAAdapter) GetCharacterID() int32 {
	return aa.characterID
}

// GetAAPoints returns the character's AA points
func (aa *AAAdapter) GetAAPoints() (total, spent, available int32, err error) {
	return aa.manager.GetAAPoints(aa.characterID)
}

// PurchaseAA purchases an AA for the character
func (aa *AAAdapter) PurchaseAA(nodeID int32, targetRank int8) error {
	return aa.manager.PurchaseAA(aa.characterID, nodeID, targetRank)
}

// RefundAA refunds an AA for the character
func (aa *AAAdapter) RefundAA(nodeID int32) error {
	return aa.manager.RefundAA(aa.characterID, nodeID)
}

// GetAvailableAAs returns available AAs for a tab
func (aa *AAAdapter) GetAvailableAAs(tabID int8) ([]*AltAdvanceData, error) {
	return aa.manager.GetAvailableAAs(aa.characterID, tabID)
}

// ChangeTemplate changes the active AA template
func (aa *AAAdapter) ChangeTemplate(templateID int8) error {
	return aa.manager.ChangeAATemplate(aa.characterID, templateID)
}

// GetTemplates returns all AA temples for the character
func (aa *AAAdapter) GetTemplates() (map[int8]*AATemplate, error) {
	return aa.manager.GetAATemplates(aa.characterID)
}

// GetPlayerStats returns AA statistics for the character
func (aa *AAAdapter) GetPlayerStats() map[string]interface{} {
	return aa.manager.GetPlayerStats(aa.characterID)
}

// AwardPoints awards AA points to the character
func (aa *AAAdapter) AwardPoints(points int32, reason string) error {
	return aa.manager.AwardAAPoints(aa.characterID, points, reason)
}

// GetAAState returns the character's complete AA state
func (aa *AAAdapter) GetAAState() (*AAPlayerState, error) {
	return aa.manager.GetPlayerAAState(aa.characterID)
}

// SaveAAState saves the character's AA state
func (aa *AAAdapter) SaveAAState() error {
	return aa.manager.SavePlayerAA(aa.characterID)
}

// PlayerAAAdapter adapts player functionality for AA systems
type PlayerAAAdapter struct {
	player Player
}

// NewPlayerAAAdapter creates a new player AA adapter
func NewPlayerAAAdapter(player Player) *PlayerAAAdapter {
	return &PlayerAAAdapter{player: player}
}

// GetPlayer returns the wrapped player
func (paa *PlayerAAAdapter) GetPlayer() Player {
	return paa.player
}

// GetCharacterID returns the player's character ID
func (paa *PlayerAAAdapter) GetCharacterID() int32 {
	return paa.player.GetCharacterID()
}

// GetLevel returns the player's level
func (paa *PlayerAAAdapter) GetLevel() int8 {
	return paa.player.GetLevel()
}

// GetClass returns the player's class
func (paa *PlayerAAAdapter) GetClass() int8 {
	return paa.player.GetClass()
}

// GetAdventureClass returns the player's adventure class
func (paa *PlayerAAAdapter) GetAdventureClass() int8 {
	return paa.player.GetAdventureClass()
}

// GetRace returns the player's race
func (paa *PlayerAAAdapter) GetRace() int8 {
	return paa.player.GetRace()
}

// GetName returns the player's name
func (paa *PlayerAAAdapter) GetName() string {
	return paa.player.GetName()
}

// HasExpansion checks if the player has a specific expansion
func (paa *PlayerAAAdapter) HasExpansion(expansionFlag int8) bool {
	return paa.player.HasExpansion(expansionFlag)
}

// ClientAAAdapter adapts client functionality for AA systems
type ClientAAAdapter struct {
	client Client
}

// NewClientAAAdapter creates a new client AA adapter
func NewClientAAAdapter(client Client) *ClientAAAdapter {
	return &ClientAAAdapter{client: client}
}

// GetClient returns the wrapped client
func (caa *ClientAAAdapter) GetClient() Client {
	return caa.client
}

// GetCharacterID returns the client's character ID
func (caa *ClientAAAdapter) GetCharacterID() int32 {
	return caa.client.GetCharacterID()
}

// GetPlayer returns the client's player
func (caa *ClientAAAdapter) GetPlayer() Player {
	return caa.client.GetPlayer()
}

// SendPacket sends a packet to the client
func (caa *ClientAAAdapter) SendPacket(data []byte) error {
	return caa.client.SendPacket(data)
}

// GetClientVersion returns the client version
func (caa *ClientAAAdapter) GetClientVersion() int16 {
	return caa.client.GetClientVersion()
}

// Simple cache implementation for testing
type SimpleAACache struct {
	aaData       map[int32]*AltAdvanceData
	playerStates map[int32]*AAPlayerState
	treeNodes    map[int32]*TreeNodeData
	mutex        sync.RWMutex
	maxSize      int32
	hits         int64
	misses       int64
}

// NewSimpleAACache creates a new simple cache
func NewSimpleAACache(maxSize int32) *SimpleAACache {
	return &SimpleAACache{
		aaData:       make(map[int32]*AltAdvanceData),
		playerStates: make(map[int32]*AAPlayerState),
		treeNodes:    make(map[int32]*TreeNodeData),
		maxSize:      maxSize,
	}
}

// GetAA retrieves AA data from cache
func (c *SimpleAACache) GetAA(nodeID int32) (*AltAdvanceData, bool) {
	c.mutex.RLock()
	defer c.mutex.RUnlock()

	if data, exists := c.aaData[nodeID]; exists {
		c.hits++
		return data.Copy(), true
	}

	c.misses++
	return nil, false
}

// SetAA stores AA data in cache
func (c *SimpleAACache) SetAA(nodeID int32, aaData *AltAdvanceData) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if int32(len(c.aaData)) >= c.maxSize {
		// Simple eviction: remove a random entry
		for k := range c.aaData {
			delete(c.aaData, k)
			break
		}
	}

	c.aaData[nodeID] = aaData.Copy()
}

// InvalidateAA removes AA data from cache
func (c *SimpleAACache) InvalidateAA(nodeID int32) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	delete(c.aaData, nodeID)
}

// GetPlayerState retrieves player state from cache
func (c *SimpleAACache) GetPlayerState(characterID int32) (*AAPlayerState, bool) {
	c.mutex.RLock()
	defer c.mutex.RUnlock()

	if state, exists := c.playerStates[characterID]; exists {
		c.hits++
		return state, true
	}

	c.misses++
	return nil, false
}

// SetPlayerState stores player state in cache
func (c *SimpleAACache) SetPlayerState(characterID int32, playerState *AAPlayerState) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if int32(len(c.playerStates)) >= c.maxSize {
		// Simple eviction: remove a random entry
		for k := range c.playerStates {
			delete(c.playerStates, k)
			break
		}
	}

	c.playerStates[characterID] = playerState
}

// InvalidatePlayerState removes player state from cache
func (c *SimpleAACache) InvalidatePlayerState(characterID int32) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	delete(c.playerStates, characterID)
}

// GetTreeNode retrieves tree node from cache
func (c *SimpleAACache) GetTreeNode(treeID int32) (*TreeNodeData, bool) {
	c.mutex.RLock()
	defer c.mutex.RUnlock()

	if node, exists := c.treeNodes[treeID]; exists {
		c.hits++
		nodeCopy := *node
		return &nodeCopy, true
	}

	c.misses++
	return nil, false
}

// SetTreeNode stores tree node in cache
func (c *SimpleAACache) SetTreeNode(treeID int32, nodeData *TreeNodeData) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	if int32(len(c.treeNodes)) >= c.maxSize {
		// Simple eviction: remove a random entry
		for k := range c.treeNodes {
			delete(c.treeNodes, k)
			break
		}
	}

	nodeCopy := *nodeData
	c.treeNodes[treeID] = &nodeCopy
}

// InvalidateTreeNode removes tree node from cache
func (c *SimpleAACache) InvalidateTreeNode(treeID int32) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	delete(c.treeNodes, treeID)
}

// Clear removes all cached data
func (c *SimpleAACache) Clear() {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	c.aaData = make(map[int32]*AltAdvanceData)
	c.playerStates = make(map[int32]*AAPlayerState)
	c.treeNodes = make(map[int32]*TreeNodeData)
}

// GetStats returns cache statistics
func (c *SimpleAACache) GetStats() map[string]interface{} {
	c.mutex.RLock()
	defer c.mutex.RUnlock()

	return map[string]interface{}{
		"hits":            c.hits,
		"misses":          c.misses,
		"aa_data_count":   len(c.aaData),
		"player_count":    len(c.playerStates),
		"tree_node_count": len(c.treeNodes),
		"max_size":        c.maxSize,
	}
}

// SetMaxSize sets the maximum cache size
func (c *SimpleAACache) SetMaxSize(maxSize int32) {
	c.mutex.Lock()
	defer c.mutex.Unlock()

	c.maxSize = maxSize
}
