package loot

import (
	"database/sql"
	"fmt"
	"log"

	"eq2emu/internal/items"
)

// LootSystem represents the complete loot system integration
type LootSystem struct {
	Database      *LootDatabase
	Manager       *LootManager
	ChestService  *ChestService
	PacketService *LootPacketService
}

// LootSystemConfig holds configuration for the loot system
type LootSystemConfig struct {
	DatabaseConnection    *sql.DB
	ItemMasterList       items.MasterItemListService
	PlayerService        PlayerService
	ZoneService          ZoneService
	ClientService        ClientService
	ItemPacketBuilder    ItemPacketBuilder
	StartCleanupTimer    bool
}

// NewLootSystem creates a complete loot system with all components
func NewLootSystem(config *LootSystemConfig) (*LootSystem, error) {
	if config.DatabaseConnection == nil {
		return nil, fmt.Errorf("database connection is required")
	}

	if config.ItemMasterList == nil {
		return nil, fmt.Errorf("item master list is required")
	}

	// Create database layer
	database := NewLootDatabase(config.DatabaseConnection)

	// Load loot data
	if err := database.LoadAllLootData(); err != nil {
		return nil, fmt.Errorf("failed to load loot data: %v", err)
	}

	// Create loot manager
	manager := NewLootManager(database, config.ItemMasterList)

	// Create chest service (optional - requires player and zone services)
	var chestService *ChestService
	if config.PlayerService != nil && config.ZoneService != nil {
		chestService = NewChestService(manager, config.PlayerService, config.ZoneService)
	}

	// Create packet service (optional - requires client and item packet builder)
	var packetService *LootPacketService
	if config.ClientService != nil && config.ItemPacketBuilder != nil {
		packetBuilder := NewLootPacketBuilder(config.ItemPacketBuilder)
		packetService = NewLootPacketService(packetBuilder, config.ClientService)
	}

	// Start cleanup timer if requested
	if config.StartCleanupTimer {
		manager.StartCleanupTimer()
	}

	system := &LootSystem{
		Database:      database,
		Manager:       manager,
		ChestService:  chestService,
		PacketService: packetService,
	}

	log.Printf("%s Loot system initialized successfully", LogPrefixLoot)
	return system, nil
}

// GenerateAndCreateChest generates loot for a spawn and creates a treasure chest
func (ls *LootSystem) GenerateAndCreateChest(spawnID int32, zoneID int32, x, y, z, heading float32, 
	context *LootContext) (*TreasureChest, error) {

	if ls.ChestService == nil {
		return nil, fmt.Errorf("chest service not available")
	}

	// Generate loot
	lootResult, err := ls.Manager.GenerateLoot(spawnID, context)
	if err != nil {
		return nil, fmt.Errorf("failed to generate loot: %v", err)
	}

	// Don't create chest if no loot
	if lootResult.IsEmpty() {
		log.Printf("%s No loot generated for spawn %d, not creating chest", LogPrefixLoot, spawnID)
		return nil, nil
	}

	// Create treasure chest
	chest, err := ls.ChestService.CreateTreasureChestFromLoot(spawnID, zoneID, x, y, z, heading, 
		lootResult, context.GroupMembers)
	if err != nil {
		return nil, fmt.Errorf("failed to create treasure chest: %v", err)
	}

	return chest, nil
}

// HandlePlayerLootInteraction handles a player's interaction with a chest and sends appropriate packets
func (ls *LootSystem) HandlePlayerLootInteraction(chestID int32, playerID uint32, 
	interaction ChestInteraction, itemUniqueID int64) error {

	if ls.ChestService == nil {
		return fmt.Errorf("chest service not available")
	}

	// Handle the interaction
	result := ls.ChestService.HandleChestInteraction(chestID, playerID, interaction, itemUniqueID)

	// Send response packet if packet service is available
	if ls.PacketService != nil {
		if err := ls.PacketService.SendLootResponse(result, playerID); err != nil {
			log.Printf("%s Failed to send loot response packet: %v", LogPrefixLoot, err)
		}

		// Send updated loot window if chest is still open and has items
		if result.Success && !result.ChestClosed {
			chest := ls.Manager.GetTreasureChest(chestID)
			if chest != nil && !chest.LootResult.IsEmpty() {
				if err := ls.PacketService.SendLootUpdate(chest, playerID); err != nil {
					log.Printf("%s Failed to send loot update packet: %v", LogPrefixLoot, err)
				}
			} else if chest != nil && chest.LootResult.IsEmpty() {
				// Send stopped looting packet for empty chest
				if err := ls.PacketService.SendStoppedLooting(chestID, playerID); err != nil {
					log.Printf("%s Failed to send stopped looting packet: %v", LogPrefixLoot, err)
				}
			}
		}
	}

	// Log the interaction
	log.Printf("%s Player %d %s chest %d: %s", 
		LogPrefixLoot, playerID, interaction.String(), chestID, result.Message)

	return nil
}

