extends Area2D

# Floor Switch - Activates when enough weight is placed on it

@export_enum("walk", "pillar") var switch_type: String = "walk"  # "walk" = walk-on switch (weight 1), "pillar" = requires pillar (weight 5)
@export var required_weight: float = 1.0  # Required weight to activate (automatically set based on switch_type)

var is_activated: bool = false
var current_weight: float = 0.0
var objects_on_switch: Array = []  # Track objects currently on the switch

var tilemap_layer: TileMapLayer = null
var switch_tile_position: Vector2i = Vector2i.ZERO  # Tile position in the tilemap
var check_timer: float = 0.0  # Timer for periodic checks (pillar switches only)
var check_interval: float = 0.2  # Check every 0.2 seconds

func _ready():
	# Set required weight based on switch type
	if switch_type == "walk":
		required_weight = 1.0  # Player weight only
	elif switch_type == "pillar":
		required_weight = 5.0  # Requires pillar (weight 5)
	
	# Set collision mask to detect players and objects
	collision_layer = 0
	collision_mask = 1 | 2  # Detect players (layer 1) and objects (layer 2)
	
	# Connect signals
	body_entered.connect(_on_body_entered)
	body_exited.connect(_on_body_exited)
	
	# Find tilemap layer to update switch visual (deferred to ensure game_world is ready)
	call_deferred("_find_tilemap_layer")
	call_deferred("_update_visual")
	
	# For pillar switches, enable _process to periodically check if pillars are still not being held
	# This handles the case where a player picks up a pillar that's already on the switch
	# Walk switches don't need this check, so _process is disabled by default
	if switch_type == "pillar":
		set_process(true)
	else:
		set_process(false)

func _find_tilemap_layer():
	# Find tilemap layer to update switch visual
	var game_world = get_tree().get_first_node_in_group("game_world")
	if game_world:
		if "dungeon_tilemap_layer" in game_world:
			tilemap_layer = game_world.dungeon_tilemap_layer
		else:
			# Try to find it in Environment node
			var environment = game_world.get_node_or_null("Environment")
			if environment:
				tilemap_layer = environment.get_node_or_null("DungeonLayer0")

func _on_body_entered(body):
	# Object entered the switch
	# CRITICAL: For pillar switches, only count pillars (not being held), ignore players
	# For walk switches, count players and any objects they're carrying
	if switch_type == "pillar":
		# Pillar switch: Only count pillars that are NOT being held
		if body.has_method("can_be_grabbed") and body.can_be_grabbed():
			# Check if object is a pillar and not being held
			var object_type = body.get("object_type") if "object_type" in body else ""
			var is_being_held = body.get("is_being_held") if "is_being_held" in body else false
			
			# Only count pillars that are placed (not being held)
			if object_type == "Pillar" and not is_being_held:
				var weight = _get_object_weight(body)
				if weight >= required_weight:  # Pillar must have weight >= 5.0
					$PressSwitch.play()
					objects_on_switch.append(body)
					current_weight += weight
					print("FloorSwitch: Pillar entered switch (not held), weight: ", weight, ", total: ", current_weight)
					# Enable _process to periodically check if pillar is picked up
					if not is_processing():
						set_process(true)
					_check_activation()
		# Ignore players completely for pillar switches
	else:
		# Walk switch: Count players and any objects they're carrying
		if body.is_in_group("player") or (body.has_method("can_be_grabbed") and body.can_be_grabbed()):
			var weight = _get_object_weight(body)
			if weight > 0:
				$PressSwitch.play()
				objects_on_switch.append(body)
				current_weight += weight
				_check_activation()

