package object

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

// Object represents a game object that extends spawn functionality
// Converted from C++ Object class
type Object struct {
	// Embed spawn functionality - TODO: Use actual spawn.Spawn when integrated
	// spawn.Spawn

	// Object-specific properties
	clickable bool   // Whether the object can be clicked/interacted with
	zoneName  string // Name of the zone this object belongs to
	deviceID  int8   // Device ID for interactive objects

	// Inherited spawn properties (placeholder until spawn integration)
	databaseID             int32
	size                   int16
	sizeOffset             int8
	merchantID             int32
	merchantType           int8
	merchantMinLevel       int8
	merchantMaxLevel       int8
	isCollector            bool
	factionID              int32
	totalHP                int32
	totalPower             int32
	currentHP              int32
	currentPower           int32
	transporterID          int32
	soundsDisabled         bool
	omittedByDBFlag        bool
	lootTier               int8
	lootDropType           int8
	spawnScript            string
	spawnScriptSetDB       bool
	primaryCommandListID   int32
	secondaryCommandListID int32

	// Appearance data placeholder - TODO: Use actual appearance struct
	appearanceActivityStatus  int8
	appearancePosState        int8
	appearanceDifficulty      int8
	appearanceShowCommandIcon int8

	// Command lists - TODO: Use actual command structures
	primaryCommands   []string
	secondaryCommands []string

	// Thread safety
	mutex sync.RWMutex
}

// NewObject creates a new object with default values
// Converted from C++ Object::Object constructor
func NewObject() *Object {
	return &Object{
		clickable:                 false,
		zoneName:                  "",
		deviceID:                  DeviceIDNone,
		appearanceActivityStatus:  ObjectActivityStatus,
		appearancePosState:        ObjectPosState,
		appearanceDifficulty:      ObjectDifficulty,
		appearanceShowCommandIcon: 0,
		primaryCommands:           make([]string, 0),
		secondaryCommands:         make([]string, 0),
	}
}

// SetClickable sets whether the object can be clicked
func (o *Object) SetClickable(clickable bool) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.clickable = clickable
}

// IsClickable returns whether the object can be clicked
func (o *Object) IsClickable() bool {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.clickable
}

// SetZone sets the zone name for this object
// Converted from C++ Object::SetZone
func (o *Object) SetZone(zoneName string) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.zoneName = zoneName
}

// GetZoneName returns the zone name for this object
func (o *Object) GetZoneName() string {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.zoneName
}

// SetDeviceID sets the device ID for interactive objects
// Converted from C++ Object::SetDeviceID
func (o *Object) SetDeviceID(deviceID int8) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.deviceID = deviceID
}

// GetDeviceID returns the device ID
// Converted from C++ Object::GetDeviceID
func (o *Object) GetDeviceID() int8 {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.deviceID
}

// IsObject always returns true for Object instances
// Converted from C++ Object::IsObject
func (o *Object) IsObject() bool {
	return true
}

// Copy creates a deep copy of the object
// Converted from C++ Object::Copy
func (o *Object) Copy() *Object {
	o.mutex.RLock()
	defer o.mutex.RUnlock()

	newObject := NewObject()

	// Copy basic properties
	newObject.clickable = o.clickable
	newObject.zoneName = o.zoneName
	newObject.deviceID = o.deviceID
	newObject.databaseID = o.databaseID
	newObject.merchantID = o.merchantID
	newObject.merchantType = o.merchantType
	newObject.merchantMinLevel = o.merchantMinLevel
	newObject.merchantMaxLevel = o.merchantMaxLevel
	newObject.isCollector = o.isCollector
	newObject.factionID = o.factionID
	newObject.totalHP = o.totalHP
	newObject.totalPower = o.totalPower
	newObject.currentHP = o.currentHP
	newObject.currentPower = o.currentPower
	newObject.transporterID = o.transporterID
	newObject.soundsDisabled = o.soundsDisabled
	newObject.omittedByDBFlag = o.omittedByDBFlag
	newObject.lootTier = o.lootTier
	newObject.lootDropType = o.lootDropType
	newObject.spawnScript = o.spawnScript
	newObject.spawnScriptSetDB = o.spawnScriptSetDB
	newObject.primaryCommandListID = o.primaryCommandListID
	newObject.secondaryCommandListID = o.secondaryCommandListID

	// Copy appearance data
	newObject.appearanceActivityStatus = o.appearanceActivityStatus
	newObject.appearancePosState = o.appearancePosState
	newObject.appearanceDifficulty = o.appearanceDifficulty
	newObject.appearanceShowCommandIcon = o.appearanceShowCommandIcon

	// Handle size with random offset (from C++ logic)
	if o.sizeOffset > 0 {
		offset := o.sizeOffset + 1
		tmpSize := int32(o.size) + (rand.Int31n(int32(offset)) - rand.Int31n(int32(offset)))
		if tmpSize < 0 {
			tmpSize = 1
		} else if tmpSize >= 0xFFFF {
			tmpSize = 0xFFFF
		}
		newObject.size = int16(tmpSize)
	} else {
		newObject.size = o.size
	}

	// Copy command lists
	newObject.primaryCommands = make([]string, len(o.primaryCommands))
	copy(newObject.primaryCommands, o.primaryCommands)
	newObject.secondaryCommands = make([]string, len(o.secondaryCommands))
	copy(newObject.secondaryCommands, o.secondaryCommands)

	return newObject
}

