package widget

import (
	"eq2emu/internal/spawn"
)

// WidgetAction represents an action that can be performed on a widget
type WidgetAction interface {
	Execute(w *Widget, caller *spawn.Spawn) error
}

// OpenDoor opens the widget (door/lift)
func (w *Widget) OpenDoor() {
	w.mutex.Lock()
	defer w.mutex.Unlock()

	// Set heading if specified
	if w.openHeading >= 0 {
		w.SetHeading(w.openHeading)
	}

	// Handle position changes
	openX := w.openX
	openY := w.openY
	openZ := w.openZ

	if openX != 0 || openY != 0 || openZ != 0 {
		x := w.GetX()
		y := w.GetY()
		z := w.GetZ()

		// Use open positions if specified
		if openX != 0 {
			x = openX
		}
		if openY != 0 {
			y = openY
		}
		if openZ != 0 {
			z = openZ
		}

		// Add movement to the open position
		// Speed of 4 units per second (from C++)
		w.AddRunningLocation(x, y, z, 4)

		// Calculate movement duration
		diff := calculateDistance(w.GetX(), w.GetY(), w.GetZ(), x, y, z)
		if diff < 0 {
			diff = -diff
		}

		// Schedule timer for movement completion
		// TODO: Zone will need to handle widget timers
		// GetZone()->AddWidgetTimer(this, diff / 4)
	}

	// Update activity status for non-lifts
	if w.widgetType != WidgetTypeLift {
		w.SetActivityStatus(DefaultActivityOpen)
	}

	// Set open state
	w.isOpen = true

	// Schedule auto-close timer if duration is set
	if w.openDuration > 0 {
		// TODO: Zone will need to handle widget timers
		// GetZone()->AddWidgetTimer(this, open_duration)
	}

	// TODO: Notify zone of spawn changes
	// GetZone()->SendSpawnChanges(this)
}

// CloseDoor closes the widget (door/lift)
func (w *Widget) CloseDoor() {
	w.mutex.Lock()
	defer w.mutex.Unlock()

	// Set heading
	if w.closedHeading > 0 {
		w.SetHeading(w.closedHeading)
	} else if w.openHeading >= 0 {
		// Fall back to original heading
		w.SetHeading(w.GetSpawnOrigHeading())
	}

	// Update activity status for non-lifts
	if w.widgetType != WidgetTypeLift {
		w.SetActivityStatus(DefaultActivityClosed)
	}

	// Handle position changes
	if w.closeX != 0 || w.closeY != 0 || w.closeZ != 0 || w.openX != 0 || w.openY != 0 || w.openZ != 0 {
		// Default to original spawn position
		x := w.GetSpawnOrigX()
		y := w.GetSpawnOrigY()
		z := w.GetSpawnOrigZ()

		// Use close positions if specified
		if w.closeX != 0 {
			x = w.closeX
		}
		if w.closeY != 0 {
			y = w.closeY
		}
		if w.closeZ != 0 {
			z = w.closeZ
		}

		// Add movement to the close position
		w.AddRunningLocation(x, y, z, 4)

		// Calculate movement duration
		diff := calculateDistance(w.GetX(), w.GetY(), w.GetZ(), x, y, z)
		if diff < 0 {
			diff = -diff
		}

		// Schedule timer for movement completion
		// TODO: Zone will need to handle widget timers
		// GetZone()->AddWidgetTimer(this, diff / 4)
	}

	// Set closed state
	w.isOpen = false

	// TODO: Notify zone of spawn changes
	// GetZone()->SendSpawnChanges(this)
}

// ProcessUse processes the use of this widget
func (w *Widget) ProcessUse(caller *spawn.Spawn) {
	// Skip if this is a lift that's currently in use
	if w.widgetType == WidgetTypeLift {
		// TODO: Check if widget has active timer in zone
		// if GetZone()->HasWidgetTimer(this) return
	}

	// TODO: Call spawn script for custom handling
	// if GetZone()->CallSpawnScript(this, SPAWN_SCRIPT_USEDOOR, caller, "", is_open)
	//   return // handled in lua

	// Default behavior: toggle open/closed state
	wasOpen := w.IsOpen()
	if wasOpen {
		w.CloseDoor()
	} else {
		w.OpenDoor()
	}

	// Play appropriate sound
	if w.IsOpen() && w.openSound != "" {
		// TODO: Play sound through zone
		// GetZone()->PlaySoundFile(0, openSound, widgetX, widgetY, widgetZ)
	} else if !w.IsOpen() && w.closeSound != "" {
		// TODO: Play sound through zone
		// GetZone()->PlaySoundFile(0, closeSound, widgetX, widgetY, widgetZ)
	}
}

