package transmute

import (
	"fmt"
	"math"
	"math/rand"
	"sync"
)

// Transmuter manages the transmutation system
type Transmuter struct {
	tiers          []*TransmutingTier
	activeRequests map[int32]*TransmuteRequest
	itemMaster     ItemMaster
	spellMaster    SpellMaster
	packetBuilder  PacketBuilder
	mutex          sync.RWMutex
	requestMutex   sync.Mutex
}

// SpellMaster represents the spell system interface
type SpellMaster interface {
	GetSpell(spellID int32, tier int32) Spell
}

// NewTransmuter creates a new transmuter instance
func NewTransmuter(itemMaster ItemMaster, spellMaster SpellMaster, packetBuilder PacketBuilder) *Transmuter {
	return &Transmuter{
		tiers:          make([]*TransmutingTier, 0),
		activeRequests: make(map[int32]*TransmuteRequest),
		itemMaster:     itemMaster,
		spellMaster:    spellMaster,
		packetBuilder:  packetBuilder,
	}
}

// LoadTransmutingTiers loads transmuting tiers from database
func (t *Transmuter) LoadTransmutingTiers(database Database) error {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	tiers, err := database.LoadTransmutingTiers()
	if err != nil {
		return fmt.Errorf("failed to load transmuting tiers: %w", err)
	}

	t.tiers = tiers
	return nil
}

// GetTransmutingTiers returns a copy of the transmuting tiers
func (t *Transmuter) GetTransmutingTiers() []*TransmutingTier {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	// Return a copy to prevent external modification
	tiers := make([]*TransmutingTier, len(t.tiers))
	for i, tier := range t.tiers {
		tiers[i] = &TransmutingTier{
			MinLevel:   tier.MinLevel,
			MaxLevel:   tier.MaxLevel,
			FragmentID: tier.FragmentID,
			PowderID:   tier.PowderID,
			InfusionID: tier.InfusionID,
			ManaID:     tier.ManaID,
		}
	}

	return tiers
}

// IsItemTransmutable checks if an item can be transmuted
func (t *Transmuter) IsItemTransmutable(item Item) bool {
	// Item level > 0 AND Item is not LORE_EQUP, LORE, NO_VALUE etc AND item rarity is >= 5
	// (4 is treasured but the rarity used for journeyman spells)
	// Flag 16384 is NO-TRANSMUTE

	disqualifyFlags := NoZone | NoValue | Temporary | NoDestroy | NoTransmute
	disqualifyFlags2 := Ornate

	if item.GetAdventureDefaultLevel() > 0 &&
		(item.GetItemFlags()&disqualifyFlags) == 0 &&
		(item.GetItemFlags2()&disqualifyFlags2) == 0 &&
		item.GetTier() >= ItemTagLegendary &&
		item.GetStackCount() <= 1 {
		return true
	}

	return false
}

// CreateItemRequest creates a new transmutation item selection request
func (t *Transmuter) CreateItemRequest(client Client, player Player) (int32, error) {
	// Generate unique request ID
	var requestID int32
	for {
		// Generate random signed 32-bit integer (excluding 0)
		requestID = rand.Int31()
		if requestID != 0 && rand.Intn(2) == 1 {
			requestID = -requestID // Make it negative sometimes like C++
		}
		if requestID != 0 {
			break
		}
	}

	// Get player's item list
	itemList := player.GetItemList()
	transmutables := make([]int32, 0)

	// Find all transmutable items
	for itemID, item := range itemList {
		if item != nil && t.IsItemTransmutable(item) {
			transmutables = append(transmutables, itemID)
		}
	}

	// Build and send packet
	packet, err := t.packetBuilder.BuildItemRequestPacket(requestID, transmutables, client.GetVersion())
	if err != nil {
		return 0, fmt.Errorf("failed to build item request packet: %w", err)
	}

	client.QueuePacket(packet)
	client.SetTransmuteID(requestID)

	// Store the request
	t.requestMutex.Lock()
	t.activeRequests[requestID] = &TransmuteRequest{
		RequestID: requestID,
		ClientID:  0, // TODO: Get client ID when available
		Phase:     PhaseItemSelection,
	}
	t.requestMutex.Unlock()

	return requestID, nil
}

