package items

import (
	"fmt"
	"log"
	"sync"
	"time"
)

// SpellManager defines the interface for spell-related operations needed by items
type SpellManager interface {
	// GetSpell retrieves spell information by ID and tier
	GetSpell(spellID uint32, tier int8) (Spell, error)

	// GetSpellsBySkill gets spells associated with a skill
	GetSpellsBySkill(skillID uint32) ([]uint32, error)

	// ValidateSpellID checks if a spell ID is valid
	ValidateSpellID(spellID uint32) bool
}

// PlayerManager defines the interface for player-related operations needed by items
type PlayerManager interface {
	// GetPlayer retrieves player information by ID
	GetPlayer(playerID uint32) (Player, error)

	// GetPlayerLevel gets a player's current level
	GetPlayerLevel(playerID uint32) (int16, error)

	// GetPlayerClass gets a player's adventure class
	GetPlayerClass(playerID uint32) (int8, error)

	// GetPlayerRace gets a player's race
	GetPlayerRace(playerID uint32) (int8, error)

	// SendMessageToPlayer sends a message to a player
	SendMessageToPlayer(playerID uint32, channel int8, message string) error

	// GetPlayerName gets a player's name
	GetPlayerName(playerID uint32) (string, error)
}

// PacketManager defines the interface for packet-related operations
type PacketManager interface {
	// SendPacketToPlayer sends a packet to a specific player
	SendPacketToPlayer(playerID uint32, packetData []byte) error

	// QueuePacketForPlayer queues a packet for delayed sending
	QueuePacketForPlayer(playerID uint32, packetData []byte) error

	// GetClientVersion gets the client version for a player
	GetClientVersion(playerID uint32) (int16, error)

	// SerializeItem serializes an item for network transmission
	SerializeItem(item *Item, clientVersion int16, player Player) ([]byte, error)
}

// RuleManager defines the interface for rules/configuration access
type RuleManager interface {
	// GetBool retrieves a boolean rule value
	GetBool(category, rule string) bool

	// GetInt32 retrieves an int32 rule value
	GetInt32(category, rule string) int32

	// GetFloat retrieves a float rule value
	GetFloat(category, rule string) float32

	// GetString retrieves a string rule value
	GetString(category, rule string) string
}

// DatabaseService defines the interface for item persistence operations
type DatabaseService interface {
	// LoadItems loads all item templates from the database
	LoadItems(masterList *MasterItemList) error

	// SaveItem saves an item template to the database
	SaveItem(item *Item) error

	// DeleteItem removes an item template from the database
	DeleteItem(itemID int32) error

	// LoadPlayerItems loads a player's inventory from the database
	LoadPlayerItems(playerID uint32) (*PlayerItemList, error)

	// SavePlayerItems saves a player's inventory to the database
	SavePlayerItems(playerID uint32, itemList *PlayerItemList) error

	// LoadPlayerEquipment loads a player's equipment from the database
	LoadPlayerEquipment(playerID uint32, appearanceType int8) (*EquipmentItemList, error)

	// SavePlayerEquipment saves a player's equipment to the database
	SavePlayerEquipment(playerID uint32, equipment *EquipmentItemList) error

	// LoadItemStats loads item stat mappings from the database
	LoadItemStats() (map[string]int32, map[int32]string, error)

	// SaveItemStat saves an item stat mapping to the database
	SaveItemStat(statID int32, statName string) error
}

// QuestManager defines the interface for quest-related item operations
type QuestManager interface {
	// CheckQuestPrerequisites checks if a player meets quest prerequisites for an item
	CheckQuestPrerequisites(playerID uint32, questID int32) bool

	// GetQuestRewards gets quest rewards for an item
	GetQuestRewards(questID int32) ([]*QuestRewardData, error)

	// IsQuestItem checks if an item is a quest item
	IsQuestItem(itemID int32) bool
}

// BrokerManager defines the interface for broker/marketplace operations
type BrokerManager interface {
	// SearchItems searches for items on the broker
	SearchItems(criteria *ItemSearchCriteria) ([]*Item, error)

	// ListItem lists an item on the broker
	ListItem(playerID uint32, item *Item, price int64) error

	// BuyItem purchases an item from the broker
	BuyItem(playerID uint32, itemID int32, sellerID uint32) error

	// GetItemPrice gets the current market price for an item
	GetItemPrice(itemID int32) (int64, error)
}

