extends GdUnitTestSuite

## NEO-72: `ItemDefinitionsClient` parses world item defs and resolves display names.

const ItemDefsClient := preload("res://scripts/item_definitions_client.gd")

const _SAMPLE_BODY_JSON := (
	'{"schemaVersion":1,"items":[{"id":"scrap_metal_bulk",'
	+ '"displayName":"Scrap Metal (Bulk)","inventorySlotKind":"bag"}]}'
)


class MockHttpTransport:
	extends Node
	signal request_completed(
		result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray
	)
	var last_url: String = ""
	var response_code: int = 200
	var body_json: String = ""
	var _queue: Array[Dictionary] = []

	func enqueue(result: int, code: int, body: String) -> void:
		_queue.append({"result": result, "code": code, "body": body})

	func request(
		url: String,
		_custom_headers: PackedStringArray = PackedStringArray(),
		_method: HTTPClient.Method = HTTPClient.METHOD_GET,
		_request_data: String = ""
	) -> Error:
		last_url = url
		if _queue.is_empty():
			request_completed.emit(
				HTTPRequest.RESULT_SUCCESS,
				response_code,
				PackedStringArray(),
				body_json.to_utf8_buffer()
			)
			return OK
		var r: Dictionary = _queue.pop_front()
		request_completed.emit(
			r["result"], r["code"], PackedStringArray(), str(r["body"]).to_utf8_buffer()
		)
		return OK


func _make_client(transport: Node) -> Node:
	var c: Node = ItemDefsClient.new()
	c.set("injected_http", transport)
	auto_free(transport)
	auto_free(c)
	add_child(c)
	return c


func test_build_definitions_map_extracts_display_name() -> void:
	# Arrange
	var root := {
		"schemaVersion": 1,
		"items":
		[
			{
				"id": "scrap_metal_bulk",
				"displayName": "Scrap Metal (Bulk)",
				"inventorySlotKind": "bag",
			}
		],
	}
	# Act
	var built: Dictionary = ItemDefsClient.build_definitions_map(root)
	# Assert
	assert_that(built.has("scrap_metal_bulk")).is_true()
	assert_that(built["scrap_metal_bulk"]["displayName"]).is_equal("Scrap Metal (Bulk)")


func test_request_sync_hits_definitions_endpoint_and_emits() -> void:
	# Arrange
	var transport := MockHttpTransport.new()
	transport.enqueue(HTTPRequest.RESULT_SUCCESS, 200, _SAMPLE_BODY_JSON)
	var c := _make_client(transport)
	monitor_signals(c)
	# Act
	c.call("request_sync_from_server")
	# Assert
	assert_that(transport.last_url).contains("/game/world/item-definitions")
	await assert_signal(c).is_emitted("definitions_ready", any())


func test_display_name_for_after_request_sync() -> void:
	# Arrange
	var transport := MockHttpTransport.new()
	transport.enqueue(HTTPRequest.RESULT_SUCCESS, 200, _SAMPLE_BODY_JSON)
	var c := _make_client(transport)
	# Act
	c.call("request_sync_from_server")
	# Assert
	assert_that(c.call("display_name_for", "scrap_metal_bulk")).is_equal("Scrap Metal (Bulk)")


func test_http_error_does_not_emit_definitions_ready() -> void:
	# Arrange
	var transport := MockHttpTransport.new()
	transport.enqueue(HTTPRequest.RESULT_SUCCESS, 500, "{}")
	var c := _make_client(transport)
	monitor_signals(c)
	# Act
	c.call("request_sync_from_server")
	# Assert
	await assert_signal(c).wait_until(400).is_not_emitted("definitions_ready")
