## Tests for Character System v2 — animation state, outfit layers, hand slots.
extends GutTest

var _char: Character


func before_each() -> void:
	_char = preload("res://scenes/characters/Character.tscn").instantiate() as Character
	add_child_autofree(_char)
	var cd: CharacterData = CharacterData.new()
	_char.data = cd


func test_animated_sprite_node_exists() -> void:
	assert_not_null(_char.get_node_or_null("AnimatedSprite2D"))


func test_outfit_layer_1_node_exists() -> void:
	assert_not_null(_char.get_node_or_null("OutfitLayer1"))


func test_outfit_layer_2_node_exists() -> void:
	assert_not_null(_char.get_node_or_null("OutfitLayer2"))


func test_outfit_layer_3_node_exists() -> void:
	assert_not_null(_char.get_node_or_null("OutfitLayer3"))


func test_hand_left_node_exists() -> void:
	assert_not_null(_char.get_node_or_null("HandLeft"))


func test_hand_right_node_exists() -> void:
	assert_not_null(_char.get_node_or_null("HandRight"))


func test_snap_receiver_node_exists() -> void:
	assert_not_null(_char.get_node_or_null("SnapReceiver"))


func test_character_data_outfit_has_three_empty_slots() -> void:
	assert_eq(_char.data.outfit.size(), 3)
	assert_eq(_char.data.outfit[0], "")
	assert_eq(_char.data.outfit[1], "")
	assert_eq(_char.data.outfit[2], "")


func test_default_animation_state_is_idle() -> void:
	assert_eq(_char.get_animation_state(), "idle")


func test_set_animation_state_sitting() -> void:
	_char.set_animation_state("sitting")
	assert_eq(_char.get_animation_state(), "sitting")


func test_set_animation_state_lying() -> void:
	_char.set_animation_state("lying")
	assert_eq(_char.get_animation_state(), "lying")


func test_set_animation_state_held() -> void:
	_char.set_animation_state("held")
	assert_eq(_char.get_animation_state(), "held")


func test_set_animation_state_happy() -> void:
	_char.set_animation_state("happy")
	assert_eq(_char.get_animation_state(), "happy")


func test_set_animation_state_sleeping() -> void:
	_char.set_animation_state("sleeping")
	assert_eq(_char.get_animation_state(), "sleeping")


func test_get_outfit_returns_empty_for_all_layers_initially() -> void:
	assert_eq(_char.get_outfit(1), "")
	assert_eq(_char.get_outfit(2), "")
	assert_eq(_char.get_outfit(3), "")


func test_set_outfit_stores_item_id() -> void:
	_char.set_outfit(1, "white_coat", null)
	assert_eq(_char.get_outfit(1), "white_coat")


func test_set_outfit_does_not_affect_other_layers() -> void:
	_char.set_outfit(1, "white_coat", null)
	assert_eq(_char.get_outfit(2), "")
	assert_eq(_char.get_outfit(3), "")


func test_clear_outfit_returns_item_id() -> void:
	_char.set_outfit(2, "cast_arm", null)
	var returned: String = _char.clear_outfit(2)
	assert_eq(returned, "cast_arm")


func test_clear_outfit_empties_layer() -> void:
	_char.set_outfit(2, "cast_arm", null)
	_char.clear_outfit(2)
	assert_eq(_char.get_outfit(2), "")


func test_set_outfit_invalid_layer_zero_is_noop() -> void:
	_char.set_outfit(0, "white_coat", null)
	assert_eq(_char.get_outfit(1), "")


func test_set_outfit_invalid_layer_four_is_noop() -> void:
	_char.set_outfit(4, "white_coat", null)
	assert_eq(_char.get_outfit(3), "")


func test_both_hands_free_initially() -> void:
	assert_true(_char.is_hand_free("left"))
	assert_true(_char.is_hand_free("right"))


func test_attach_item_to_left_hand() -> void:
	var item: Node2D = Node2D.new()
	add_child_autofree(item)
	_char.attach_item("left", item)
	assert_false(_char.is_hand_free("left"))


