package trade

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

// Trade represents an active trade between two entities
// Converted from C++ Trade class
type Trade struct {
	// Core trade participants
	trader1 *TradeParticipant // First trader (initiator)
	trader2 *TradeParticipant // Second trader (recipient)

	// Trade state
	state     TradeState // Current state of the trade
	startTime time.Time  // When the trade was initiated

	// Thread safety
	mutex sync.RWMutex

	// TODO: Add references to packet system and entity manager when available
	// packetManager *PacketManager
	// entityManager *EntityManager
}

// NewTrade creates a new trade between two entities
// Converted from C++ Trade::Trade constructor
func NewTrade(entity1, entity2 Entity) *Trade {
	if entity1 == nil || entity2 == nil {
		return nil
	}

	trade := &Trade{
		trader1:   NewTradeParticipant(entity1.GetID(), entity1.IsBot(), entity1.GetClientVersion()),
		trader2:   NewTradeParticipant(entity2.GetID(), entity2.IsBot(), entity2.GetClientVersion()),
		state:     TradeStateActive,
		startTime: time.Now(),
	}

	// TODO: Open trade window when packet system is available
	// trade.openTradeWindow()

	return trade
}

// GetTrader1ID returns the ID of the first trader
func (t *Trade) GetTrader1ID() int32 {
	t.mutex.RLock()
	defer t.mutex.RUnlock()
	return t.trader1.EntityID
}

// GetTrader2ID returns the ID of the second trader
func (t *Trade) GetTrader2ID() int32 {
	t.mutex.RLock()
	defer t.mutex.RUnlock()
	return t.trader2.EntityID
}

// GetTradee returns the other participant in the trade
// Converted from C++ Trade::GetTradee
func (t *Trade) GetTradee(entityID int32) int32 {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	if t.trader1.EntityID == entityID {
		return t.trader2.EntityID
	} else if t.trader2.EntityID == entityID {
		return t.trader1.EntityID
	}

	return 0 // Invalid entity ID
}

// GetParticipant returns the trade participant for an entity
func (t *Trade) GetParticipant(entityID int32) *TradeParticipant {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	if t.trader1.EntityID == entityID {
		return t.trader1
	} else if t.trader2.EntityID == entityID {
		return t.trader2
	}

	return nil
}

// GetState returns the current trade state
func (t *Trade) GetState() TradeState {
	t.mutex.RLock()
	defer t.mutex.RUnlock()
	return t.state
}

// AddItemToTrade adds an item to a participant's trade offer
// Converted from C++ Trade::AddItemToTrade
func (t *Trade) AddItemToTrade(entityID int32, item Item, quantity int32, slot int8) error {
	if t.state != TradeStateActive {
		return &TradeValidationError{
			Code:    TradeResultInvalidSlot,
			Message: "Trade is not active",
		}
	}

	t.mutex.Lock()
	defer t.mutex.Unlock()

	participant := t.getParticipantUnsafe(entityID)
	if participant == nil {
		return &TradeValidationError{
			Code:    TradeResultInvalidSlot,
			Message: "Entity is not part of this trade",
		}
	}

	// Auto-find slot if needed
	if slot == TradeSlotAutoFind {
		slot = participant.GetNextFreeSlot()
	}

	// Validate slot
	if slot < 0 || slot >= participant.MaxSlots {
		return &TradeValidationError{
			Code:    TradeResultSlotOutOfRange,
			Message: fmt.Sprintf("Invalid trade slot: %d", slot),
		}
	}

	// Check if slot is already occupied
	if _, exists := participant.Items[slot]; exists {
		return &TradeValidationError{
			Code:    TradeResultInvalidSlot,
			Message: "Trade slot is already occupied",
		}
	}

	// Validate quantity
	if quantity <= 0 {
		quantity = 1
	}

	if quantity > item.GetQuantity() {
		return &TradeValidationError{
			Code:    TradeResultInsufficientQty,
			Message: "Not enough quantity available",
		}
	}

	// Check if item is already in trade
	if participant.HasItem(item.GetID()) {
		return &TradeValidationError{
			Code:    TradeResultAlreadyInTrade,
			Message: "Item is already in trade",
		}
	}

	// Validate item tradability
	otherID := t.getTradeeUnsafe(entityID)
	if err := t.validateItemTradability(item, entityID, otherID); err != nil {
		return err
	}

	// Add item to trade
	participant.Items[slot] = TradeItemInfo{
		Item:     item,
		Quantity: quantity,
	}

	// Reset acceptance flags
	t.trader1.HasAccepted = false
	t.trader2.HasAccepted = false

	// TODO: Send trade packet when packet system is available
	// t.sendTradePacket()

	return nil
}