// CraftingManager defines the interface for crafting-related item operations
type CraftingManager interface {
	// CanCraftItem checks if a player can craft an item
	CanCraftItem(playerID uint32, itemID int32) bool

	// GetCraftingRequirements gets crafting requirements for an item
	GetCraftingRequirements(itemID int32) ([]CraftingRequirement, error)

	// CraftItem handles item crafting
	CraftItem(playerID uint32, itemID int32, quality int8) (*Item, error)
}

// HousingManager defines the interface for housing-related item operations
type HousingManager interface {
	// CanPlaceItem checks if an item can be placed in a house
	CanPlaceItem(playerID uint32, houseID int32, item *Item) bool

	// PlaceItem places an item in a house
	PlaceItem(playerID uint32, houseID int32, item *Item, location HouseLocation) error

	// RemoveItem removes an item from a house
	RemoveItem(playerID uint32, houseID int32, itemID int32) error

	// GetHouseItems gets all items in a house
	GetHouseItems(houseID int32) ([]*Item, error)
}

// LootManager defines the interface for loot-related operations
type LootManager interface {
	// GenerateLoot generates loot for a loot table
	GenerateLoot(lootTableID int32, playerLevel int16) ([]*Item, error)

	// DistributeLoot distributes loot to players
	DistributeLoot(items []*Item, playerIDs []uint32, lootMethod int8) error

	// CanLootItem checks if a player can loot an item
	CanLootItem(playerID uint32, item *Item) bool
}

// Data structures used by the interfaces

// Spell represents a spell in the game
type Spell interface {
	GetID() uint32
	GetName() string
	GetIcon() uint32
	GetIconBackdrop() uint32
	GetTier() int8
	GetDescription() string
}

// Player represents a player in the game
type Player interface {
	GetID() uint32
	GetName() string
	GetLevel() int16
	GetAdventureClass() int8
	GetTradeskillClass() int8
	GetRace() int8
	GetGender() int8
	GetAlignment() int8
}

// Entity represents an entity (player or NPC) that can have items
type Entity interface {
	GetID() uint32
	GetName() string
	GetLevel() int16
	GetRace() int8
	GetGender() int8
	GetAlignment() int8
	IsPlayer() bool
	IsNPC() bool
	// GetStatValueByName gets a stat value by name for item calculations
	GetStatValueByName(statName string) float64
	// GetSkillValueByName gets a skill value by name for item calculations
	GetSkillValueByName(skillName string) int32
}

// CraftingRequirement represents a crafting requirement
type CraftingRequirement struct {
	ItemID   int32 `json:"item_id"`
	Quantity int16 `json:"quantity"`
	Skill    int32 `json:"skill"`
	Level    int16 `json:"level"`
}

// HouseLocation represents a location within a house
type HouseLocation struct {
	X        float32 `json:"x"`
	Y        float32 `json:"y"`
	Z        float32 `json:"z"`
	Heading  float32 `json:"heading"`
	Pitch    float32 `json:"pitch"`
	Roll     float32 `json:"roll"`
	Location int8    `json:"location"` // 0=floor, 1=ceiling, 2=wall
}

// ItemSystemAdapter provides a high-level interface to the complete item system
type ItemSystemAdapter struct {
	masterList      *MasterItemList
	playerLists     map[uint32]*PlayerItemList
	equipmentLists  map[uint32]*EquipmentItemList
	spellManager    SpellManager
	playerManager   PlayerManager
	packetManager   PacketManager
	ruleManager     RuleManager
	databaseService DatabaseService
	questManager    QuestManager
	brokerManager   BrokerManager
	craftingManager CraftingManager
	housingManager  HousingManager
	lootManager     LootManager
	mutex           sync.RWMutex
}

// NewItemSystemAdapter creates a new item system adapter with all dependencies
func NewItemSystemAdapter(
	masterList *MasterItemList,
	spellManager SpellManager,
	playerManager PlayerManager,
	packetManager PacketManager,
	ruleManager RuleManager,
	databaseService DatabaseService,
	questManager QuestManager,
	brokerManager BrokerManager,
	craftingManager CraftingManager,
	housingManager HousingManager,
	lootManager LootManager,
) *ItemSystemAdapter {
	return &ItemSystemAdapter{
		masterList:      masterList,
		playerLists:     make(map[uint32]*PlayerItemList),
		equipmentLists:  make(map[uint32]*EquipmentItemList),
		spellManager:    spellManager,
		playerManager:   playerManager,
		packetManager:   packetManager,
		ruleManager:     ruleManager,
		databaseService: databaseService,
		questManager:    questManager,
		brokerManager:   brokerManager,
		craftingManager: craftingManager,
		housingManager:  housingManager,
		lootManager:     lootManager,
	}
}