func _on_body_exited(body):
	# Object left the switch
	# Check if node is still in scene tree (might be removed during level cleanup)
	if not is_inside_tree():
		return
	
	if body in objects_on_switch:
			# For pillar switches, verify the object is still valid (not being held now)
			if switch_type == "pillar":
				var object_type = body.get("object_type") if "object_type" in body else ""
				var _is_being_held = body.get("is_being_held") if "is_being_held" in body else false
				
				# Only remove if it was a pillar (and might now be held)
				if object_type == "Pillar":
					var weight = _get_object_weight(body)
					if weight > 0:
						if is_inside_tree() and has_node("ReleaseSwitch") and get_node("ReleaseSwitch").is_inside_tree():
							get_node("ReleaseSwitch").play()
						objects_on_switch.erase(body)
						current_weight -= weight
						print("FloorSwitch: Pillar left switch, weight: ", weight, ", total: ", current_weight)
						_check_activation()
			else:
				# Walk switch: Remove any object
				var weight = _get_object_weight(body)
				if weight > 0:
					if is_inside_tree() and has_node("ReleaseSwitch") and get_node("ReleaseSwitch").is_inside_tree():
						get_node("ReleaseSwitch").play()
					objects_on_switch.erase(body)
					current_weight -= weight
					_check_activation()

func _get_object_weight(body: Node) -> float:
	# Get weight of an object
	# CRITICAL: For pillar switches, this function should NOT be called for players
	# (they are filtered out in _on_body_entered)
	if body.is_in_group("player"):
		# Player base weight = 1
		var weight = 1.0
		# If player is carrying another player or object, add their weight
		# held_object is a property in player.gd, so access directly
		# NOTE: For pillar switches, players should never activate, so held objects don't matter
		if body.held_object:
			if body.held_object.is_in_group("player"):
				weight += 1.0
			else:
				# Check if held object has weight property (for interactable objects)
				# But only for walk switches - pillar switches don't count held objects
				if switch_type == "walk":
					var held_weight = _get_object_weight(body.held_object)
					if held_weight > 0:
						weight += held_weight
		return weight
	else:
		# For interactable objects, weight is an exported variable
		# Try to access it directly - if it doesn't exist, will return 0
		var weight_value = body.get("weight")
		if weight_value != null:
			return weight_value as float
		# Fallback: check if object has get_weight method
		if body.has_method("get_weight"):
			return body.get_weight()
	return 0.0

func _check_activation():
	# CRITICAL: For pillar switches, verify that objects on switch are NOT being held
	# If any pillar on switch is now being held, remove it from weight calculation
	if switch_type == "pillar":
		var valid_objects = []
		var new_weight = 0.0
		
		for obj in objects_on_switch:
			if not is_instance_valid(obj):
				continue
			
			var object_type = obj.get("object_type") if "object_type" in obj else ""
			var is_being_held = obj.get("is_being_held") if "is_being_held" in obj else false
			
			# Only count pillars that are NOT being held
			if object_type == "Pillar" and not is_being_held:
				var weight = _get_object_weight(obj)
				if weight >= required_weight:
					valid_objects.append(obj)
					new_weight += weight
			else:
				# Object is being held or not a pillar - remove it
				print("FloorSwitch: Removing held/invalid object from switch: ", obj.name if obj else "null")
		
		# Update objects list and weight
		objects_on_switch = valid_objects
		current_weight = new_weight
	
	# Check if switch should be activated
	var should_activate = current_weight >= required_weight
	
	if should_activate != is_activated:
		is_activated = should_activate
		_update_visual()
		
		# Track activated switch for syncing to new clients
		if multiplayer.has_multiplayer_peer() and multiplayer.is_server() and is_activated:
			var game_world = get_tree().get_first_node_in_group("game_world")
			if game_world:
				game_world.activated_switches[name] = true
				LogManager.log("FloorSwitch: Tracked activated switch " + name, LogManager.CATEGORY_NETWORK)
		
		# Notify connected doors
		_notify_doors()

