package actions

import (
	"fmt"

	"dk/internal/models/items"
	"dk/internal/models/users"
)

// UserEquipItem calculates equipment updates for a user equipping an item.
// Returns map of database field updates without modifying the user struct.
func UserEquipItem(user *users.User, item *items.Item) (map[string]any, error) {
	if !item.IsEquippable() && !item.IsSlottable() {
		return nil, fmt.Errorf("item %s is not equippable", item.Name)
	}

	updates := make(map[string]any)

	// Calculate stat changes by removing old item and adding new item
	newStats := calculateNewStats(user, item)

	// Update equipment slots and stats based on item type
	switch item.Type {
	case items.TypeWeapon:
		updates["weapon_id"] = item.ID
		updates["weapon_name"] = item.Name

	case items.TypeArmor:
		updates["armor_id"] = item.ID
		updates["armor_name"] = item.Name

	case items.TypeShield:
		updates["shield_id"] = item.ID
		updates["shield_name"] = item.Name

	case items.TypeAccessory:
		// Find first available slot or replace slot 1
		if user.Slot1ID == 0 {
			updates["slot_1_id"] = item.ID
			updates["slot_1_name"] = item.Name
		} else if user.Slot2ID == 0 {
			updates["slot_2_id"] = item.ID
			updates["slot_2_name"] = item.Name
		} else if user.Slot3ID == 0 {
			updates["slot_3_id"] = item.ID
			updates["slot_3_name"] = item.Name
		} else {
			// All slots full, replace slot 1
			updates["slot_1_id"] = item.ID
			updates["slot_1_name"] = item.Name
		}
	}

	// Apply stat changes
	updates["attack"] = newStats.Attack
	updates["defense"] = newStats.Defense
	updates["strength"] = newStats.Strength
	updates["dexterity"] = newStats.Dexterity
	updates["max_hp"] = newStats.MaxHP
	updates["max_mp"] = newStats.MaxMP
	updates["exp_bonus"] = newStats.ExpBonus
	updates["gold_bonus"] = newStats.GoldBonus

	return updates, nil
}

// UserEquipAccessoryToSlot equips an accessory to a specific slot (1-3)
func UserEquipAccessoryToSlot(user *users.User, item *items.Item, slot int) (map[string]any, error) {
	if !item.IsSlottable() {
		return nil, fmt.Errorf("item %s is not slottable", item.Name)
	}

	if slot < 1 || slot > 3 {
		return nil, fmt.Errorf("invalid slot number: %d", slot)
	}

	updates := make(map[string]any)
	newStats := calculateNewStatsForSlot(user, item, slot)

	// Update the specific slot
	switch slot {
	case 1:
		updates["slot_1_id"] = item.ID
		updates["slot_1_name"] = item.Name
	case 2:
		updates["slot_2_id"] = item.ID
		updates["slot_2_name"] = item.Name
	case 3:
		updates["slot_3_id"] = item.ID
		updates["slot_3_name"] = item.Name
	}

	// Apply stat changes
	updates["attack"] = newStats.Attack
	updates["defense"] = newStats.Defense
	updates["strength"] = newStats.Strength
	updates["dexterity"] = newStats.Dexterity
	updates["max_hp"] = newStats.MaxHP
	updates["max_mp"] = newStats.MaxMP
	updates["exp_bonus"] = newStats.ExpBonus
	updates["gold_bonus"] = newStats.GoldBonus

	return updates, nil
}