// HandleItemResponse handles the player's item selection response
func (t *Transmuter) HandleItemResponse(client Client, player Player, requestID int32, itemID int32) error {
	// Find the item
	item := player.GetItemFromUniqueID(itemID)
	if item == nil {
		client.SimpleMessage(ChannelColorRed, "Could not find the item you wish to transmute. Please try again.")
		return fmt.Errorf("item not found: %d", itemID)
	}

	// Verify item is transmutable
	if !t.IsItemTransmutable(item) {
		client.Message(ChannelColorRed, "%s is not transmutable.", item.GetName())
		return fmt.Errorf("item not transmutable: %s", item.GetName())
	}

	// Check transmuting skill requirement
	itemLevel := item.GetAdventureDefaultLevel()
	skill := player.GetSkillByName("Transmuting")

	requiredSkill := int32(math.Max(float64(itemLevel-5), 0) * 5)
	itemStatBonus := player.GetStat(ItemStatTransmuting) // TODO: Define this constant
	currentSkill := int32(0)
	if skill != nil {
		currentSkill = skill.GetCurrentValue() + itemStatBonus
	}

	if skill == nil || currentSkill < requiredSkill {
		client.Message(ChannelColorRed, "You need at least %d Transmuting skill to transmute the %s. You have %d Transmuting skill.",
			requiredSkill, item.GetName(), currentSkill)
		return fmt.Errorf("insufficient transmuting skill: need %d, have %d", requiredSkill, currentSkill)
	}

	// Update request state
	t.requestMutex.Lock()
	if request, exists := t.activeRequests[requestID]; exists {
		request.ItemID = itemID
		request.Phase = PhaseConfirmation
	}
	t.requestMutex.Unlock()

	client.SetTransmuteID(itemID)

	// Send confirmation request
	return t.SendConfirmRequest(client, requestID, item)
}

// SendConfirmRequest sends a confirmation dialog to the client
func (t *Transmuter) SendConfirmRequest(client Client, requestID int32, item Item) error {
	packet, err := t.packetBuilder.BuildConfirmationPacket(requestID, item, client.GetVersion())
	if err != nil {
		client.SimpleMessage(ChannelColorRed, "Struct error for transmutation. Let a dev know.")
		return fmt.Errorf("failed to build confirmation packet: %w", err)
	}

	client.QueuePacket(packet)
	return nil
}

// HandleConfirmResponse handles the player's confirmation response
func (t *Transmuter) HandleConfirmResponse(client Client, player Player, itemID int32) error {
	// Find the item
	item := player.GetItemFromUniqueID(itemID)
	if item == nil {
		client.SimpleMessage(ChannelColorRed, "Item no longer exists!")
		return fmt.Errorf("item no longer exists: %d", itemID)
	}

	client.SetTransmuteID(itemID)

	// Get the zone
	zone := player.GetZone()
	if zone == nil {
		return fmt.Errorf("player not in zone")
	}

	// Get the transmute spell
	spell := t.spellMaster.GetSpell(TransmuteItemSpellID, 1)
	if spell == nil {
		return fmt.Errorf("could not find transmute item spell: %d", TransmuteItemSpellID)
	}

	// Process the spell (this will call CompleteTransmutation when finished)
	return zone.ProcessSpell(spell, player)
}

// CompleteTransmutation completes the transmutation process
func (t *Transmuter) CompleteTransmutation(client Client, player Player) error {
	itemID := client.GetTransmuteID()
	item := player.GetItemFromUniqueID(itemID)
	if item == nil {
		client.SimpleMessage(ChannelColorRed, "Item no longer exists!")
		return fmt.Errorf("item no longer exists: %d", itemID)
	}

	// Determine materials based on item level and tier
	result, err := t.calculateTransmuteResult(item)
	if err != nil {
		client.SimpleMessage(ChannelColorRed, "Could not complete transmutation! Tell a dev!")
		return fmt.Errorf("failed to calculate transmute result: %w", err)
	}

	if !result.Success {
		client.SimpleMessage(ChannelColorRed, result.ErrorMessage)
		return fmt.Errorf("transmutation failed: %s", result.ErrorMessage)
	}

	// Remove the original item
	if !player.RemoveItem(item, true) {
		return fmt.Errorf("failed to remove transmuted item")
	}

	// Send completion message
	client.Message(ChannelYellow, "You transmute %s and create: ", item.CreateItemLink(client.GetVersion(), false))

	// Add the resulting materials
	rewardItems := make([]Item, 0, 2)
	if result.CommonMaterial != nil {
		result.CommonMaterial.SetCount(1)
		client.Message(ChannelYellow, "     %s", result.CommonMaterial.CreateItemLink(client.GetVersion(), false))

		var itemDeleted bool
		if err := client.AddItem(result.CommonMaterial, &itemDeleted); err != nil {
			return fmt.Errorf("failed to add common material: %w", err)
		}
		if !itemDeleted {
			rewardItems = append(rewardItems, result.CommonMaterial)
		}
	}

	if result.RareMaterial != nil {
		result.RareMaterial.SetCount(1)
		client.Message(ChannelYellow, "     %s", result.RareMaterial.CreateItemLink(client.GetVersion(), false))

		var itemDeleted bool
		if err := client.AddItem(result.RareMaterial, &itemDeleted); err != nil {
			return fmt.Errorf("failed to add rare material: %w", err)
		}
		if !itemDeleted {
			rewardItems = append(rewardItems, result.RareMaterial)
		}
	}

	// Send reward packet if there are items
	if len(rewardItems) > 0 {
		packet, err := t.packetBuilder.BuildRewardPacket(rewardItems, client.GetVersion())
		if err == nil {
			client.QueuePacket(packet)
		}
	}

	// Handle skill up
	return t.handleSkillUp(player, item)
}

