package loot

import (
	"fmt"
	"log"

	"eq2emu/internal/items"
)

// PacketBuilder interface for building loot-related packets
type PacketBuilder interface {
	BuildUpdateLootPacket(chest *TreasureChest, playerID uint32, clientVersion int32) ([]byte, error)
	BuildLootItemPacket(item *items.Item, playerID uint32, clientVersion int32) ([]byte, error)
	BuildStoppedLootingPacket(chestID int32, playerID uint32, clientVersion int32) ([]byte, error)
	BuildLootResponsePacket(result *ChestInteractionResult, clientVersion int32) ([]byte, error)
}

// LootPacketBuilder builds loot-related packets for client communication
type LootPacketBuilder struct {
	itemPacketBuilder ItemPacketBuilder
}

// ItemPacketBuilder interface for building item-related packet data
type ItemPacketBuilder interface {
	BuildItemData(item *items.Item, clientVersion int32) ([]byte, error)
	GetItemAppearanceData(item *items.Item) (int32, int16, int16, int16, int16, int16, int16)
}

// NewLootPacketBuilder creates a new loot packet builder
func NewLootPacketBuilder(itemPacketBuilder ItemPacketBuilder) *LootPacketBuilder {
	return &LootPacketBuilder{
		itemPacketBuilder: itemPacketBuilder,
	}
}

// BuildUpdateLootPacket builds an UpdateLoot packet to show chest contents to a player
func (lpb *LootPacketBuilder) BuildUpdateLootPacket(chest *TreasureChest, playerID uint32, clientVersion int32) ([]byte, error) {
	log.Printf("%s Building UpdateLoot packet for chest %d, player %d, version %d", 
		LogPrefixLoot, chest.ID, playerID, clientVersion)

	// Start with base packet structure
	packet := &LootPacketData{
		PacketType:    "UpdateLoot",
		ChestID:       chest.ID,
		SpawnID:       chest.SpawnID,
		PlayerID:      playerID,
		ClientVersion: clientVersion,
	}

	// Add loot items
	lootItems := chest.LootResult.GetItems()
	packet.ItemCount = int16(len(lootItems))
	packet.Items = make([]*LootItemData, len(lootItems))

	for i, item := range lootItems {
		itemData, err := lpb.buildLootItemData(item, clientVersion)
		if err != nil {
			log.Printf("%s Failed to build item data for item %d: %v", LogPrefixLoot, item.Details.ItemID, err)
			continue
		}
		packet.Items[i] = itemData
	}

	// Add coin information
	packet.Coins = chest.LootResult.GetCoins()

	// Build packet based on client version
	return lpb.buildVersionSpecificLootPacket(packet)
}

// buildLootItemData builds loot item data for a specific item
func (lpb *LootPacketBuilder) buildLootItemData(item *items.Item, clientVersion int32) (*LootItemData, error) {
	// Get item appearance data
	appearanceID, red, green, blue, highlightRed, highlightGreen, highlightBlue := 
		lpb.itemPacketBuilder.GetItemAppearanceData(item)

	return &LootItemData{
		ItemID:      item.Details.ItemID,
		UniqueID:    item.Details.UniqueID,
		Name:        item.Name,
		Count:       item.Details.Count,
		Tier:        item.Details.Tier,
		Icon:        item.Details.Icon,
		AppearanceID: appearanceID,
		Red:         red,
		Green:       green,
		Blue:        blue,
		HighlightRed:   highlightRed,
		HighlightGreen: highlightGreen,
		HighlightBlue:  highlightBlue,
		ItemType:    item.GenericInfo.ItemType,
		NoTrade:     (item.GenericInfo.ItemFlags & uint32(LootFlagNoTrade)) != 0,
		Heirloom:    (item.GenericInfo.ItemFlags & uint32(LootFlagHeirloom)) != 0,
		Lore:        (item.GenericInfo.ItemFlags & uint32(LootFlagLore)) != 0,
	}, nil
}

