extends GdUnitTestSuite

## NEO-72: `InventoryClient` GET parse + failure signals.

const InventoryClient := preload("res://scripts/inventory_client.gd")

var _inventory_capture: Dictionary = {}


func _capture_inventory(snapshot: Dictionary) -> void:
	_inventory_capture = snapshot


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 = ""

	func request(
		url: String,
		_custom_headers: PackedStringArray = PackedStringArray(),
		_method: HTTPClient.Method = HTTPClient.METHOD_GET,
		_request_data: String = ""
	) -> Error:
		last_url = url
		request_completed.emit(
			HTTPRequest.RESULT_SUCCESS,
			response_code,
			PackedStringArray(),
			body_json.to_utf8_buffer()
		)
		return OK


static func _empty_inventory_json() -> String:
	var bag_parts: PackedStringArray = PackedStringArray()
	for i in range(24):
		bag_parts.append('{"slotIndex":%d,"quantity":0}' % i)
	var bag: String = ", ".join(bag_parts)
	return (
		(
			'{"schemaVersion":1,"playerId":"dev-local-1","bagSlots":[%s],'
			+ '"equipmentSlots":[{"slotIndex":0,"quantity":0}]}'
		)
		% bag
	)


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


func test_parse_inventory_json_empty_grid() -> void:
	# Arrange
	var json := _empty_inventory_json()
	# Act
	var snapshot: Variant = InventoryClient.parse_inventory_json(json)
	# Assert
	assert_that(snapshot is Dictionary).is_true()
	var bag: Variant = (snapshot as Dictionary).get("bagSlots", null)
	assert_that(bag is Array).is_true()
	assert_that((bag as Array).size()).is_equal(24)


func test_request_sync_emits_inventory_received() -> void:
	# Arrange
	_inventory_capture = {}
	var transport := MockHttpTransport.new()
	transport.body_json = _empty_inventory_json()
	var c := _make_client(transport)
	c.connect("inventory_received", Callable(self, "_capture_inventory"))
	monitor_signals(c)
	# Act
	c.call("request_sync_from_server")
	# Assert
	await assert_signal(c).is_emitted("inventory_received", any())
	assert_that(_inventory_capture.has("bagSlots")).is_true()
	assert_that((_inventory_capture["bagSlots"] as Array).size()).is_equal(24)
	assert_that((_inventory_capture["equipmentSlots"] as Array).size()).is_equal(1)
	assert_that(transport.last_url).contains("/inventory")


func test_http_404_emits_inventory_sync_failed() -> void:
	# Arrange
	var transport := MockHttpTransport.new()
	transport.response_code = 404
	var c := _make_client(transport)
	monitor_signals(c)
	# Act
	c.call("request_sync_from_server")
	# Assert
	await assert_signal(c).is_emitted("inventory_sync_failed", "HTTP 404 (player unknown)")


func test_malformed_json_emits_inventory_sync_failed() -> void:
	# Arrange
	var transport := MockHttpTransport.new()
	transport.body_json = "[]"
	var c := _make_client(transport)
	monitor_signals(c)
	# Act
	c.call("request_sync_from_server")
	# Assert
	await assert_signal(c).is_emitted("inventory_sync_failed", any())