// UserUnequipItem removes an equipped item and recalculates stats
func UserUnequipItem(user *users.User, itemType int) (map[string]any, error) {
	updates := make(map[string]any)

	switch itemType {
	case items.TypeWeapon:
		if user.WeaponID == 0 {
			return nil, fmt.Errorf("no weapon equipped")
		}
		updates["weapon_id"] = 0
		updates["weapon_name"] = ""

	case items.TypeArmor:
		if user.ArmorID == 0 {
			return nil, fmt.Errorf("no armor equipped")
		}
		updates["armor_id"] = 0
		updates["armor_name"] = ""

	case items.TypeShield:
		if user.ShieldID == 0 {
			return nil, fmt.Errorf("no shield equipped")
		}
		updates["shield_id"] = 0
		updates["shield_name"] = ""

	default:
		return nil, fmt.Errorf("invalid item type for unequip: %d", itemType)
	}

	// Recalculate all stats after unequipping
	newStats := recalculateAllStats(user, itemType, 0)
	updates["attack"] = newStats.Attack
	updates["defense"] = newStats.Defense
	updates["strength"] = newStats.Strength
	updates["dexterity"] = newStats.Dexterity
	updates["max_hp"] = newStats.MaxHP
	updates["max_mp"] = newStats.MaxMP
	updates["exp_bonus"] = newStats.ExpBonus
	updates["gold_bonus"] = newStats.GoldBonus

	return updates, nil
}

// UserUnequipAccessory removes an accessory from a specific slot
func UserUnequipAccessory(user *users.User, slot int) (map[string]any, error) {
	if slot < 1 || slot > 3 {
		return nil, fmt.Errorf("invalid slot number: %d", slot)
	}

	updates := make(map[string]any)

	// Check if slot has an item
	switch slot {
	case 1:
		if user.Slot1ID == 0 {
			return nil, fmt.Errorf("slot 1 is empty")
		}
		updates["slot_1_id"] = 0
		updates["slot_1_name"] = ""
	case 2:
		if user.Slot2ID == 0 {
			return nil, fmt.Errorf("slot 2 is empty")
		}
		updates["slot_2_id"] = 0
		updates["slot_2_name"] = ""
	case 3:
		if user.Slot3ID == 0 {
			return nil, fmt.Errorf("slot 3 is empty")
		}
		updates["slot_3_id"] = 0
		updates["slot_3_name"] = ""
	}

	// Recalculate stats after removing accessory
	newStats := recalculateAllStats(user, items.TypeAccessory, slot)
	updates["attack"] = newStats.Attack
	updates["defense"] = newStats.Defense
	updates["strength"] = newStats.Strength
	updates["dexterity"] = newStats.Dexterity
	updates["max_hp"] = newStats.MaxHP
	updates["max_mp"] = newStats.MaxMP
	updates["exp_bonus"] = newStats.ExpBonus
	updates["gold_bonus"] = newStats.GoldBonus

	return updates, nil
}

// Stats represents calculated user stats
type Stats struct {
	Attack    int
	Defense   int
	Strength  int
	Dexterity int
	MaxHP     int
	MaxMP     int
	ExpBonus  int
	GoldBonus int
}

// calculateNewStats calculates new stats when equipping an item
func calculateNewStats(user *users.User, newItem *items.Item) Stats {
	stats := Stats{
		Attack:    user.Attack,
		Defense:   user.Defense,
		Strength:  user.Strength,
		Dexterity: user.Dexterity,
		MaxHP:     user.MaxHP,
		MaxMP:     user.MaxMP,
		ExpBonus:  user.ExpBonus,
		GoldBonus: user.GoldBonus,
	}

	// Remove old item stats if slot occupied
	switch newItem.Type {
	case items.TypeWeapon:
		if user.WeaponID != 0 {
			if oldItem, err := items.Find(user.WeaponID); err == nil {
				removeItemStats(&stats, oldItem)
			}
		}
	case items.TypeArmor:
		if user.ArmorID != 0 {
			if oldItem, err := items.Find(user.ArmorID); err == nil {
				removeItemStats(&stats, oldItem)
			}
		}
	case items.TypeShield:
		if user.ShieldID != 0 {
			if oldItem, err := items.Find(user.ShieldID); err == nil {
				removeItemStats(&stats, oldItem)
			}
		}
	}

	// Add new item stats
	addItemStats(&stats, newItem)
	return stats
}

