package object

import (
	"fmt"

	"eq2emu/internal/spawn"
)

// ObjectSpawn represents an object that extends spawn functionality
// This properly integrates with the existing spawn system
type ObjectSpawn struct {
	*spawn.Spawn // Embed the spawn functionality

	// Object-specific properties
	clickable bool // Whether the object can be clicked/interacted with
	deviceID  int8 // Device ID for interactive objects
}

// NewObjectSpawn creates a new object spawn with default values
func NewObjectSpawn() *ObjectSpawn {
	// Create base spawn
	baseSpawn := spawn.NewSpawn()

	// Set object-specific spawn properties
	baseSpawn.SetSpawnType(ObjectSpawnType)

	// Set object appearance defaults
	appearance := baseSpawn.GetAppearance()
	appearance.ActivityStatus = ObjectActivityStatus
	appearance.Pos.State = ObjectPosState
	appearance.Difficulty = ObjectDifficulty
	baseSpawn.SetAppearance(appearance)

	return &ObjectSpawn{
		Spawn:     baseSpawn,
		clickable: false,
		deviceID:  DeviceIDNone,
	}
}

// SetClickable sets whether the object can be clicked
func (os *ObjectSpawn) SetClickable(clickable bool) {
	os.clickable = clickable
}

// IsClickable returns whether the object can be clicked
func (os *ObjectSpawn) IsClickable() bool {
	return os.clickable
}

// SetDeviceID sets the device ID for interactive objects
func (os *ObjectSpawn) SetDeviceID(deviceID int8) {
	os.deviceID = deviceID
}

// GetDeviceID returns the device ID
func (os *ObjectSpawn) GetDeviceID() int8 {
	return os.deviceID
}

// IsObject always returns true for object spawns
func (os *ObjectSpawn) IsObject() bool {
	return true
}

// Copy creates a deep copy of the object spawn
func (os *ObjectSpawn) Copy() *ObjectSpawn {
	// Copy base spawn
	newSpawn := os.Spawn.Copy()

	// Create new object spawn
	newObjectSpawn := &ObjectSpawn{
		Spawn:     newSpawn,
		clickable: os.clickable,
		deviceID:  os.deviceID,
	}

	return newObjectSpawn
}

// HandleUse processes object interaction by a client
func (os *ObjectSpawn) HandleUse(clientID int32, command string) error {
	// Use the base object's HandleUse logic but with spawn integration
	object := &Object{}

	// Copy relevant properties for handling
	object.clickable = os.clickable
	object.deviceID = os.deviceID
	object.transporterID = os.GetTransporterID()
	object.appearanceShowCommandIcon = int8(0)
	if os.GetAppearance().ShowCommandIcon == 1 {
		object.appearanceShowCommandIcon = ObjectShowCommandIcon
	}

	// TODO: Copy command lists when they're integrated with spawn system

	return object.HandleUse(clientID, command)
}

// SetShowCommandIcon sets whether to show the command icon
func (os *ObjectSpawn) SetShowCommandIcon(show bool) {
	appearance := os.GetAppearance()
	if show {
		appearance.ShowCommandIcon = ObjectShowCommandIcon
	} else {
		appearance.ShowCommandIcon = 0
	}
	os.SetAppearance(appearance)
}

// ShowsCommandIcon returns whether the command icon is shown
func (os *ObjectSpawn) ShowsCommandIcon() bool {
	return os.GetAppearance().ShowCommandIcon == ObjectShowCommandIcon
}

// GetObjectInfo returns comprehensive information about the object spawn
func (os *ObjectSpawn) GetObjectInfo() map[string]interface{} {
	info := make(map[string]interface{})

	// Add spawn info
	info["spawn_id"] = os.GetID()
	info["database_id"] = os.GetDatabaseID()
	info["zone_name"] = os.GetZoneName()
	info["spawn_type"] = os.GetSpawnType()
	info["size"] = os.GetSize()

	// Add object-specific info
	info["clickable"] = os.clickable
	info["device_id"] = os.deviceID
	info["shows_command_icon"] = os.ShowsCommandIcon()
	info["transporter_id"] = os.GetTransporterID()
	info["merchant_id"] = os.GetMerchantID()
	info["is_collector"] = os.IsCollector()

	// Add position info
	appearance := os.GetAppearance()
	info["x"] = appearance.Pos.X
	info["y"] = appearance.Pos.Y
	info["z"] = appearance.Pos.Z
	info["heading"] = appearance.Pos.Dir1

	return info
}

// ObjectSpawnManager manages object spawns specifically
type ObjectSpawnManager struct {
	spawnManager *spawn.SpawnManager    // Reference to global spawn manager
	objects      map[int32]*ObjectSpawn // Object spawns by spawn ID
}

