package loot

import (
	"fmt"
	"log"
	"time"

	"eq2emu/internal/items"
)

// ChestInteraction represents the different ways a player can interact with a chest
type ChestInteraction int8

const (
	ChestInteractionView ChestInteraction = iota
	ChestInteractionLoot
	ChestInteractionLootAll
	ChestInteractionDisarm
	ChestInteractionLockpick
	ChestInteractionClose
)

// String returns the string representation of ChestInteraction
func (ci ChestInteraction) String() string {
	switch ci {
	case ChestInteractionView:
		return "view"
	case ChestInteractionLoot:
		return "loot"
	case ChestInteractionLootAll:
		return "loot_all"
	case ChestInteractionDisarm:
		return "disarm"
	case ChestInteractionLockpick:
		return "lockpick"
	case ChestInteractionClose:
		return "close"
	default:
		return "unknown"
	}
}

// ChestInteractionResult represents the result of a chest interaction
type ChestInteractionResult struct {
	Success     bool          `json:"success"`
	Result      int8          `json:"result"`        // ChestResult constant
	Message     string        `json:"message"`       // Message to display to player
	Items       []*items.Item `json:"items"`         // Items received
	Coins       int32         `json:"coins"`         // Coins received
	Experience  int32         `json:"experience"`    // Experience gained (for disarming/lockpicking)
	ChestEmpty  bool          `json:"chest_empty"`   // Whether chest is now empty
	ChestClosed bool          `json:"chest_closed"`  // Whether chest should be closed
}

// ChestService handles treasure chest interactions and management
type ChestService struct {
	lootManager   *LootManager
	playerService PlayerService
	zoneService   ZoneService
}

// PlayerService interface for player-related operations
type PlayerService interface {
	GetPlayerPosition(playerID uint32) (x, y, z, heading float32, zoneID int32, err error)
	IsPlayerInCombat(playerID uint32) bool
	CanPlayerCarryItems(playerID uint32, itemCount int) bool
	AddItemsToPlayer(playerID uint32, items []*items.Item) error
	AddCoinsToPlayer(playerID uint32, coins int32) error
	GetPlayerSkillValue(playerID uint32, skillName string) int32
	AddPlayerExperience(playerID uint32, experience int32, skillName string) error
	SendMessageToPlayer(playerID uint32, message string) error
}

// ZoneService interface for zone-related operations
type ZoneService interface {
	GetZoneRule(zoneID int32, ruleName string) (interface{}, error)
	SpawnObjectInZone(zoneID int32, appearanceID int32, x, y, z, heading float32, name string, commands []string) (int32, error)
	RemoveObjectFromZone(zoneID int32, objectID int32) error
	GetDistanceBetweenPoints(x1, y1, z1, x2, y2, z2 float32) float32
}

// NewChestService creates a new chest service
func NewChestService(lootManager *LootManager, playerService PlayerService, zoneService ZoneService) *ChestService {
	return &ChestService{
		lootManager:   lootManager,
		playerService: playerService,
		zoneService:   zoneService,
	}
}

// CreateTreasureChestFromLoot creates a treasure chest at the specified location with the given loot
func (cs *ChestService) CreateTreasureChestFromLoot(spawnID int32, zoneID int32, x, y, z, heading float32,
	lootResult *LootResult, lootRights []uint32) (*TreasureChest, error) {

	// Check if treasure chests are enabled in this zone
	enabled, err := cs.zoneService.GetZoneRule(zoneID, ConfigTreasureChestEnabled)
	if err != nil {
		log.Printf("%s Failed to check treasure chest rule for zone %d: %v", LogPrefixChest, zoneID, err)
	} else if enabled == false {
		log.Printf("%s Treasure chests disabled in zone %d", LogPrefixChest, zoneID)
		return nil, nil // Not an error, just disabled
	}

	// Don't create chest if no loot
	if lootResult.IsEmpty() {
		log.Printf("%s No loot to put in treasure chest for spawn %d", LogPrefixChest, spawnID)
		return nil, nil
	}

	// Filter items by tier (only common+ items go in chests, matching C++ ITEM_TAG_COMMON)
	filteredItems := make([]*items.Item, 0)
	for _, item := range lootResult.GetItems() {
		if item.Details.Tier >= LootTierCommon {
			filteredItems = append(filteredItems, item)
		}
	}

	// Update loot result with filtered items
	filteredResult := &LootResult{
		Items: filteredItems,
		Coins: lootResult.GetCoins(),
	}

	// Don't create chest if no qualifying items and no coins
	if filteredResult.IsEmpty() {
		log.Printf("%s No qualifying loot for treasure chest (tier >= %d) for spawn %d", 
			LogPrefixChest, LootTierCommon, spawnID)
		return nil, nil
	}

	// Create the chest
	chest, err := cs.lootManager.CreateTreasureChest(spawnID, zoneID, x, y, z, heading, filteredResult, lootRights)
	if err != nil {
		return nil, fmt.Errorf("failed to create treasure chest: %v", err)
	}

	// Spawn the chest object in the zone
	chestCommands := []string{"loot", "disarm"} // TODO: Add "lockpick" if chest is locked
	objectID, err := cs.zoneService.SpawnObjectInZone(zoneID, chest.AppearanceID, x, y, z, heading, 
		"Treasure Chest", chestCommands)
	if err != nil {
		log.Printf("%s Failed to spawn chest object in zone: %v", LogPrefixChest, err)
		// Continue anyway, chest exists in memory
	} else {
		log.Printf("%s Spawned treasure chest object %d in zone %d", LogPrefixChest, objectID, zoneID)
	}

	return chest, nil
}