func _update_visual():
	# Update tile visual to show activated/inactive state
	if not tilemap_layer:
		return
	
	# Choose tiles based on switch type
	var tile_coord: Vector2i
	if switch_type == "walk":
		# Walk-on switch: 11,9 (inactive) and 12,9 (active)
		tile_coord = Vector2i(12, 9) if is_activated else Vector2i(11, 9)
	elif switch_type == "pillar":
		# Pillar switch: 16,9 (inactive) and 17,9 (active)
		tile_coord = Vector2i(17, 9) if is_activated else Vector2i(16, 9)
	else:
		# Fallback to walk-on switch
		tile_coord = Vector2i(12, 9) if is_activated else Vector2i(11, 9)
	
	tilemap_layer.set_cell(switch_tile_position, 0, tile_coord)

func _process(delta):
	# For pillar switches, periodically check if pillars on switch are still not being held
	# This handles the case where a player picks up a pillar that's already on the switch
	# Only check if there are objects on the switch (optimization)
	if objects_on_switch.size() > 0:
		check_timer += delta
		if check_timer >= check_interval:
			check_timer = 0.0
			_check_activation()
	else:
		# No objects on switch - disable _process to save performance
		# It will be re-enabled when an object enters the switch
		set_process(false)

func _notify_doors():
	# Notify only doors that are explicitly connected to this switch
	# CRITICAL: Only notify doors that have this switch in their connected_switches list
	# Do NOT use position-based checks - that causes cross-room door triggering!
	var game_world = get_tree().get_first_node_in_group("game_world")
	if not game_world:
		return
	
	var entities_node = game_world.get_node_or_null("Entities")
	if not entities_node:
		return
	
	# Get this switch's room for verification
	var switch_room = get_meta("switch_room") if has_meta("switch_room") else {}
	
	# Find all blocking doors and check if they are connected to this switch
	for child in entities_node.get_children():
		if child.is_in_group("blocking_door") or child.name.begins_with("BlockingDoor_"):
			if not is_instance_valid(child):
				continue
			
			# CRITICAL SAFETY CHECK: Verify door is in the same room as this switch
			# Only notify doors that are in the SAME room as the switch
			var door_blocking_room = child.blocking_room if "blocking_room" in child else {}
			var rooms_match = false
			if switch_room and not switch_room.is_empty() and door_blocking_room and not door_blocking_room.is_empty():
				rooms_match = (switch_room.x == door_blocking_room.x and \
							   switch_room.y == door_blocking_room.y and \
							   switch_room.w == door_blocking_room.w and \
							   switch_room.h == door_blocking_room.h)
			
			# CRITICAL: Only notify doors that have this switch in their connected_switches list
			# AND verify rooms match (double-check to prevent cross-room triggering)
			if child.connected_switches.has(self):
				if rooms_match:
					# This switch is explicitly connected to this door AND in the same room - trigger puzzle check
					if child.has_method("_check_puzzle_state"):
						var door_room_info = "room:(" + str(door_blocking_room.get("x", "?")) + "," + str(door_blocking_room.get("y", "?")) + ")" if door_blocking_room and not door_blocking_room.is_empty() else "no room"
						print("FloorSwitch: Notifying door ", child.name, " (", door_room_info, ") that switch ", name, " (room: ", switch_room.get("x", "?") if switch_room and not switch_room.is_empty() else "?", ",", switch_room.get("y", "?") if switch_room and not switch_room.is_empty() else "?", ") state changed to ", is_activated)
						child._check_puzzle_state()
				else:
					push_error("FloorSwitch: ERROR - Switch ", name, " is connected to door ", child.name, " but rooms don't match! Switch room: (", switch_room.get("x", "?") if switch_room and not switch_room.is_empty() else "?", ",", switch_room.get("y", "?") if switch_room and not switch_room.is_empty() else "?", "), Door room: (", door_blocking_room.get("x", "?") if door_blocking_room and not door_blocking_room.is_empty() else "?", ",", door_blocking_room.get("y", "?") if door_blocking_room and not door_blocking_room.is_empty() else "?", ") - NOT notifying!")