// Initialize sets up the item system (loads items from database, etc.)
func (isa *ItemSystemAdapter) Initialize() error {
	// Load items from database
	err := isa.databaseService.LoadItems(isa.masterList)
	if err != nil {
		return err
	}

	// Load item stat mappings
	statsStrings, statsIDs, err := isa.databaseService.LoadItemStats()
	if err != nil {
		return err
	}

	isa.masterList.mutex.Lock()
	isa.masterList.mappedItemStatsStrings = statsStrings
	isa.masterList.mappedItemStatTypeIDs = statsIDs
	isa.masterList.mutex.Unlock()

	return nil
}

// GetPlayerInventory gets or loads a player's inventory
func (isa *ItemSystemAdapter) GetPlayerInventory(playerID uint32) (*PlayerItemList, error) {
	isa.mutex.Lock()
	defer isa.mutex.Unlock()

	if itemList, exists := isa.playerLists[playerID]; exists {
		return itemList, nil
	}

	// Load from database
	itemList, err := isa.databaseService.LoadPlayerItems(playerID)
	if err != nil {
		return nil, err
	}

	if itemList == nil {
		itemList = NewPlayerItemList()
	}

	isa.playerLists[playerID] = itemList
	return itemList, nil
}

// GetPlayerEquipment gets or loads a player's equipment
func (isa *ItemSystemAdapter) GetPlayerEquipment(playerID uint32, appearanceType int8) (*EquipmentItemList, error) {
	isa.mutex.Lock()
	defer isa.mutex.Unlock()

	key := uint32(playerID)*10 + uint32(appearanceType)
	if equipment, exists := isa.equipmentLists[key]; exists {
		return equipment, nil
	}

	// Load from database
	equipment, err := isa.databaseService.LoadPlayerEquipment(playerID, appearanceType)
	if err != nil {
		return nil, err
	}

	if equipment == nil {
		equipment = NewEquipmentItemList()
		equipment.SetAppearanceType(appearanceType)
	}

	isa.equipmentLists[key] = equipment
	return equipment, nil
}