// ShowChestToPlayer sends the loot window to a player
func (ls *LootSystem) ShowChestToPlayer(chestID int32, playerID uint32) error {
	if ls.PacketService == nil {
		return fmt.Errorf("packet service not available")
	}

	chest := ls.Manager.GetTreasureChest(chestID)
	if chest == nil {
		return fmt.Errorf("chest %d not found", chestID)
	}

	// Check loot rights
	if !chest.HasLootRights(playerID) {
		return fmt.Errorf("player %d has no loot rights for chest %d", playerID, chestID)
	}

	// Send loot update packet
	return ls.PacketService.SendLootUpdate(chest, playerID)
}

// GetSystemStatistics returns comprehensive statistics about the loot system
func (ls *LootSystem) GetSystemStatistics() (map[string]interface{}, error) {
	stats := make(map[string]interface{})

	// Database statistics
	if dbStats, err := ls.Database.GetLootStatistics(); err == nil {
		stats["database"] = dbStats
	}

	// Manager statistics
	stats["generation"] = ls.Manager.GetStatistics()

	// Active chests count
	chestCount := 0
	ls.Manager.mutex.RLock()
	chestCount = len(ls.Manager.treasureChests)
	ls.Manager.mutex.RUnlock()
	stats["active_chests"] = chestCount

	return stats, nil
}

// ReloadAllData reloads all loot data from the database
func (ls *LootSystem) ReloadAllData() error {
	log.Printf("%s Reloading all loot system data", LogPrefixLoot)
	return ls.Database.LoadAllLootData()
}

// Shutdown gracefully shuts down the loot system
func (ls *LootSystem) Shutdown() error {
	log.Printf("%s Shutting down loot system", LogPrefixLoot)

	// Close database connections
	if err := ls.Database.Close(); err != nil {
		log.Printf("%s Error closing database: %v", LogPrefixLoot, err)
		return err
	}

	// Clear active chests
	ls.Manager.mutex.Lock()
	ls.Manager.treasureChests = make(map[int32]*TreasureChest)
	ls.Manager.mutex.Unlock()

	log.Printf("%s Loot system shutdown complete", LogPrefixLoot)
	return nil
}

// AddLootTableWithDrops adds a complete loot table with drops in a single transaction
func (ls *LootSystem) AddLootTableWithDrops(table *LootTable) error {
	return ls.Database.AddLootTable(table)
}

// CreateQuickLootTable creates a simple loot table with basic parameters
func (ls *LootSystem) CreateQuickLootTable(tableID int32, name string, items []QuickLootItem, 
	minCoin, maxCoin int32, maxItems int16) error {

	table := &LootTable{
		ID:                  tableID,
		Name:                name,
		MinCoin:             minCoin,
		MaxCoin:             maxCoin,
		MaxLootItems:        maxItems,
		LootDropProbability: DefaultLootDropProbability,
		CoinProbability:     DefaultCoinProbability,
		Drops:               make([]*LootDrop, len(items)),
	}

	for i, item := range items {
		table.Drops[i] = &LootDrop{
			LootTableID: tableID,
			ItemID:      item.ItemID,
			ItemCharges: item.Charges,
			EquipItem:   item.AutoEquip,
			Probability: item.Probability,
		}
	}

	return ls.AddLootTableWithDrops(table)
}

// QuickLootItem represents a simple loot item for quick table creation
type QuickLootItem struct {
	ItemID      int32
	Charges     int16
	Probability float32
	AutoEquip   bool
}

// AssignLootToSpawns assigns a loot table to multiple spawns
func (ls *LootSystem) AssignLootToSpawns(tableID int32, spawnIDs []int32) error {
	for _, spawnID := range spawnIDs {
		if err := ls.Database.AddSpawnLoot(spawnID, tableID); err != nil {
			return fmt.Errorf("failed to assign loot table %d to spawn %d: %v", tableID, spawnID, err)
		}
	}

	log.Printf("%s Assigned loot table %d to %d spawns", LogPrefixLoot, tableID, len(spawnIDs))
	return nil
}