// HandleChestInteraction processes a player's interaction with a treasure chest
func (cs *ChestService) HandleChestInteraction(chestID int32, playerID uint32, 
	interaction ChestInteraction, itemUniqueID int64) *ChestInteractionResult {

	result := &ChestInteractionResult{
		Success: false,
		Items:   make([]*items.Item, 0),
	}

	// Get the chest
	chest := cs.lootManager.GetTreasureChest(chestID)
	if chest == nil {
		result.Result = ChestResultFailed
		result.Message = "Treasure chest not found"
		return result
	}

	// Basic validation
	if validationResult := cs.validateChestInteraction(chest, playerID); validationResult != nil {
		return validationResult
	}

	// Process the specific interaction
	switch interaction {
	case ChestInteractionView:
		return cs.handleViewChest(chest, playerID)
	case ChestInteractionLoot:
		return cs.handleLootItem(chest, playerID, itemUniqueID)
	case ChestInteractionLootAll:
		return cs.handleLootAll(chest, playerID)
	case ChestInteractionDisarm:
		return cs.handleDisarmChest(chest, playerID)
	case ChestInteractionLockpick:
		return cs.handleLockpickChest(chest, playerID)
	case ChestInteractionClose:
		return cs.handleCloseChest(chest, playerID)
	default:
		result.Result = ChestResultFailed
		result.Message = "Unknown chest interaction"
		return result
	}
}

// validateChestInteraction performs basic validation for chest interactions
func (cs *ChestService) validateChestInteraction(chest *TreasureChest, playerID uint32) *ChestInteractionResult {
	// Check loot rights
	if !chest.HasLootRights(playerID) {
		return &ChestInteractionResult{
			Success: false,
			Result:  ChestResultNoRights,
			Message: "You do not have rights to loot this chest",
		}
	}

	// Check if player is in combat
	if cs.playerService.IsPlayerInCombat(playerID) {
		return &ChestInteractionResult{
			Success: false,
			Result:  ChestResultInCombat,
			Message: "You cannot loot while in combat",
		}
	}

	// Check distance
	px, py, pz, _, pZoneID, err := cs.playerService.GetPlayerPosition(playerID)
	if err != nil {
		return &ChestInteractionResult{
			Success: false,
			Result:  ChestResultFailed,
			Message: "Failed to get player position",
		}
	}

	if pZoneID != chest.ZoneID {
		return &ChestInteractionResult{
			Success: false,
			Result:  ChestResultTooFar,
			Message: "You are too far from the chest",
		}
	}

	distance := cs.zoneService.GetDistanceBetweenPoints(px, py, pz, chest.X, chest.Y, chest.Z)
	if distance > 10.0 { // TODO: Make this configurable
		return &ChestInteractionResult{
			Success: false,
			Result:  ChestResultTooFar,
			Message: "You are too far from the chest",
		}
	}

	// Check if chest is locked
	if chest.IsLocked {
		return &ChestInteractionResult{
			Success: false,
			Result:  ChestResultLocked,
			Message: "The chest is locked",
		}
	}

	// Check if chest is trapped
	if chest.IsDisarmable {
		return &ChestInteractionResult{
			Success: false,
			Result:  ChestResultTrapped,
			Message: "The chest appears to be trapped",
		}
	}

	return nil // Validation passed
}

