extends GdUnitTestSuite

## NEO-74: successful craft triggers inventory + skill refresh; deny skips refresh.

const CraftClient := preload("res://scripts/craft_client.gd")


class MockCraftTransport:
	extends Node
	signal request_completed(
		result: int, response_code: int, headers: PackedStringArray, body: PackedByteArray
	)
	var body_json: String = '{"schemaVersion":1,"success":true,"outputsGranted":[]}'

	func request(
		_url: String,
		_custom_headers: PackedStringArray = PackedStringArray(),
		_method: HTTPClient.Method = HTTPClient.METHOD_POST,
		_request_data: String = ""
	) -> Error:
		request_completed.emit(
			HTTPRequest.RESULT_SUCCESS, 200, PackedStringArray(), body_json.to_utf8_buffer()
		)
		return OK


class SpySyncClient:
	extends Node
	var sync_calls: int = 0

	func request_sync_from_server() -> void:
		sync_calls += 1


class CraftRefreshHarness:
	extends Node
	var inventory_sync_calls: int = 0
	var skill_sync_calls: int = 0
	var _inventory: SpySyncClient
	var _skill: SpySyncClient
	var _craft: Node

	func setup(parent: Node, transport: Node) -> void:
		_inventory = SpySyncClient.new()
		_skill = SpySyncClient.new()
		_craft = CraftClient.new()
		_craft.set("injected_http", transport)
		parent.add_child(_craft)
		parent.add_child(_inventory)
		parent.add_child(_skill)
		_craft.connect("craft_result_received", Callable(self, "_on_craft_result"))

	func _on_craft_result(_recipe_id: String, result: Dictionary) -> void:
		if not bool(result.get("success", false)):
			return
		_inventory.request_sync_from_server()
		_skill.request_sync_from_server()

	func post_craft(recipe_id: String) -> void:
		_craft.call("request_craft", recipe_id)


func test_successful_craft_triggers_both_refreshes() -> void:
	# Arrange
	var transport := MockCraftTransport.new()
	var harness := CraftRefreshHarness.new()
	auto_free(transport)
	auto_free(harness)
	add_child(harness)
	harness.setup(self, transport)
	monitor_signals(harness._craft)
	# Act
	harness.post_craft("refine_scrap_standard")
	# Assert
	assert_signal(harness._craft).is_emitted("craft_result_received")
	assert_that(harness._inventory.sync_calls).is_equal(1)
	assert_that(harness._skill.sync_calls).is_equal(1)


func test_denied_craft_skips_refreshes() -> void:
	# Arrange
	var transport := MockCraftTransport.new()
	transport.body_json = (
		'{"schemaVersion":1,"success":false,"reasonCode":"insufficient_materials",'
		+ '"inputsConsumed":[],"outputsGranted":[]}'
	)
	var harness := CraftRefreshHarness.new()
	auto_free(transport)
	auto_free(harness)
	add_child(harness)
	harness.setup(self, transport)
	# Act
	harness.post_craft("refine_scrap_standard")
	# Assert
	assert_that(harness._inventory.sync_calls).is_equal(0)
	assert_that(harness._skill.sync_calls).is_equal(0)