// CreateGlobalLevelLoot creates global loot for a level range
func (ls *LootSystem) CreateGlobalLevelLoot(minLevel, maxLevel int8, tableID int32, tier int32) error {
	global := &GlobalLoot{
		Type:     GlobalLootTypeLevel,
		MinLevel: minLevel,
		MaxLevel: maxLevel,
		TableID:  tableID,
		LootTier: tier,
	}

	// Insert into database
	query := `INSERT INTO loot_global (type, loot_table, value1, value2, value3, value4) VALUES (?, ?, ?, ?, ?, ?)`
	_, err := ls.Database.db.Exec(query, "level", tableID, minLevel, maxLevel, tier, 0)
	if err != nil {
		return fmt.Errorf("failed to insert global level loot: %v", err)
	}

	// Add to in-memory cache
	ls.Database.mutex.Lock()
	ls.Database.globalLoot = append(ls.Database.globalLoot, global)
	ls.Database.mutex.Unlock()

	log.Printf("%s Created global level loot for levels %d-%d using table %d", 
		LogPrefixLoot, minLevel, maxLevel, tableID)
	
	return nil
}

// GetActiveChestsInZone returns all active chests in a specific zone
func (ls *LootSystem) GetActiveChestsInZone(zoneID int32) []*TreasureChest {
	return ls.Manager.GetZoneChests(zoneID)
}

// CleanupZoneChests removes all chests from a specific zone
func (ls *LootSystem) CleanupZoneChests(zoneID int32) {
	chests := ls.Manager.GetZoneChests(zoneID)
	
	for _, chest := range chests {
		ls.Manager.RemoveTreasureChest(chest.ID)
		
		// Remove from zone if chest service is available
		if ls.ChestService != nil {
			ls.ChestService.zoneService.RemoveObjectFromZone(zoneID, chest.ID)
		}
	}

	log.Printf("%s Cleaned up %d chests from zone %d", LogPrefixLoot, len(chests), zoneID)
}

// ValidateItemsInLootTables checks that all items in loot tables exist in the item master list
func (ls *LootSystem) ValidateItemsInLootTables() []ValidationError {
	var errors []ValidationError

	ls.Database.mutex.RLock()
	defer ls.Database.mutex.RUnlock()

	for tableID, table := range ls.Database.lootTables {
		for _, drop := range table.Drops {
			item := ls.Manager.itemMasterList.GetItem(drop.ItemID)
			if item == nil {
				errors = append(errors, ValidationError{
					Type:        "missing_item",
					TableID:     tableID,
					ItemID:      drop.ItemID,
					Description: fmt.Sprintf("Item %d in loot table %d (%s) does not exist", drop.ItemID, tableID, table.Name),
				})
			}
		}
	}

	if len(errors) > 0 {
		log.Printf("%s Found %d validation errors in loot tables", LogPrefixLoot, len(errors))
	}

	return errors
}

// ValidationError represents a loot system validation error
type ValidationError struct {
	Type        string `json:"type"`
	TableID     int32  `json:"table_id"`
	ItemID      int32  `json:"item_id,omitempty"`
	Description string `json:"description"`
}

// GetLootPreview generates a preview of potential loot without actually creating it
func (ls *LootSystem) GetLootPreview(spawnID int32, context *LootContext) (*LootPreview, error) {
	tableIDs := ls.Database.GetSpawnLootTables(spawnID)
	globalLoot := ls.Database.GetGlobalLootTables(context.PlayerLevel, context.PlayerRace, context.ZoneID)
	
	for _, global := range globalLoot {
		tableIDs = append(tableIDs, global.TableID)
	}

	preview := &LootPreview{
		SpawnID:     spawnID,
		TableIDs:    tableIDs,
		PossibleItems: make([]*LootPreviewItem, 0),
		MinCoins:    0,
		MaxCoins:    0,
	}

	for _, tableID := range tableIDs {
		table := ls.Database.GetLootTable(tableID)
		if table == nil {
			continue
		}

		preview.MinCoins += table.MinCoin
		preview.MaxCoins += table.MaxCoin

		for _, drop := range table.Drops {
			item := ls.Manager.itemMasterList.GetItem(drop.ItemID)
			if item == nil {
				continue
			}

			previewItem := &LootPreviewItem{
				ItemID:      drop.ItemID,
				ItemName:    item.Name,
				Probability: drop.Probability,
				Tier:        item.Details.Tier,
			}

			preview.PossibleItems = append(preview.PossibleItems, previewItem)
		}
	}

	return preview, nil
}

// LootPreview represents a preview of potential loot
type LootPreview struct {
	SpawnID       int32               `json:"spawn_id"`
	TableIDs      []int32             `json:"table_ids"`
	PossibleItems []*LootPreviewItem  `json:"possible_items"`
	MinCoins      int32               `json:"min_coins"`
	MaxCoins      int32               `json:"max_coins"`
}

// LootPreviewItem represents a potential loot item in a preview
type LootPreviewItem struct {
	ItemID      int32   `json:"item_id"`
	ItemName    string  `json:"item_name"`
	Probability float32 `json:"probability"`
	Tier        int8    `json:"tier"`
}