// buildVersionSpecificLootPacket builds the actual packet bytes based on client version
func (lpb *LootPacketBuilder) buildVersionSpecificLootPacket(packet *LootPacketData) ([]byte, error) {
	switch {
	case packet.ClientVersion >= 60114:
		return lpb.buildLootPacketV60114(packet)
	case packet.ClientVersion >= 1193:
		return lpb.buildLootPacketV1193(packet)
	case packet.ClientVersion >= 546:
		return lpb.buildLootPacketV546(packet)
	case packet.ClientVersion >= 373:
		return lpb.buildLootPacketV373(packet)
	default:
		return lpb.buildLootPacketV1(packet)
	}
}

// buildLootPacketV60114 builds loot packet for client version 60114+
func (lpb *LootPacketBuilder) buildLootPacketV60114(packet *LootPacketData) ([]byte, error) {
	// This is the most recent packet format with all features
	buffer := NewPacketBuffer()
	
	// Packet header
	buffer.WriteInt32(packet.ChestID)
	buffer.WriteInt32(packet.SpawnID)
	buffer.WriteInt16(packet.ItemCount)
	buffer.WriteInt32(packet.Coins)
	
	// Loot options
	buffer.WriteInt8(1) // loot_all_enabled
	buffer.WriteInt8(1) // auto_loot_enabled
	buffer.WriteInt8(0) // loot_timeout (0 = no timeout)
	
	// Item array
	for _, item := range packet.Items {
		if item == nil {
			continue
		}
		
		buffer.WriteInt32(item.ItemID)
		buffer.WriteInt64(item.UniqueID)
		buffer.WriteString(item.Name)
		buffer.WriteInt16(item.Count)
		buffer.WriteInt8(item.Tier)
		buffer.WriteInt16(item.Icon)
		buffer.WriteInt32(item.AppearanceID)
		buffer.WriteInt16(item.Red)
		buffer.WriteInt16(item.Green)
		buffer.WriteInt16(item.Blue)
		buffer.WriteInt16(item.HighlightRed)
		buffer.WriteInt16(item.HighlightGreen)
		buffer.WriteInt16(item.HighlightBlue)
		buffer.WriteInt8(item.ItemType)
		buffer.WriteBool(item.NoTrade)
		buffer.WriteBool(item.Heirloom)
		buffer.WriteBool(item.Lore)
		
		// Extended item data for newer clients
		buffer.WriteInt32(0) // adornment_slot0
		buffer.WriteInt32(0) // adornment_slot1  
		buffer.WriteInt32(0) // adornment_slot2
	}
	
	return buffer.GetBytes(), nil
}

// buildLootPacketV1193 builds loot packet for client version 1193+
func (lpb *LootPacketBuilder) buildLootPacketV1193(packet *LootPacketData) ([]byte, error) {
	buffer := NewPacketBuffer()
	
	buffer.WriteInt32(packet.ChestID)
	buffer.WriteInt32(packet.SpawnID)
	buffer.WriteInt16(packet.ItemCount)
	buffer.WriteInt32(packet.Coins)
	buffer.WriteInt8(1) // loot_all_enabled
	
	for _, item := range packet.Items {
		if item == nil {
			continue
		}
		
		buffer.WriteInt32(item.ItemID)
		buffer.WriteInt64(item.UniqueID)
		buffer.WriteString(item.Name)
		buffer.WriteInt16(item.Count)
		buffer.WriteInt8(item.Tier)
		buffer.WriteInt16(item.Icon)
		buffer.WriteInt32(item.AppearanceID)
		buffer.WriteInt16(item.Red)
		buffer.WriteInt16(item.Green)
		buffer.WriteInt16(item.Blue)
		buffer.WriteInt8(item.ItemType)
		buffer.WriteBool(item.NoTrade)
		buffer.WriteBool(item.Heirloom)
	}
	
	return buffer.GetBytes(), nil
}

// buildLootPacketV546 builds loot packet for client version 546+
func (lpb *LootPacketBuilder) buildLootPacketV546(packet *LootPacketData) ([]byte, error) {
	buffer := NewPacketBuffer()
	
	buffer.WriteInt32(packet.ChestID)
	buffer.WriteInt32(packet.SpawnID) 
	buffer.WriteInt16(packet.ItemCount)
	buffer.WriteInt32(packet.Coins)
	
	for _, item := range packet.Items {
		if item == nil {
			continue
		}
		
		buffer.WriteInt32(item.ItemID)
		buffer.WriteInt64(item.UniqueID)
		buffer.WriteString(item.Name)
		buffer.WriteInt16(item.Count)
		buffer.WriteInt8(item.Tier)
		buffer.WriteInt16(item.Icon)
		buffer.WriteInt8(item.ItemType)
		buffer.WriteBool(item.NoTrade)
	}
	
	return buffer.GetBytes(), nil
}