// SavePlayerData saves a player's item data
func (isa *ItemSystemAdapter) SavePlayerData(playerID uint32) error {
	isa.mutex.RLock()
	defer isa.mutex.RUnlock()

	// Save inventory
	if itemList, exists := isa.playerLists[playerID]; exists {
		err := isa.databaseService.SavePlayerItems(playerID, itemList)
		if err != nil {
			return err
		}
	}

	// Save equipment (both normal and appearance)
	for key, equipment := range isa.equipmentLists {
		if key/10 == playerID {
			err := isa.databaseService.SavePlayerEquipment(playerID, equipment)
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// GiveItemToPlayer gives an item to a player
func (isa *ItemSystemAdapter) GiveItemToPlayer(playerID uint32, itemID int32, quantity int16, addType AddItemType) error {
	// Get item template
	itemTemplate := isa.masterList.GetItem(itemID)
	if itemTemplate == nil {
		return ErrItemNotFound
	}

	// Create item instance
	item := NewItemFromTemplate(itemTemplate)
	item.Details.Count = quantity

	// Get player inventory
	inventory, err := isa.GetPlayerInventory(playerID)
	if err != nil {
		return err
	}

	// Try to add item to inventory
	if !inventory.AddItem(item) {
		return ErrInsufficientSpace
	}

	// Send update to player
	player, err := isa.playerManager.GetPlayer(playerID)
	if err != nil {
		return err
	}

	clientVersion, _ := isa.packetManager.GetClientVersion(playerID)
	packetData, err := isa.packetManager.SerializeItem(item, clientVersion, player)
	if err != nil {
		return err
	}

	return isa.packetManager.SendPacketToPlayer(playerID, packetData)
}

// RemoveItemFromPlayer removes an item from a player
func (isa *ItemSystemAdapter) RemoveItemFromPlayer(playerID uint32, uniqueID int32, quantity int16) error {
	inventory, err := isa.GetPlayerInventory(playerID)
	if err != nil {
		return err
	}

	item := inventory.GetItemFromUniqueID(uniqueID, true, true)
	if item == nil {
		return ErrItemNotFound
	}

	// Check if item can be removed
	if item.IsItemLocked() {
		return ErrItemLocked
	}

	if item.Details.Count <= quantity {
		// Remove entire stack
		inventory.RemoveItem(item, true, true)
	} else {
		// Reduce quantity
		item.Details.Count -= quantity
	}

	return nil
}

// EquipItem equips an item for a player
func (isa *ItemSystemAdapter) EquipItem(playerID uint32, uniqueID int32, slot int8, appearanceType int8) error {
	inventory, err := isa.GetPlayerInventory(playerID)
	if err != nil {
		return err
	}

	equipment, err := isa.GetPlayerEquipment(playerID, appearanceType)
	if err != nil {
		return err
	}

	// Get item from inventory
	item := inventory.GetItemFromUniqueID(uniqueID, false, true)
	if item == nil {
		return ErrItemNotFound
	}

	// Check if item can be equipped
	if !equipment.CanItemBeEquippedInSlot(item, slot) {
		return ErrCannotEquip
	}

	// Check class/race/level requirements
	player, err := isa.playerManager.GetPlayer(playerID)
	if err != nil {
		return err
	}

	if !item.CheckClass(player.GetAdventureClass(), player.GetTradeskillClass()) {
		return ErrCannotEquip
	}

	if !item.CheckClassLevel(player.GetAdventureClass(), player.GetTradeskillClass(), player.GetLevel()) {
		return ErrCannotEquip
	}

	// Remove from inventory
	inventory.RemoveItem(item, false, true)

	// Check if slot is occupied and unequip current item
	currentItem := equipment.GetItem(slot)
	if currentItem != nil {
		equipment.RemoveItem(slot, false)
		inventory.AddItem(currentItem)
	}

	// Equip new item
	equipment.SetItem(slot, item, false)

	return nil
}

// UnequipItem unequips an item for a player
func (isa *ItemSystemAdapter) UnequipItem(playerID uint32, slot int8, appearanceType int8) error {
	inventory, err := isa.GetPlayerInventory(playerID)
	if err != nil {
		return err
	}

	equipment, err := isa.GetPlayerEquipment(playerID, appearanceType)
	if err != nil {
		return err
	}

	// Get equipped item
	item := equipment.GetItem(slot)
	if item == nil {
		return ErrItemNotFound
	}

	// Check if item can be unequipped
	if item.IsItemLocked() {
		return ErrItemLocked
	}

	// Remove from equipment
	equipment.RemoveItem(slot, false)

	// Add to inventory
	if !inventory.AddItem(item) {
		// Inventory full, add to overflow
		inventory.AddOverflowItem(item)
	}

	return nil
}

// MoveItem moves an item within a player's inventory
func (isa *ItemSystemAdapter) MoveItem(playerID uint32, fromBagID int32, fromSlot int16, toBagID int32, toSlot int16, appearanceType int8) error {
	inventory, err := isa.GetPlayerInventory(playerID)
	if err != nil {
		return err
	}

	// Get item from source location
	item := inventory.GetItem(fromBagID, fromSlot, appearanceType)
	if item == nil {
		return ErrItemNotFound
	}

	// Check if item is locked
	if item.IsItemLocked() {
		return ErrItemLocked
	}

	// Move item
	inventory.MoveItem(item, toBagID, toSlot, appearanceType, true)

	return nil
}

// SearchBrokerItems searches for items on the broker
func (isa *ItemSystemAdapter) SearchBrokerItems(criteria *ItemSearchCriteria) ([]*Item, error) {
	if isa.brokerManager == nil {
		return nil, fmt.Errorf("broker manager not available")
	}

	return isa.brokerManager.SearchItems(criteria)
}

// CraftItem handles item crafting
func (isa *ItemSystemAdapter) CraftItem(playerID uint32, itemID int32, quality int8) (*Item, error) {
	if isa.craftingManager == nil {
		return nil, fmt.Errorf("crafting manager not available")
	}

	// Check if player can craft the item
	if !isa.craftingManager.CanCraftItem(playerID, itemID) {
		return nil, fmt.Errorf("player cannot craft this item")
	}

	// Craft the item
	return isa.craftingManager.CraftItem(playerID, itemID, quality)
}

// GetPlayerItemStats returns statistics about a player's items
func (isa *ItemSystemAdapter) GetPlayerItemStats(playerID uint32) (map[string]interface{}, error) {
	inventory, err := isa.GetPlayerInventory(playerID)
	if err != nil {
		return nil, err
	}

	equipment, err := isa.GetPlayerEquipment(playerID, BaseEquipment)
	if err != nil {
		return nil, err
	}

	// Calculate equipment bonuses
	bonuses := equipment.CalculateEquipmentBonuses()

	return map[string]interface{}{
		"player_id":        playerID,
		"total_items":      inventory.GetNumberOfItems(),
		"equipped_items":   equipment.GetNumberOfItems(),
		"inventory_weight": inventory.GetWeight(),
		"equipment_weight": equipment.GetWeight(),
		"free_slots":       inventory.GetNumberOfFreeSlots(),
		"overflow_items":   len(inventory.GetOverflowItemList()),
		"stat_bonuses":     bonuses,
		"last_update":      time.Now(),
	}, nil
}

// GetSystemStats returns comprehensive statistics about the item system
func (isa *ItemSystemAdapter) GetSystemStats() map[string]interface{} {
	isa.mutex.RLock()
	defer isa.mutex.RUnlock()

	masterStats := isa.masterList.GetStats()

	return map[string]interface{}{
		"total_item_templates": masterStats.TotalItems,
		"items_by_type":        masterStats.ItemsByType,
		"items_by_tier":        masterStats.ItemsByTier,
		"active_players":       len(isa.playerLists),
		"cached_inventories":   len(isa.playerLists),
		"cached_equipment":     len(isa.equipmentLists),
		"last_update":          time.Now(),
	}
}

// ClearPlayerData removes cached data for a player (e.g., when they log out)
func (isa *ItemSystemAdapter) ClearPlayerData(playerID uint32) {
	isa.mutex.Lock()
	defer isa.mutex.Unlock()

	// Remove inventory
	delete(isa.playerLists, playerID)

	// Remove equipment
	keysToDelete := make([]uint32, 0)
	for key := range isa.equipmentLists {
		if key/10 == playerID {
			keysToDelete = append(keysToDelete, key)
		}
	}

	for _, key := range keysToDelete {
		delete(isa.equipmentLists, key)
	}
}

// ValidatePlayerItems validates all items for a player
func (isa *ItemSystemAdapter) ValidatePlayerItems(playerID uint32) *ItemValidationResult {
	result := &ItemValidationResult{Valid: true}

	// Validate inventory
	inventory, err := isa.GetPlayerInventory(playerID)
	if err != nil {
		result.Valid = false
		result.Errors = append(result.Errors, fmt.Sprintf("Failed to load inventory: %v", err))
		return result
	}

	allItems := inventory.GetAllItems()
	for index, item := range allItems {
		itemResult := item.Validate()
		if !itemResult.Valid {
			result.Valid = false
			for _, itemErr := range itemResult.Errors {
				result.Errors = append(result.Errors, fmt.Sprintf("Inventory item %d: %s", index, itemErr))
			}
		}
	}

	// Validate equipment
	equipment, err := isa.GetPlayerEquipment(playerID, BaseEquipment)
	if err != nil {
		result.Valid = false
		result.Errors = append(result.Errors, fmt.Sprintf("Failed to load equipment: %v", err))
		return result
	}

	equipResult := equipment.ValidateEquipment()
	if !equipResult.Valid {
		result.Valid = false
		result.Errors = append(result.Errors, equipResult.Errors...)
	}

	return result
}

// MockImplementations for testing

// MockSpellManager is a mock implementation of SpellManager for testing
type MockSpellManager struct {
	spells map[uint32]MockSpell
}

// MockSpell is a mock implementation of Spell for testing
type MockSpell struct {
	id           uint32
	name         string
	icon         uint32
	iconBackdrop uint32
	tier         int8
	description  string
}

func (ms MockSpell) GetID() uint32           { return ms.id }
func (ms MockSpell) GetName() string         { return ms.name }
func (ms MockSpell) GetIcon() uint32         { return ms.icon }
func (ms MockSpell) GetIconBackdrop() uint32 { return ms.iconBackdrop }
func (ms MockSpell) GetTier() int8           { return ms.tier }
func (ms MockSpell) GetDescription() string  { return ms.description }

func (msm *MockSpellManager) GetSpell(spellID uint32, tier int8) (Spell, error) {
	if spell, exists := msm.spells[spellID]; exists {
		return spell, nil
	}
	return nil, fmt.Errorf("spell not found: %d", spellID)
}

func (msm *MockSpellManager) GetSpellsBySkill(skillID uint32) ([]uint32, error) {
	return []uint32{}, nil
}

func (msm *MockSpellManager) ValidateSpellID(spellID uint32) bool {
	_, exists := msm.spells[spellID]
	return exists
}

// NewMockSpellManager creates a new mock spell manager
func NewMockSpellManager() *MockSpellManager {
	return &MockSpellManager{
		spells: make(map[uint32]MockSpell),
	}
}

// AddMockSpell adds a mock spell for testing
func (msm *MockSpellManager) AddMockSpell(id uint32, name string, icon uint32, tier int8, description string) {
	msm.spells[id] = MockSpell{
		id:           id,
		name:         name,
		icon:         icon,
		iconBackdrop: icon + 1000,
		tier:         tier,
		description:  description,
	}
}

func init() {
	log.Printf("Item system interfaces initialized")
}