// handleViewChest handles viewing chest contents
func (cs *ChestService) handleViewChest(chest *TreasureChest, playerID uint32) *ChestInteractionResult {
	if chest.LootResult.IsEmpty() {
		return &ChestInteractionResult{
			Success:    true,
			Result:     ChestResultEmpty,
			Message:    "The chest is empty",
			ChestEmpty: true,
		}
	}

	return &ChestInteractionResult{
		Success: true,
		Result:  ChestResultSuccess,
		Message: fmt.Sprintf("The chest contains %d items and %d coins", 
			len(chest.LootResult.GetItems()), chest.LootResult.GetCoins()),
		Items:   chest.LootResult.GetItems(),
		Coins:   chest.LootResult.GetCoins(),
	}
}

// handleLootItem handles looting a specific item from the chest
func (cs *ChestService) handleLootItem(chest *TreasureChest, playerID uint32, itemUniqueID int64) *ChestInteractionResult {
	// Check if player can carry more items
	if !cs.playerService.CanPlayerCarryItems(playerID, 1) {
		return &ChestInteractionResult{
			Success: false,
			Result:  ChestResultCantCarry,
			Message: "Your inventory is full",
		}
	}

	// Loot the specific item
	item, err := cs.lootManager.LootChestItem(chest.ID, playerID, itemUniqueID)
	if err != nil {
		return &ChestInteractionResult{
			Success: false,
			Result:  ChestResultFailed,
			Message: fmt.Sprintf("Failed to loot item: %v", err),
		}
	}

	// Add item to player's inventory
	if err := cs.playerService.AddItemsToPlayer(playerID, []*items.Item{item}); err != nil {
		log.Printf("%s Failed to add looted item to player %d: %v", LogPrefixChest, playerID, err)
		// TODO: Put item back in chest?
		return &ChestInteractionResult{
			Success: false,
			Result:  ChestResultFailed,
			Message: "Failed to add item to inventory",
		}
	}

	// Send message to player
	message := fmt.Sprintf("You looted %s", item.Name)
	cs.playerService.SendMessageToPlayer(playerID, message)

	return &ChestInteractionResult{
		Success:    true,
		Result:     ChestResultSuccess,
		Message:    message,
		Items:      []*items.Item{item},
		ChestEmpty: cs.lootManager.IsChestEmpty(chest.ID),
	}
}

// handleLootAll handles looting all items and coins from the chest
func (cs *ChestService) handleLootAll(chest *TreasureChest, playerID uint32) *ChestInteractionResult {
	lootResult, err := cs.lootManager.LootChestAll(chest.ID, playerID)
	if err != nil {
		return &ChestInteractionResult{
			Success: false,
			Result:  ChestResultFailed,
			Message: fmt.Sprintf("Failed to loot chest: %v", err),
		}
	}

	if lootResult.IsEmpty() {
		return &ChestInteractionResult{
			Success:    true,
			Result:     ChestResultEmpty,
			Message:    "The chest is empty",
			ChestEmpty: true,
		}
	}

	// Check if player can carry all items
	if !cs.playerService.CanPlayerCarryItems(playerID, len(lootResult.Items)) {
		// TODO: Partial loot or put items back?
		return &ChestInteractionResult{
			Success: false,
			Result:  ChestResultCantCarry,
			Message: "Your inventory is full",
		}
	}

	// Add items to player's inventory
	if len(lootResult.Items) > 0 {
		if err := cs.playerService.AddItemsToPlayer(playerID, lootResult.Items); err != nil {
			log.Printf("%s Failed to add looted items to player %d: %v", LogPrefixChest, playerID, err)
			return &ChestInteractionResult{
				Success: false,
				Result:  ChestResultFailed,
				Message: "Failed to add items to inventory",
			}
		}
	}

	// Add coins to player
	if lootResult.Coins > 0 {
		if err := cs.playerService.AddCoinsToPlayer(playerID, lootResult.Coins); err != nil {
			log.Printf("%s Failed to add looted coins to player %d: %v", LogPrefixChest, playerID, err)
		}
	}

	// Send message to player
	message := fmt.Sprintf("You looted %d items and %d coins", len(lootResult.Items), lootResult.Coins)
	cs.playerService.SendMessageToPlayer(playerID, message)

	return &ChestInteractionResult{
		Success:    true,
		Result:     ChestResultSuccess,
		Message:    message,
		Items:      lootResult.Items,
		Coins:      lootResult.Coins,
		ChestEmpty: true,
	}
}