// NewObjectSpawnManager creates a new object spawn manager
func NewObjectSpawnManager(spawnManager *spawn.SpawnManager) *ObjectSpawnManager {
	return &ObjectSpawnManager{
		spawnManager: spawnManager,
		objects:      make(map[int32]*ObjectSpawn),
	}
}

// AddObjectSpawn adds an object spawn to both the object and spawn managers
func (osm *ObjectSpawnManager) AddObjectSpawn(objectSpawn *ObjectSpawn) error {
	// Add to spawn manager first
	if err := osm.spawnManager.AddSpawn(objectSpawn.Spawn); err != nil {
		return err
	}

	// Add to object tracking
	osm.objects[objectSpawn.GetID()] = objectSpawn

	return nil
}

// RemoveObjectSpawn removes an object spawn from both managers
func (osm *ObjectSpawnManager) RemoveObjectSpawn(spawnID int32) error {
	// Remove from object tracking
	delete(osm.objects, spawnID)

	// Remove from spawn manager
	return osm.spawnManager.RemoveSpawn(spawnID)
}

// GetObjectSpawn retrieves an object spawn by ID
func (osm *ObjectSpawnManager) GetObjectSpawn(spawnID int32) *ObjectSpawn {
	return osm.objects[spawnID]
}

// GetObjectSpawnsByZone returns all object spawns in a zone
func (osm *ObjectSpawnManager) GetObjectSpawnsByZone(zoneName string) []*ObjectSpawn {
	result := make([]*ObjectSpawn, 0)

	// Get all spawns in zone and filter for objects
	spawns := osm.spawnManager.GetSpawnsByZone(zoneName)
	for _, spawn := range spawns {
		if spawn.GetSpawnType() == ObjectSpawnType {
			if objectSpawn, exists := osm.objects[spawn.GetID()]; exists {
				result = append(result, objectSpawn)
			}
		}
	}

	return result
}

// GetInteractiveObjectSpawns returns all interactive object spawns
func (osm *ObjectSpawnManager) GetInteractiveObjectSpawns() []*ObjectSpawn {
	result := make([]*ObjectSpawn, 0)

	for _, objectSpawn := range osm.objects {
		if objectSpawn.IsClickable() || objectSpawn.ShowsCommandIcon() {
			result = append(result, objectSpawn)
		}
	}

	return result
}

// ProcessObjectInteraction handles interaction with an object spawn
func (osm *ObjectSpawnManager) ProcessObjectInteraction(spawnID, clientID int32, command string) error {
	objectSpawn := osm.GetObjectSpawn(spawnID)
	if objectSpawn == nil {
		return fmt.Errorf("object spawn %d not found", spawnID)
	}

	return objectSpawn.HandleUse(clientID, command)
}

// ConvertSpawnToObject converts a regular spawn to an object spawn (if applicable)
func ConvertSpawnToObject(spawn *spawn.Spawn) *ObjectSpawn {
	if spawn.GetSpawnType() != ObjectSpawnType {
		return nil
	}

	objectSpawn := &ObjectSpawn{
		Spawn:     spawn,
		clickable: false, // Default, should be loaded from data
		deviceID:  DeviceIDNone,
	}

	// Set clickable based on appearance flags or other indicators
	appearance := spawn.GetAppearance()
	if appearance.ShowCommandIcon == ObjectShowCommandIcon {
		objectSpawn.clickable = true
	}

	return objectSpawn
}

// LoadObjectSpawnFromData loads object spawn data from database/config
// This would be called when loading spawns from the database
func LoadObjectSpawnFromData(spawnData map[string]interface{}) *ObjectSpawn {
	objectSpawn := NewObjectSpawn()

	// Load basic spawn data
	if databaseID, ok := spawnData["database_id"].(int32); ok {
		objectSpawn.SetDatabaseID(databaseID)
	}

	if zoneName, ok := spawnData["zone"].(string); ok {
		objectSpawn.SetZoneName(zoneName)
	}

	// Load object-specific data
	if clickable, ok := spawnData["clickable"].(bool); ok {
		objectSpawn.SetClickable(clickable)
	}

	if deviceID, ok := spawnData["device_id"].(int8); ok {
		objectSpawn.SetDeviceID(deviceID)
	}

	// Load position data
	if x, ok := spawnData["x"].(float32); ok {
		appearance := objectSpawn.GetAppearance()
		appearance.Pos.X = x
		objectSpawn.SetAppearance(appearance)
	}

	// TODO: Load other properties as needed

	return objectSpawn
}