// RemoveItemFromTrade removes an item from a participant's trade offer
// Converted from C++ Trade::RemoveItemFromTrade
func (t *Trade) RemoveItemFromTrade(entityID int32, slot int8) error {
	if t.state != TradeStateActive {
		return &TradeValidationError{
			Code:    TradeResultInvalidSlot,
			Message: "Trade is not active",
		}
	}

	t.mutex.Lock()
	defer t.mutex.Unlock()

	participant := t.getParticipantUnsafe(entityID)
	if participant == nil {
		return &TradeValidationError{
			Code:    TradeResultInvalidSlot,
			Message: "Entity is not part of this trade",
		}
	}

	// Check if slot has an item
	if _, exists := participant.Items[slot]; !exists {
		return &TradeValidationError{
			Code:    TradeResultInvalidSlot,
			Message: "Trade slot is empty",
		}
	}

	// Remove item
	delete(participant.Items, slot)

	// Reset acceptance flags
	t.trader1.HasAccepted = false
	t.trader2.HasAccepted = false

	// TODO: Send trade packet when packet system is available
	// t.sendTradePacket()

	return nil
}

// AddCoinsToTrade adds coins to a participant's trade offer
// Converted from C++ Trade::AddCoinToTrade
func (t *Trade) AddCoinsToTrade(entityID int32, amount int64) error {
	if t.state != TradeStateActive {
		return &TradeValidationError{
			Code:    TradeResultInvalidSlot,
			Message: "Trade is not active",
		}
	}

	if amount <= 0 {
		return &TradeValidationError{
			Code:    TradeResultInvalidSlot,
			Message: "Invalid coin amount",
		}
	}

	t.mutex.Lock()
	defer t.mutex.Unlock()

	participant := t.getParticipantUnsafe(entityID)
	if participant == nil {
		return &TradeValidationError{
			Code:    TradeResultInvalidSlot,
			Message: "Entity is not part of this trade",
		}
	}

	newTotal := participant.Coins + amount

	// TODO: Validate entity has sufficient coins when entity system is available
	// For now, assume validation is done elsewhere

	participant.Coins = newTotal

	// Reset acceptance flags
	t.trader1.HasAccepted = false
	t.trader2.HasAccepted = false

	// TODO: Send trade packet when packet system is available
	// t.sendTradePacket()

	return nil
}

// RemoveCoinsFromTrade removes coins from a participant's trade offer
// Converted from C++ Trade::RemoveCoinFromTrade
func (t *Trade) RemoveCoinsFromTrade(entityID int32, amount int64) error {
	if t.state != TradeStateActive {
		return &TradeValidationError{
			Code:    TradeResultInvalidSlot,
			Message: "Trade is not active",
		}
	}

	t.mutex.Lock()
	defer t.mutex.Unlock()

	participant := t.getParticipantUnsafe(entityID)
	if participant == nil {
		return &TradeValidationError{
			Code:    TradeResultInvalidSlot,
			Message: "Entity is not part of this trade",
		}
	}

	if amount >= participant.Coins {
		participant.Coins = 0
	} else {
		participant.Coins -= amount
	}

	// Reset acceptance flags
	t.trader1.HasAccepted = false
	t.trader2.HasAccepted = false

	// TODO: Send trade packet when packet system is available
	// t.sendTradePacket()

	return nil
}

// SetTradeAccepted marks a participant as having accepted the trade
// Converted from C++ Trade::SetTradeAccepted
func (t *Trade) SetTradeAccepted(entityID int32) (bool, error) {
	if t.state != TradeStateActive {
		return false, &TradeValidationError{
			Code:    TradeResultInvalidSlot,
			Message: "Trade is not active",
		}
	}

	t.mutex.Lock()
	defer t.mutex.Unlock()

	participant := t.getParticipantUnsafe(entityID)
	if participant == nil {
		return false, &TradeValidationError{
			Code:    TradeResultInvalidSlot,
			Message: "Entity is not part of this trade",
		}
	}

	participant.HasAccepted = true

	// Check if both parties have accepted
	if t.trader1.HasAccepted && t.trader2.HasAccepted {
		// Complete the trade
		t.completeTrade()
		return true, nil
	}

	// TODO: Send acceptance packet to other trader when packet system is available

	return false, nil
}