func test_get_held_item_returns_attached_item() -> void:
	var item: Node2D = Node2D.new()
	add_child_autofree(item)
	_char.attach_item("right", item)
	assert_eq(_char.get_held_item("right"), item)


func test_attach_to_occupied_hand_returns_false() -> void:
	var item1: Node2D = Node2D.new()
	var item2: Node2D = Node2D.new()
	add_child_autofree(item1)
	add_child_autofree(item2)
	_char.attach_item("left", item1)
	var result: bool = _char.attach_item("left", item2)
	assert_false(result)


func test_attach_returns_true_on_success() -> void:
	var item: Node2D = Node2D.new()
	add_child_autofree(item)
	var result: bool = _char.attach_item("right", item)
	assert_true(result)


func test_detach_item_frees_hand() -> void:
	var item: Node2D = Node2D.new()
	add_child_autofree(item)
	_char.attach_item("left", item)
	_char.detach_item("left")
	assert_true(_char.is_hand_free("left"))


func test_detach_returns_item() -> void:
	var item: Node2D = Node2D.new()
	add_child_autofree(item)
	_char.attach_item("right", item)
	var returned: Node2D = _char.detach_item("right")
	assert_eq(returned, item)


func test_detach_from_empty_hand_returns_null() -> void:
	var returned: Node2D = _char.detach_item("left")
	assert_null(returned)


func test_character_is_in_characters_group() -> void:
	assert_true(_char.is_in_group("characters"))


func test_detach_item_preserves_global_position() -> void:
	var item: Node2D = Node2D.new()
	add_child_autofree(item)
	_char.global_position = Vector2(200.0, 300.0)
	_char.attach_item("left", item)
	var expected_global: Vector2 = (_char.get_node_or_null("HandLeft") as Node2D).global_position
	_char.detach_item("left")
	assert_eq(item.global_position, expected_global)


func test_apply_outfit_item_hides_item() -> void:
	var item: Node2D = Node2D.new()
	add_child_autofree(item)
	_char.apply_outfit_item(1, "white_coat", null, item)
	assert_false(item.visible)


func test_apply_outfit_item_sets_outfit_data() -> void:
	var item: Node2D = Node2D.new()
	add_child_autofree(item)
	_char.apply_outfit_item(1, "white_coat", null, item)
	assert_eq(_char.get_outfit(1), "white_coat")


func test_remove_outfit_restores_item_visibility() -> void:
	var item: Node2D = Node2D.new()
	add_child_autofree(item)
	_char.apply_outfit_item(2, "cast_arm", null, item)
	_char.remove_outfit(2)
	assert_true(item.visible)


func test_remove_outfit_clears_outfit_data() -> void:
	var item: Node2D = Node2D.new()
	add_child_autofree(item)
	_char.apply_outfit_item(2, "cast_arm", null, item)
	_char.remove_outfit(2)
	assert_eq(_char.get_outfit(2), "")


func test_apply_outfit_item_replaces_existing() -> void:
	var item1: Node2D = Node2D.new()
	var item2: Node2D = Node2D.new()
	add_child_autofree(item1)
	add_child_autofree(item2)
	_char.apply_outfit_item(1, "white_coat", null, item1)
	_char.apply_outfit_item(1, "doctor_coat", null, item2)
	assert_true(item1.visible)
	assert_false(item2.visible)
	assert_eq(_char.get_outfit(1), "doctor_coat")


func test_tap_removes_topmost_active_outfit_layer() -> void:
	var item1: Node2D = Node2D.new()
	var item3: Node2D = Node2D.new()
	add_child_autofree(item1)
	add_child_autofree(item3)
	_char.apply_outfit_item(1, "white_coat", null, item1)
	_char.apply_outfit_item(3, "stethoscope", null, item3)
	_char._handle_outfit_tap()
	assert_eq(_char.get_outfit(3), "")
	assert_eq(_char.get_outfit(1), "white_coat")


func test_tap_noop_when_no_outfit_active() -> void:
	_char._handle_outfit_tap()
	assert_eq(_char.get_outfit(1), "")
	assert_eq(_char.get_outfit(2), "")
	assert_eq(_char.get_outfit(3), "")
