extends SceneTree

const BattleStateScript := preload("res://scripts/core/battle_state.gd")


func _init() -> void:
	var failures: Array[String] = []
	var state = BattleStateScript.new()
	if not state.load_battle("res://data/scenarios/009_xiapi_siege.json"):
		push_error("Could not load Xiapi smoke scenario.")
		quit(1)
		return

	_check_single_withdraw(state, failures)
	_check_multi_withdraw(state, failures)

	if failures.is_empty():
		print("event withdraw smoke ok")
		quit(0)
		return

	for failure in failures:
		push_error(failure)
	quit(1)


func _check_single_withdraw(state, failures: Array[String]) -> void:
	var logs := []
	var feedback := []
	state.log_added.connect(func(message: String) -> void:
		logs.append(message)
	)
	state.combat_feedback_requested.connect(func(unit_id: String, text: String, kind: String) -> void:
		feedback.append({"unit_id": unit_id, "text": text, "kind": kind})
	)

	_spawn_smoke_unit(state, "withdraw_smoke_enemy", "Smoke Raider", Vector2i(4, 4))
	logs.clear()
	feedback.clear()
	var before_living: int = state.get_living_units("enemy").size()
	state._execute_event_action({"type": "withdraw_unit", "unit_id": "withdraw_smoke_enemy"})
	var unit: Dictionary = state.get_unit("withdraw_smoke_enemy")
	if unit.is_empty():
		failures.append("single withdraw smoke unit disappeared from state")
		return
	if bool(unit.get("deployed", true)):
		failures.append("single withdraw unit should no longer be deployed")
	if not bool(unit.get("alive", false)):
		failures.append("single withdraw unit should stay alive for non-defeat retreat semantics")
	if not state.get_unit_at(Vector2i(4, 4)).is_empty():
		failures.append("single withdraw unit should no longer occupy its map cell")
	if state.get_living_units("enemy").size() != before_living - 1:
		failures.append("single withdraw should remove one living enemy from battle counts")
	_check_log_contains(failures, logs, "Smoke Raider withdraws from the battlefield.")
	_check_feedback_contains(failures, feedback, "withdraw_smoke_enemy", "WITHDRAW", "fade")


func _check_multi_withdraw(state, failures: Array[String]) -> void:
	_spawn_smoke_unit(state, "withdraw_smoke_north", "North Raider", Vector2i(5, 4))
	_spawn_smoke_unit(state, "withdraw_smoke_south", "South Raider", Vector2i(6, 4))
	state._execute_event_action({
		"type": "withdraw_units",
		"unit_ids": ["withdraw_smoke_north", "withdraw_smoke_south"]
	})
	for unit_id in ["withdraw_smoke_north", "withdraw_smoke_south"]:
		var unit: Dictionary = state.get_unit(unit_id)
		if unit.is_empty():
			failures.append("multi withdraw unit missing from state: %s" % unit_id)
			continue
		if bool(unit.get("deployed", true)):
			failures.append("multi withdraw unit should no longer be deployed: %s" % unit_id)
		if not bool(unit.get("alive", false)):
			failures.append("multi withdraw unit should stay alive: %s" % unit_id)


func _spawn_smoke_unit(state, unit_id: String, unit_name: String, cell: Vector2i) -> void:
	state._execute_event_action({
		"type": "spawn_deployment",
		"deployment": {
			"unit_id": unit_id,
			"name": unit_name,
			"class_id": "cavalry",
			"team": "enemy",
			"level": 8,
			"pos": [cell.x, cell.y],
			"base": {"hp": 40, "atk": 15, "def": 8, "agi": 11}
		}
	})


func _check_log_contains(failures: Array[String], logs: Array, expected: String) -> void:
	for log_entry in logs:
		if str(log_entry) == expected:
			return
	failures.append("expected log `%s` in `%s`" % [expected, logs])


func _check_feedback_contains(
	failures: Array[String],
	feedback: Array,
	expected_unit_id: String,
	expected_text: String,
	expected_kind: String
) -> void:
	for entry in feedback:
		if (
			str(entry.get("unit_id", "")) == expected_unit_id
			and str(entry.get("text", "")) == expected_text
			and str(entry.get("kind", "")) == expected_kind
		):
			return
	failures.append("expected feedback `%s/%s/%s` in `%s`" % [
		expected_unit_id,
		expected_text,
		expected_kind,
		feedback
	])