// HandleTimerUpdate handles widget timer updates
func (w *Widget) HandleTimerUpdate() {
	// Lifts don't auto-close
	if w.widgetType == WidgetTypeLift {
		return
	}

	// Auto-close open doors
	if w.widgetType == WidgetTypeDoor && w.IsOpen() {
		w.HandleUse(nil, "")
	}
}

// HandleUse handles widget interaction from a client
func (w *Widget) HandleUse(client ClientInterface, command string) {
	// Handle override widget type for scripted behavior
	overrideWidgetType := w.widgetType

	// Client validation
	if client != nil {
		// TODO: Check quest requirements
		// meetsQuestReqs := w.MeetsSpawnAccessRequirements(client.GetPlayer())
		// if !meetsQuestReqs && (w.GetQuestsRequiredOverride() & 2) == 0 {
		//     return
		// }
		// if meetsQuestReqs && w.GetShowCommandIcon() != 1 {
		//     return
		// }
	}

	// Handle transporter functionality
	if client != nil && w.GetTransporterID() > 0 {
		// TODO: Handle transporter destinations
		// client.SetTemporaryTransportID(0)
		// destinations := GetZone()->GetTransporters(client, w.GetTransporterID())
		// if len(destinations) > 0 {
		//     client.ProcessTeleport(w, destinations, w.GetTransporterID())
		//     return
		// }
	}

	// Skip house commands for certain operations
	skipHouseCommands := isCommand(command, "access") || isCommand(command, "visit")

	// Handle door/lift widgets
	if !skipHouseCommands && (overrideWidgetType == WidgetTypeDoor || overrideWidgetType == WidgetTypeLift) {
		// Resolve action spawn if needed
		if w.actionSpawn == nil && w.actionSpawnID > 0 {
			// TODO: Get spawn from zone
			// spawn := GetZone()->GetSpawnByDatabaseID(w.actionSpawnID)
			// if spawn != nil && spawn.IsWidget() {
			//     w.actionSpawn = spawn.(*Widget)
			// }
		}

		// Resolve linked spawn if needed
		if w.linkedSpawn == nil && w.linkedSpawnID > 0 {
			// TODO: Get spawn from zone
			// spawn := GetZone()->GetSpawnByDatabaseID(w.linkedSpawnID)
			// if spawn != nil && spawn.IsWidget() {
			//     w.linkedSpawn = spawn.(*Widget)
			// }
		}

		// Process linked spawns
		widget := w
		if w.linkedSpawn != nil {
			widget = w.linkedSpawn
			// Fire the first door
			var caller *spawn.Spawn
			if client != nil {
				caller = client.GetPlayer()
			}
			w.ProcessUse(caller)
		} else if w.actionSpawn != nil {
			widget = w.actionSpawn
			// Resolve action spawn's linked spawn if needed
			if widget.linkedSpawn == nil && widget.linkedSpawnID > 0 {
				// TODO: Get spawn from zone
				// spawn := GetZone()->GetSpawnByDatabaseID(widget.linkedSpawnID)
				// if spawn != nil && spawn.IsWidget() {
				//     widget.linkedSpawn = spawn.(*Widget)
				// }
			}

			// Process linked spawn first
			if widget.linkedSpawn != nil {
				var caller *spawn.Spawn
				if client != nil {
					caller = client.GetPlayer()
				}
				widget.linkedSpawn.ProcessUse(caller)
			}
		}

		// Process the main widget
		var caller *spawn.Spawn
		if client != nil {
			caller = client.GetPlayer()
		}
		widget.ProcessUse(caller)
	} else if client != nil && isCommand(command, "access") && w.houseID > 0 {
		// Handle house access
		// TODO: Implement house access functionality
		// This involves PlayerHouse and HouseZone systems
	} else if client != nil && isCommand(command, "visit") && w.houseID > 0 {
		// Handle house visit
		// TODO: Implement house visit functionality
	} else if client != nil && command != "" {
		// Handle other entity commands
		// TODO: Process entity commands
		// entityCommand := w.FindEntityCommand(command)
		// if entityCommand != nil {
		//     GetZone()->ProcessEntityCommand(entityCommand, client.GetPlayer(), client.GetPlayer().GetTarget())
		// }
	}
}

// SetLinkedSpawn sets the linked spawn widget
func (w *Widget) SetLinkedSpawn(linked *Widget) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.linkedSpawn = linked
}

// GetLinkedSpawn returns the linked spawn widget
func (w *Widget) GetLinkedSpawn() *Widget {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.linkedSpawn
}

// SetActionSpawn sets the action spawn widget
func (w *Widget) SetActionSpawn(action *Widget) {
	w.mutex.Lock()
	defer w.mutex.Unlock()
	w.actionSpawn = action
}

// GetActionSpawn returns the action spawn widget
func (w *Widget) GetActionSpawn() *Widget {
	w.mutex.RLock()
	defer w.mutex.RUnlock()
	return w.actionSpawn
}