// calculateNewStatsForSlot calculates stats when equipping to a specific accessory slot
func calculateNewStatsForSlot(user *users.User, newItem *items.Item, slot int) Stats {
	stats := Stats{
		Attack:    user.Attack,
		Defense:   user.Defense,
		Strength:  user.Strength,
		Dexterity: user.Dexterity,
		MaxHP:     user.MaxHP,
		MaxMP:     user.MaxMP,
		ExpBonus:  user.ExpBonus,
		GoldBonus: user.GoldBonus,
	}

	// Remove old accessory stats from the specified slot
	var oldItemID int
	switch slot {
	case 1:
		oldItemID = user.Slot1ID
	case 2:
		oldItemID = user.Slot2ID
	case 3:
		oldItemID = user.Slot3ID
	}

	if oldItemID != 0 {
		if oldItem, err := items.Find(oldItemID); err == nil {
			removeItemStats(&stats, oldItem)
		}
	}

	// Add new item stats
	addItemStats(&stats, newItem)
	return stats
}

// recalculateAllStats recalculates all stats from scratch, excluding specified item
func recalculateAllStats(user *users.User, excludeType, excludeSlot int) Stats {
	// Start with base stats (these would come from class/level progression)
	// For now, using current stats minus all equipment bonuses
	stats := Stats{
		Attack:    0, // Base attack from strength/level
		Defense:   0, // Base defense from dexterity/level
		Strength:  user.Strength,
		Dexterity: user.Dexterity,
		MaxHP:     user.MaxHP,
		MaxMP:     user.MaxMP,
		ExpBonus:  0, // Base exp bonus
		GoldBonus: 0, // Base gold bonus
	}

	// Add weapon stats (unless being removed)
	if user.WeaponID != 0 && excludeType != items.TypeWeapon {
		if item, err := items.Find(user.WeaponID); err == nil {
			addItemStats(&stats, item)
		}
	}

	// Add armor stats (unless being removed)
	if user.ArmorID != 0 && excludeType != items.TypeArmor {
		if item, err := items.Find(user.ArmorID); err == nil {
			addItemStats(&stats, item)
		}
	}

	// Add shield stats (unless being removed)
	if user.ShieldID != 0 && excludeType != items.TypeShield {
		if item, err := items.Find(user.ShieldID); err == nil {
			addItemStats(&stats, item)
		}
	}

	// Add accessory stats (unless specific slot being removed)
	if user.Slot1ID != 0 && !(excludeType == items.TypeAccessory && excludeSlot == 1) {
		if item, err := items.Find(user.Slot1ID); err == nil {
			addItemStats(&stats, item)
		}
	}
	if user.Slot2ID != 0 && !(excludeType == items.TypeAccessory && excludeSlot == 2) {
		if item, err := items.Find(user.Slot2ID); err == nil {
			addItemStats(&stats, item)
		}
	}
	if user.Slot3ID != 0 && !(excludeType == items.TypeAccessory && excludeSlot == 3) {
		if item, err := items.Find(user.Slot3ID); err == nil {
			addItemStats(&stats, item)
		}
	}

	return stats
}

// addItemStats adds an item's stats to the current stats
func addItemStats(stats *Stats, item *items.Item) {
	stats.Attack += item.Attack
	stats.Defense += item.Defense
	stats.Strength += item.Strength
	stats.Dexterity += item.Dexterity
	stats.MaxHP += item.MaxHP
	stats.MaxMP += item.MaxMP
	stats.ExpBonus += item.ExpBonus
	stats.GoldBonus += item.GoldBonus
}

// removeItemStats removes an item's stats from the current stats
func removeItemStats(stats *Stats, item *items.Item) {
	stats.Attack -= item.Attack
	stats.Defense -= item.Defense
	stats.Strength -= item.Strength
	stats.Dexterity -= item.Dexterity
	stats.MaxHP -= item.MaxHP
	stats.MaxMP -= item.MaxMP
	stats.ExpBonus -= item.ExpBonus
	stats.GoldBonus -= item.GoldBonus
}