// buildLootPacketV373 builds loot packet for client version 373+
func (lpb *LootPacketBuilder) buildLootPacketV373(packet *LootPacketData) ([]byte, error) {
	buffer := NewPacketBuffer()
	
	buffer.WriteInt32(packet.ChestID)
	buffer.WriteInt16(packet.ItemCount)
	buffer.WriteInt32(packet.Coins)
	
	for _, item := range packet.Items {
		if item == nil {
			continue
		}
		
		buffer.WriteInt32(item.ItemID)
		buffer.WriteString(item.Name)
		buffer.WriteInt16(item.Count)  
		buffer.WriteInt16(item.Icon)
		buffer.WriteInt8(item.ItemType)
	}
	
	return buffer.GetBytes(), nil
}

// buildLootPacketV1 builds loot packet for client version 1 (oldest)
func (lpb *LootPacketBuilder) buildLootPacketV1(packet *LootPacketData) ([]byte, error) {
	buffer := NewPacketBuffer()
	
	buffer.WriteInt32(packet.ChestID)
	buffer.WriteInt16(packet.ItemCount)
	
	for _, item := range packet.Items {
		if item == nil {
			continue
		}
		
		buffer.WriteInt32(item.ItemID)
		buffer.WriteString(item.Name)
		buffer.WriteInt16(item.Count)
	}
	
	return buffer.GetBytes(), nil
}

// BuildLootItemPacket builds a packet for when a player loots a specific item
func (lpb *LootPacketBuilder) BuildLootItemPacket(item *items.Item, playerID uint32, clientVersion int32) ([]byte, error) {
	log.Printf("%s Building LootItem packet for item %d, player %d", LogPrefixLoot, item.Details.ItemID, playerID)
	
	buffer := NewPacketBuffer()
	
	// Basic loot item response
	buffer.WriteInt32(item.Details.ItemID)
	buffer.WriteInt64(item.Details.UniqueID)
	buffer.WriteString(item.Name)
	buffer.WriteInt16(item.Details.Count)
	buffer.WriteInt8(1) // success flag
	
	return buffer.GetBytes(), nil
}

// BuildStoppedLootingPacket builds a packet when player stops looting
func (lpb *LootPacketBuilder) BuildStoppedLootingPacket(chestID int32, playerID uint32, clientVersion int32) ([]byte, error) {
	log.Printf("%s Building StoppedLooting packet for chest %d, player %d", LogPrefixLoot, chestID, playerID)
	
	buffer := NewPacketBuffer()
	buffer.WriteInt32(chestID)
	
	return buffer.GetBytes(), nil
}

// BuildLootResponsePacket builds a response packet for chest interactions
func (lpb *LootPacketBuilder) BuildLootResponsePacket(result *ChestInteractionResult, clientVersion int32) ([]byte, error) {
	buffer := NewPacketBuffer()
	
	// Result code and message
	buffer.WriteInt8(result.Result)
	buffer.WriteBool(result.Success)
	buffer.WriteString(result.Message)
	
	// Items received
	buffer.WriteInt16(int16(len(result.Items)))
	for _, item := range result.Items {
		buffer.WriteInt32(item.Details.ItemID)
		buffer.WriteString(item.Name)
		buffer.WriteInt16(item.Details.Count)
	}
	
	// Coins received
	buffer.WriteInt32(result.Coins)
	
	// Experience gained
	buffer.WriteInt32(result.Experience)
	
	// Status flags
	buffer.WriteBool(result.ChestEmpty)
	buffer.WriteBool(result.ChestClosed)
	
	return buffer.GetBytes(), nil
}

// LootPacketData represents the data structure for loot packets
type LootPacketData struct {
	PacketType    string
	ChestID       int32
	SpawnID       int32
	PlayerID      uint32
	ClientVersion int32
	ItemCount     int16
	Items         []*LootItemData
	Coins         int32
}