// HandleUse processes object interaction by a client
// Converted from C++ Object::HandleUse
func (o *Object) HandleUse(clientID int32, command string) error {
	o.mutex.RLock()
	defer o.mutex.RUnlock()

	// TODO: Implement transport destination handling when zone system is available
	// This would check for transporter ID and process teleportation
	if o.transporterID > 0 {
		// Handle transport logic
		return o.handleTransport(clientID)
	}

	// Handle command-based interaction
	if len(command) > 0 && o.appearanceShowCommandIcon == ObjectShowCommandIcon {
		return o.handleCommand(clientID, command)
	}

	return fmt.Errorf("object is not interactive")
}

// handleTransport processes transport/teleport functionality
func (o *Object) handleTransport(clientID int32) error {
	// TODO: Implement when zone and transport systems are available
	// This would:
	// 1. Get transport destinations for this object
	// 2. Present options to the client
	// 3. Process teleportation request

	return fmt.Errorf("transport system not yet implemented")
}

// handleCommand processes command-based object interaction
func (o *Object) handleCommand(clientID int32, command string) error {
	// TODO: Implement when entity command system is available
	// This would:
	// 1. Find the entity command by name
	// 2. Validate client permissions
	// 3. Execute the command

	command = strings.TrimSpace(strings.ToLower(command))

	// Check if command exists in primary or secondary commands
	for _, cmd := range o.primaryCommands {
		if strings.ToLower(cmd) == command {
			return o.executeCommand(clientID, cmd)
		}
	}

	for _, cmd := range o.secondaryCommands {
		if strings.ToLower(cmd) == command {
			return o.executeCommand(clientID, cmd)
		}
	}

	return fmt.Errorf("command '%s' not found", command)
}

// executeCommand executes a specific command for a client
func (o *Object) executeCommand(clientID int32, command string) error {
	// TODO: Implement actual command execution when command system is available
	// For now, just return success for valid commands
	return nil
}

// Serialize returns packet data for this object
// Converted from C++ Object::serialize
func (o *Object) Serialize(playerID int32, version int16) ([]byte, error) {
	// TODO: Implement actual serialization when packet system is available
	// This would call the spawn serialization method
	return nil, fmt.Errorf("serialization not yet implemented")
}

// Getter/Setter methods for inherited spawn properties

// SetCollector sets whether this object is a collector
func (o *Object) SetCollector(isCollector bool) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.isCollector = isCollector
}

// IsCollector returns whether this object is a collector
func (o *Object) IsCollector() bool {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.isCollector
}

// SetMerchantID sets the merchant ID
func (o *Object) SetMerchantID(merchantID int32) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.merchantID = merchantID
}

// GetMerchantID returns the merchant ID
func (o *Object) GetMerchantID() int32 {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.merchantID
}

// SetMerchantType sets the merchant type
func (o *Object) SetMerchantType(merchantType int8) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.merchantType = merchantType
}

// GetMerchantType returns the merchant type
func (o *Object) GetMerchantType() int8 {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.merchantType
}

// SetMerchantLevelRange sets the merchant level range
func (o *Object) SetMerchantLevelRange(minLevel, maxLevel int8) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.merchantMinLevel = minLevel
	o.merchantMaxLevel = maxLevel
}

// GetMerchantMinLevel returns the minimum merchant level
func (o *Object) GetMerchantMinLevel() int8 {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.merchantMinLevel
}

// GetMerchantMaxLevel returns the maximum merchant level
func (o *Object) GetMerchantMaxLevel() int8 {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.merchantMaxLevel
}

// SetSize sets the object size
func (o *Object) SetSize(size int16) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.size = size
}

// GetSize returns the object size
func (o *Object) GetSize() int16 {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.size
}

// SetSizeOffset sets the size randomization offset
func (o *Object) SetSizeOffset(offset int8) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.sizeOffset = offset
}

// GetSizeOffset returns the size randomization offset
func (o *Object) GetSizeOffset() int8 {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.sizeOffset
}

// SetPrimaryCommands sets the primary command list
func (o *Object) SetPrimaryCommands(commands []string) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.primaryCommands = make([]string, len(commands))
	copy(o.primaryCommands, commands)
}

// GetPrimaryCommands returns the primary command list
func (o *Object) GetPrimaryCommands() []string {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	commands := make([]string, len(o.primaryCommands))
	copy(commands, o.primaryCommands)
	return commands
}