// handleDisarmChest handles disarming a trapped chest
func (cs *ChestService) handleDisarmChest(chest *TreasureChest, playerID uint32) *ChestInteractionResult {
	if !chest.IsDisarmable {
		return &ChestInteractionResult{
			Success: false,
			Result:  ChestResultFailed,
			Message: "This chest is not trapped",
		}
	}

	// Get player's disarm skill
	disarmSkill := cs.playerService.GetPlayerSkillValue(playerID, "Disarm Trap")
	
	// Calculate success chance (simplified)
	successChance := float32(disarmSkill) - float32(chest.DisarmDifficulty)
	if successChance < 0 {
		successChance = 0
	} else if successChance > 95 {
		successChance = 95
	}

	// Roll for success
	roll := float32(time.Now().UnixNano()%100) // Simple random
	if roll > successChance {
		// Failed disarm - could trigger trap effects here
		return &ChestInteractionResult{
			Success: false,
			Result:  ChestResultFailed,
			Message: "You failed to disarm the trap",
		}
	}

	// Success - disarm the trap
	chest.IsDisarmable = false
	
	// Give experience
	experience := int32(chest.DisarmDifficulty * 10) // 10 exp per difficulty point
	cs.playerService.AddPlayerExperience(playerID, experience, "Disarm Trap")

	message := "You successfully disarmed the trap"
	cs.playerService.SendMessageToPlayer(playerID, message)

	return &ChestInteractionResult{
		Success:    true,
		Result:     ChestResultSuccess,
		Message:    message,
		Experience: experience,
	}
}

// handleLockpickChest handles picking a locked chest
func (cs *ChestService) handleLockpickChest(chest *TreasureChest, playerID uint32) *ChestInteractionResult {
	if !chest.IsLocked {
		return &ChestInteractionResult{
			Success: false,
			Result:  ChestResultFailed,
			Message: "This chest is not locked",
		}
	}

	// Get player's lockpicking skill
	lockpickSkill := cs.playerService.GetPlayerSkillValue(playerID, "Pick Lock")
	
	// Calculate success chance (simplified)
	successChance := float32(lockpickSkill) - float32(chest.LockpickDifficulty)
	if successChance < 0 {
		successChance = 0
	} else if successChance > 95 {
		successChance = 95
	}

	// Roll for success
	roll := float32(time.Now().UnixNano()%100) // Simple random
	if roll > successChance {
		return &ChestInteractionResult{
			Success: false,
			Result:  ChestResultFailed,
			Message: "You failed to pick the lock",
		}
	}

	// Success - unlock the chest
	chest.IsLocked = false
	
	// Give experience
	experience := int32(chest.LockpickDifficulty * 10) // 10 exp per difficulty point
	cs.playerService.AddPlayerExperience(playerID, experience, "Pick Lock")

	message := "You successfully picked the lock"
	cs.playerService.SendMessageToPlayer(playerID, message)

	return &ChestInteractionResult{
		Success:    true,
		Result:     ChestResultSuccess,
		Message:    message,
		Experience: experience,
	}
}

// handleCloseChest handles closing the chest interface
func (cs *ChestService) handleCloseChest(chest *TreasureChest, playerID uint32) *ChestInteractionResult {
	return &ChestInteractionResult{
		Success:     true,
		Result:      ChestResultSuccess,
		Message:     "Closed chest",
		ChestClosed: true,
	}
}

// CleanupEmptyChests removes empty chests from zones
func (cs *ChestService) CleanupEmptyChests(zoneID int32) {
	chests := cs.lootManager.GetZoneChests(zoneID)
	
	for _, chest := range chests {
		if chest.LootResult.IsEmpty() {
			// Remove from zone
			cs.zoneService.RemoveObjectFromZone(zoneID, chest.ID)
			
			// Remove from loot manager
			cs.lootManager.RemoveTreasureChest(chest.ID)
		}
	}
}

// GetPlayerChestList returns a list of chests a player can access
func (cs *ChestService) GetPlayerChestList(playerID uint32) []*TreasureChest {
	return cs.lootManager.GetPlayerChests(playerID)
}