// LootItemData represents an item in a loot packet
type LootItemData struct {
	ItemID         int32
	UniqueID       int64
	Name           string
	Count          int16
	Tier           int8
	Icon           int16
	AppearanceID   int32
	Red            int16
	Green          int16
	Blue           int16
	HighlightRed   int16
	HighlightGreen int16
	HighlightBlue  int16
	ItemType       int8
	NoTrade        bool
	Heirloom       bool
	Lore           bool
}

// PacketBuffer is a simple buffer for building packet data
type PacketBuffer struct {
	data []byte
}

// NewPacketBuffer creates a new packet buffer
func NewPacketBuffer() *PacketBuffer {
	return &PacketBuffer{
		data: make([]byte, 0, 1024),
	}
}

// WriteInt8 writes an 8-bit integer
func (pb *PacketBuffer) WriteInt8(value int8) {
	pb.data = append(pb.data, byte(value))
}

// WriteInt16 writes a 16-bit integer
func (pb *PacketBuffer) WriteInt16(value int16) {
	pb.data = append(pb.data, byte(value), byte(value>>8))
}

// WriteInt32 writes a 32-bit integer
func (pb *PacketBuffer) WriteInt32(value int32) {
	pb.data = append(pb.data, 
		byte(value), byte(value>>8), byte(value>>16), byte(value>>24))
}

// WriteInt64 writes a 64-bit integer
func (pb *PacketBuffer) WriteInt64(value int64) {
	pb.data = append(pb.data,
		byte(value), byte(value>>8), byte(value>>16), byte(value>>24),
		byte(value>>32), byte(value>>40), byte(value>>48), byte(value>>56))
}

// WriteBool writes a boolean as a single byte
func (pb *PacketBuffer) WriteBool(value bool) {
	if value {
		pb.data = append(pb.data, 1)
	} else {
		pb.data = append(pb.data, 0)
	}
}

// WriteString writes a null-terminated string
func (pb *PacketBuffer) WriteString(value string) {
	pb.data = append(pb.data, []byte(value)...)
	pb.data = append(pb.data, 0) // null terminator
}

// GetBytes returns the current buffer data
func (pb *PacketBuffer) GetBytes() []byte {
	return pb.data
}

// LootPacketService provides high-level packet building services
type LootPacketService struct {
	packetBuilder *LootPacketBuilder
	clientService ClientService
}

// ClientService interface for client-related operations
type ClientService interface {
	GetClientVersion(playerID uint32) int32
	SendPacketToPlayer(playerID uint32, packetType string, data []byte) error
}

// NewLootPacketService creates a new loot packet service
func NewLootPacketService(packetBuilder *LootPacketBuilder, clientService ClientService) *LootPacketService {
	return &LootPacketService{
		packetBuilder: packetBuilder,
		clientService: clientService,
	}
}

// SendLootUpdate sends a loot update packet to a player
func (lps *LootPacketService) SendLootUpdate(chest *TreasureChest, playerID uint32) error {
	clientVersion := lps.clientService.GetClientVersion(playerID)
	
	packet, err := lps.packetBuilder.BuildUpdateLootPacket(chest, playerID, clientVersion)
	if err != nil {
		return fmt.Errorf("failed to build loot update packet: %v", err)
	}
	
	return lps.clientService.SendPacketToPlayer(playerID, "UpdateLoot", packet)
}

// SendLootResponse sends a loot interaction response to a player
func (lps *LootPacketService) SendLootResponse(result *ChestInteractionResult, playerID uint32) error {
	clientVersion := lps.clientService.GetClientVersion(playerID)
	
	packet, err := lps.packetBuilder.BuildLootResponsePacket(result, clientVersion)
	if err != nil {
		return fmt.Errorf("failed to build loot response packet: %v", err)
	}
	
	return lps.clientService.SendPacketToPlayer(playerID, "LootResponse", packet)
}

// SendStoppedLooting sends a stopped looting packet to a player
func (lps *LootPacketService) SendStoppedLooting(chestID int32, playerID uint32) error {
	clientVersion := lps.clientService.GetClientVersion(playerID)
	
	packet, err := lps.packetBuilder.BuildStoppedLootingPacket(chestID, playerID, clientVersion)
	if err != nil {
		return fmt.Errorf("failed to build stopped looting packet: %v", err)
	}
	
	return lps.clientService.SendPacketToPlayer(playerID, "StoppedLooting", packet)
}