// calculateTransmuteResult determines what materials are produced from transmutation
func (t *Transmuter) calculateTransmuteResult(item Item) (*TransmuteResult, error) {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	itemLevel := item.GetAdventureDefaultLevel()
	var tier *TransmutingTier

	// Find the correct tier
	for _, t := range t.tiers {
		if t.MinLevel <= itemLevel && t.MaxLevel >= itemLevel {
			tier = t
			break
		}
	}

	if tier == nil {
		return &TransmuteResult{
			Success:      false,
			ErrorMessage: "No transmuting tier found for item level",
		}, nil
	}

	// Determine material types based on item tier
	itemTier := item.GetTier()
	var commonMatID, rareMatID int32

	if itemTier >= ItemTagFabled {
		commonMatID = tier.InfusionID
		rareMatID = tier.ManaID
	} else if itemTier >= ItemTagLegendary {
		commonMatID = tier.PowderID
		rareMatID = tier.InfusionID
	} else {
		commonMatID = tier.FragmentID
		rareMatID = tier.PowderID
	}

	if commonMatID == 0 || rareMatID == 0 {
		return &TransmuteResult{
			Success:      false,
			ErrorMessage: "Invalid material IDs for transmutation",
		}, nil
	}

	// Do the loot roll
	result := &TransmuteResult{Success: true}
	roll := rand.Intn(100) + 1

	if roll <= BothItemsChancePercent {
		// Both items
		result.CommonMaterial = t.itemMaster.CreateItem(commonMatID)
		result.RareMaterial = t.itemMaster.CreateItem(rareMatID)
	} else if roll <= CommonMatChancePercent {
		// Common material only
		result.CommonMaterial = t.itemMaster.CreateItem(commonMatID)
	} else {
		// Rare material only
		result.RareMaterial = t.itemMaster.CreateItem(rareMatID)
	}

	return result, nil
}

// handleSkillUp processes potential skill increases from transmutation
func (t *Transmuter) handleSkillUp(player Player, item Item) error {
	skill := player.GetSkillByName("Transmuting")
	if skill == nil {
		return fmt.Errorf("unable to find transmuting skill for player %s", player.GetName())
	}

	// Calculate skill up chance
	itemLevel := item.GetAdventureDefaultLevel()
	maxTransLevel := skill.GetCurrentValue()/5 + 5
	levelDif := int32(maxTransLevel) - itemLevel

	// No skill up if level difference is too high or skill is maxed
	if levelDif > MaxSkillUpLevelDif || skill.GetCurrentValue() >= skill.GetMaxValue() {
		return nil
	}

	// Calculate skill up probability
	// 50% base chance at max item level, 20% decrease per level difference
	baseChance := float64(SkillUpPercentChanceMax)
	penalty := 0.0
	if itemLevel > 5 {
		penalty = float64(levelDif) * 0.2
	}
	requiredRoll := int32(baseChance * (1.0 - penalty))

	roll := rand.Intn(100) + 1
	if int32(roll) <= requiredRoll {
		return player.IncreaseSkill("Transmuting", 1)
	}

	return nil
}

// CleanupRequest removes a completed or expired request
func (t *Transmuter) CleanupRequest(requestID int32) {
	t.requestMutex.Lock()
	defer t.requestMutex.Unlock()

	delete(t.activeRequests, requestID)
}

// GetActiveRequest returns an active request by ID
func (t *Transmuter) GetActiveRequest(requestID int32) *TransmuteRequest {
	t.requestMutex.Lock()
	defer t.requestMutex.Unlock()

	if request, exists := t.activeRequests[requestID]; exists {
		// Return a copy to prevent external modification
		return &TransmuteRequest{
			RequestID: request.RequestID,
			ClientID:  request.ClientID,
			ItemID:    request.ItemID,
			Phase:     request.Phase,
		}
	}

	return nil
}
