extends GdUnitTestSuite

## NEO-25: `InteractionRequestClient` POST bodies for E vs R contract ids.

const IxClient := preload("res://scripts/interaction_request_client.gd")


class MockHttpTransport:
	extends Node
	signal request_completed(
		result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray
	)
	var last_url: String = ""
	var last_body: String = ""
	var last_method: HTTPClient.Method = HTTPClient.METHOD_GET

	func request(
		url: String,
		_custom_headers: PackedStringArray = PackedStringArray(),
		method: HTTPClient.Method = HTTPClient.METHOD_GET,
		request_data: String = ""
	) -> Error:
		last_url = url
		last_body = request_data
		last_method = method
		var body_bytes: PackedByteArray = '{"schemaVersion":1,"allowed":true}'.to_utf8_buffer()
		request_completed.emit(HTTPRequest.RESULT_SUCCESS, 200, PackedStringArray(), body_bytes)
		return OK


## First POST defers completion one frame so a second press can queue as `_pending_interactable_id`.
class HoldFirstThenAutoTransport:
	extends Node
	signal request_completed(
		result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray
	)
	var bodies: Array[String] = []
	var _first: bool = true

	func request(
		_url: String,
		_custom_headers: PackedStringArray = PackedStringArray(),
		_method: HTTPClient.Method = HTTPClient.METHOD_GET,
		request_data: String = ""
	) -> Error:
		bodies.append(request_data)
		var body_bytes: PackedByteArray = '{"schemaVersion":1,"allowed":true}'.to_utf8_buffer()
		if _first:
			_first = false
			call_deferred("_emit", body_bytes)
		else:
			request_completed.emit(HTTPRequest.RESULT_SUCCESS, 200, PackedStringArray(), body_bytes)
		return OK

	func _emit(body_bytes: PackedByteArray) -> void:
		request_completed.emit(HTTPRequest.RESULT_SUCCESS, 200, PackedStringArray(), body_bytes)


func _make_ix(transport: Node) -> Node:
	var ix: Node = IxClient.new()
	ix.set("injected_http", transport)
	auto_free(transport)
	auto_free(ix)
	add_child(ix)
	return ix


func test_post_terminal_body_contains_prototype_terminal() -> void:
	# Arrange
	var transport := MockHttpTransport.new()
	var ix := _make_ix(transport)
	# Act
	ix.call("_post_interact", "prototype_terminal")
	# Assert
	assert_that(transport.last_body).contains("prototype_terminal")
	assert_that(transport.last_url).contains("/interact")


func test_post_resource_body_contains_resource_node_id() -> void:
	# Arrange
	var transport := MockHttpTransport.new()
	var ix := _make_ix(transport)
	# Act
	ix.call("_post_interact", "prototype_resource_node_alpha")
	# Assert
	assert_that(transport.last_body).contains("prototype_resource_node_alpha")


func test_post_interact_terminal_public_entrypoint() -> void:
	# Arrange
	var transport := MockHttpTransport.new()
	var ix := _make_ix(transport)
	# Act
	ix.call("post_interact_terminal")
	# Assert
	assert_that(transport.last_body).contains("prototype_terminal")


func test_post_interact_resource_public_entrypoint() -> void:
	# Arrange
	var transport := MockHttpTransport.new()
	var ix := _make_ix(transport)
	# Act
	ix.call("post_interact_resource")
	# Assert
	assert_that(transport.last_body).contains("prototype_resource_node_alpha")


func test_last_wins_second_press_before_first_completes() -> void:
	# Arrange
	var transport := HoldFirstThenAutoTransport.new()
	var ix := _make_ix(transport)
	# Act
	ix.call("_post_interact", "prototype_terminal")
	ix.call("_post_interact", "prototype_resource_node_alpha")
	await get_tree().process_frame
	await get_tree().process_frame
	# Assert
	assert_that(transport.bodies.size()).is_equal(2)
	assert_that(transport.bodies[0]).contains("prototype_terminal")
	assert_that(transport.bodies[1]).contains("prototype_resource_node_alpha")