// SetSecondaryCommands sets the secondary command list
func (o *Object) SetSecondaryCommands(commands []string) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.secondaryCommands = make([]string, len(commands))
	copy(o.secondaryCommands, commands)
}

// GetSecondaryCommands returns the secondary command list
func (o *Object) GetSecondaryCommands() []string {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	commands := make([]string, len(o.secondaryCommands))
	copy(commands, o.secondaryCommands)
	return commands
}

// SetDatabaseID sets the database ID
func (o *Object) SetDatabaseID(id int32) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.databaseID = id
}

// GetDatabaseID returns the database ID
func (o *Object) GetDatabaseID() int32 {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.databaseID
}

// SetFactionID sets the faction ID
func (o *Object) SetFactionID(factionID int32) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.factionID = factionID
}

// GetFactionID returns the faction ID
func (o *Object) GetFactionID() int32 {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.factionID
}

// SetTotalHP sets the total hit points
func (o *Object) SetTotalHP(hp int32) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.totalHP = hp
}

// GetTotalHP returns the total hit points
func (o *Object) GetTotalHP() int32 {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.totalHP
}

// SetTotalPower sets the total power
func (o *Object) SetTotalPower(power int32) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.totalPower = power
}

// GetTotalPower returns the total power
func (o *Object) GetTotalPower() int32 {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.totalPower
}

// SetHP sets the current hit points
func (o *Object) SetHP(hp int32) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.currentHP = hp
}

// GetHP returns the current hit points
func (o *Object) GetHP() int32 {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.currentHP
}

// SetPower sets the current power
func (o *Object) SetPower(power int32) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.currentPower = power
}

// GetPower returns the current power
func (o *Object) GetPower() int32 {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.currentPower
}

// SetTransporterID sets the transporter ID
func (o *Object) SetTransporterID(transporterID int32) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.transporterID = transporterID
}

// GetTransporterID returns the transporter ID
func (o *Object) GetTransporterID() int32 {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.transporterID
}

// SetSoundsDisabled sets whether sounds are disabled
func (o *Object) SetSoundsDisabled(disabled bool) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.soundsDisabled = disabled
}

// IsSoundsDisabled returns whether sounds are disabled
func (o *Object) IsSoundsDisabled() bool {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.soundsDisabled
}

// SetOmittedByDBFlag sets the omitted by DB flag
func (o *Object) SetOmittedByDBFlag(omitted bool) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.omittedByDBFlag = omitted
}

// IsOmittedByDBFlag returns the omitted by DB flag
func (o *Object) IsOmittedByDBFlag() bool {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.omittedByDBFlag
}

// SetLootTier sets the loot tier
func (o *Object) SetLootTier(tier int8) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.lootTier = tier
}

// GetLootTier returns the loot tier
func (o *Object) GetLootTier() int8 {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.lootTier
}

// SetLootDropType sets the loot drop type
func (o *Object) SetLootDropType(dropType int8) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.lootDropType = dropType
}

// GetLootDropType returns the loot drop type
func (o *Object) GetLootDropType() int8 {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.lootDropType
}

// SetSpawnScript sets the spawn script
func (o *Object) SetSpawnScript(script string) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	o.spawnScript = script
	o.spawnScriptSetDB = len(script) > 0
}

// GetSpawnScript returns the spawn script
func (o *Object) GetSpawnScript() string {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.spawnScript
}

// GetSpawnScriptSetDB returns whether spawn script is set from DB
func (o *Object) GetSpawnScriptSetDB() bool {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.spawnScriptSetDB
}

// SetShowCommandIcon sets whether to show the command icon
func (o *Object) SetShowCommandIcon(show bool) {
	o.mutex.Lock()
	defer o.mutex.Unlock()
	if show {
		o.appearanceShowCommandIcon = ObjectShowCommandIcon
	} else {
		o.appearanceShowCommandIcon = 0
	}
}

// ShowsCommandIcon returns whether the command icon is shown
func (o *Object) ShowsCommandIcon() bool {
	o.mutex.RLock()
	defer o.mutex.RUnlock()
	return o.appearanceShowCommandIcon == ObjectShowCommandIcon
}

// GetObjectInfo returns comprehensive information about the object
func (o *Object) GetObjectInfo() map[string]interface{} {
	o.mutex.RLock()
	defer o.mutex.RUnlock()

	info := make(map[string]interface{})
	info["clickable"] = o.clickable
	info["zone_name"] = o.zoneName
	info["device_id"] = o.deviceID
	info["database_id"] = o.databaseID
	info["size"] = o.size
	info["merchant_id"] = o.merchantID
	info["transporter_id"] = o.transporterID
	info["is_collector"] = o.isCollector
	info["sounds_disabled"] = o.soundsDisabled
	info["primary_commands"] = len(o.primaryCommands)
	info["secondary_commands"] = len(o.secondaryCommands)
	info["shows_command_icon"] = o.ShowsCommandIcon()

	return info
}