// HasAcceptedTrade checks if a participant has accepted the trade
// Converted from C++ Trade::HasAcceptedTrade
func (t *Trade) HasAcceptedTrade(entityID int32) bool {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	participant := t.getParticipantUnsafe(entityID)
	if participant == nil {
		return false
	}

	return participant.HasAccepted
}

// CancelTrade cancels the trade
// Converted from C++ Trade::CancelTrade
func (t *Trade) CancelTrade(entityID int32) error {
	t.mutex.Lock()
	defer t.mutex.Unlock()

	if t.state != TradeStateActive {
		return &TradeValidationError{
			Code:    TradeResultInvalidSlot,
			Message: "Trade is not active",
		}
	}

	t.state = TradeStateCanceled

	// TODO: Send cancel packets to both traders when packet system is available
	// TODO: Clear trade references on entities when entity system is available

	return nil
}

// GetTraderSlot returns the item in a specific slot for a trader
// Converted from C++ Trade::GetTraderSlot
func (t *Trade) GetTraderSlot(entityID int32, slot int8) Item {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	participant := t.getParticipantUnsafe(entityID)
	if participant == nil {
		return nil
	}

	if itemInfo, exists := participant.Items[slot]; exists {
		return itemInfo.Item
	}

	return nil
}

// GetTradeInfo returns comprehensive information about the trade
func (t *Trade) GetTradeInfo() map[string]interface{} {
	t.mutex.RLock()
	defer t.mutex.RUnlock()

	info := make(map[string]interface{})
	info["state"] = t.state
	info["start_time"] = t.startTime
	info["trader1_id"] = t.trader1.EntityID
	info["trader2_id"] = t.trader2.EntityID
	info["trader1_accepted"] = t.trader1.HasAccepted
	info["trader2_accepted"] = t.trader2.HasAccepted
	info["trader1_items"] = len(t.trader1.Items)
	info["trader2_items"] = len(t.trader2.Items)
	info["trader1_coins"] = t.trader1.Coins
	info["trader2_coins"] = t.trader2.Coins

	return info
}

// Private helper methods

// getParticipantUnsafe returns participant without locking (must be called with lock held)
func (t *Trade) getParticipantUnsafe(entityID int32) *TradeParticipant {
	if t.trader1.EntityID == entityID {
		return t.trader1
	} else if t.trader2.EntityID == entityID {
		return t.trader2
	}
	return nil
}

// getTradeeUnsafe returns the other trader's ID without locking
func (t *Trade) getTradeeUnsafe(entityID int32) int32 {
	if t.trader1.EntityID == entityID {
		return t.trader2.EntityID
	} else if t.trader2.EntityID == entityID {
		return t.trader1.EntityID
	}
	return 0
}

// validateItemTradability checks if an item can be traded between entities
// Converted from C++ Trade::CheckItem
func (t *Trade) validateItemTradability(item Item, traderID, otherID int32) error {
	// Check no-trade flag
	if item.IsNoTrade() {
		// TODO: Only allow no-trade items with bots when bot system is available
		return &TradeValidationError{
			Code:    TradeResultNoTrade,
			Message: "Item cannot be traded",
		}
	}

	// Check heirloom restrictions
	if item.IsHeirloom() {
		// TODO: Implement heirloom group checking when group system is available
		// For now, allow heirloom trades with basic time/attunement checks

		if item.IsAttuned() {
			return &TradeValidationError{
				Code:    TradeResultHeirloom,
				Message: "Attuned heirloom items cannot be traded",
			}
		}

		// Check time-based restrictions (48 hours default)
		creationTime := item.GetCreationTime()
		if time.Since(creationTime) > 48*time.Hour {
			return &TradeValidationError{
				Code:    TradeResultHeirloom,
				Message: "Heirloom item sharing period has expired",
			}
		}
	}

	return nil
}

// completeTrade finalizes the trade and transfers items/coins
// Converted from C++ Trade::CompleteTrade
func (t *Trade) completeTrade() {
	t.state = TradeStateCompleted

	// TODO: Implement actual item/coin transfer when entity and inventory systems are available
	// This would involve:
	// 1. Remove items/coins from each trader's inventory
	// 2. Add the other trader's items/coins to their inventory
	// 3. Send completion packets
	// 4. Log the trade
	// 5. Clear trade references on entities

	// For now, just mark as completed
}
