class_name CombatSystem extends Node

func create_proposal(attacker: Unit, defender: Unit, distance: int) -> CombatProposal:
	var proposal := CombatProposal.new()

	var atk_tactics := _filter_tactics(attacker, distance)
	var def_tactics := _filter_tactics(defender, distance)

	var atk_tactic := _find_default_attack(atk_tactics)
	var def_tactic := _find_default_attack(def_tactics)

	# AI auto-selects for non-player units
	if not _is_player_controlled(defender):
		def_tactic = select_ai_tactic(defender, attacker, def_tactics)

	proposal.attacker = _snapshot(attacker, defender, atk_tactics, atk_tactic, def_tactic)
	proposal.defender = _snapshot(defender, attacker, def_tactics, def_tactic, atk_tactic)

	return proposal


func _filter_tactics(unit: Unit, distance: int) -> Array[CombatTactic]:
	var valid: Array[CombatTactic] = []
	for tactic in unit.tactics:
		if tactic.tactic_range and tactic.tactic_range.is_valid_range(distance, unit):
			valid.append(tactic)
	return valid


func _find_default_attack(tactics: Array[CombatTactic]) -> CombatTactic:
	for tactic in tactics:
		if tactic is AttackCombatTactic:
			return tactic
	return tactics[0] if tactics.size() > 0 else null


func _snapshot(unit: Unit, opponent: Unit, available: Array[CombatTactic], selected: CombatTactic, opponent_selected: CombatTactic) -> CombatProposal.CombatantStats:
	var stats := CombatProposal.CombatantStats.new()
	stats.unit = unit
	stats.max_hp = unit.current_stats.max_hp
	stats.hp = unit.current_stats.current_hp
	stats.sp = unit.current_stats.current_sp
	stats.spd = unit.current_stats.spd
	stats.available_tactics = available
	stats.selected_tactic = selected

	if selected and selected.deals_damage():
		var offensive: Dictionary = selected.get_offensive_stats(unit)
		stats.atk = offensive["atk"]
		stats.hit = offensive["hit"] - opponent.current_stats.eva
	else:
		stats.atk = 0
		stats.hit = 0

	if opponent_selected and opponent_selected.deals_damage():
		stats.def = opponent_selected.get_relevant_defense(unit)
	else:
		stats.def = unit.current_stats.phys_def

	return stats


func update_tactic(proposal: CombatProposal, is_attacker: bool, tactic: CombatTactic) -> void:
	var self_stats: CombatProposal.CombatantStats
	var opp_stats: CombatProposal.CombatantStats
	if is_attacker:
		self_stats = proposal.attacker
		opp_stats = proposal.defender
	else:
		self_stats = proposal.defender
		opp_stats = proposal.attacker

	self_stats.selected_tactic = tactic

	# Recalculate this side's offensive stats
	if tactic and tactic.deals_damage():
		var offensive: Dictionary = tactic.get_offensive_stats(self_stats.unit)
		self_stats.atk = offensive["atk"]
		self_stats.hit = offensive["hit"] - opp_stats.unit.current_stats.eva
	else:
		self_stats.atk = 0
		self_stats.hit = 0

	# Recalculate opponent's def based on this side's new tactic
	if tactic and tactic.deals_damage():
		opp_stats.def = tactic.get_relevant_defense(opp_stats.unit)
	else:
		opp_stats.def = opp_stats.unit.current_stats.phys_def


func select_ai_tactic(unit: Unit, opponent: Unit, available_tactics: Array[CombatTactic]) -> CombatTactic:
	var best_tactic: CombatTactic = null
	var best_damage := -1

	for tactic in available_tactics:
		if not tactic.deals_damage():
			continue
		var offensive: Dictionary = tactic.get_offensive_stats(unit)
		var defense: int = tactic.get_relevant_defense(opponent)
		var damage := maxi(offensive["atk"] - defense, 0)
		if damage > best_damage:
			best_damage = damage
			best_tactic = tactic

	if best_tactic == null or best_damage <= 0:
		for tactic in available_tactics:
			if tactic is DefendCombatTactic:
				return tactic
		return available_tactics[0] if available_tactics.size() > 0 else null

	return best_tactic


func process_combat(attacker: Unit, defender: Unit, distance: int) -> void:
	if not attacker.is_alive() or not defender.is_alive():
		return
	var proposal := create_proposal(attacker, defender, distance)
	var atk_name := attacker.current_info.name
	var def_name := defender.current_info.name
	print("=== Combat: %s vs %s ===" % [atk_name, def_name])
	print("  %s — HP:%d ATK:%d DEF:%d HIT:%d" % [atk_name, proposal.attacker.hp, proposal.attacker.atk, proposal.attacker.def, proposal.attacker.hit])
	print("  %s — HP:%d ATK:%d DEF:%d HIT:%d" % [def_name, proposal.defender.hp, proposal.defender.atk, proposal.defender.def, proposal.defender.hit])
	apply_proposal(proposal)
	var atk_hp := attacker.current_stats.current_hp if is_instance_valid(attacker) else 0
	var def_hp := defender.current_stats.current_hp if is_instance_valid(defender) else 0
	print("  Result: %s HP=%d, %s HP=%d" % [atk_name, atk_hp, def_name, def_hp])


func apply_proposal(proposal: CombatProposal) -> void:
	var atk_stats := proposal.attacker
	var def_stats := proposal.defender
	var atk_unit := atk_stats.unit
	var def_unit := def_stats.unit

	# Attacker strikes (if their tactic deals damage)
	if atk_stats.selected_tactic and atk_stats.selected_tactic.deals_damage():
		var atk_roll := randi_range(1, 100)
		if atk_roll <= atk_stats.hit:
			var damage := maxi(atk_stats.atk - def_stats.def, 0)
			def_unit.take_damage(damage)

	# Counterattack if defender survives and their tactic deals damage
	if def_unit.is_alive() and def_stats.selected_tactic and def_stats.selected_tactic.deals_damage():
		var def_roll := randi_range(1, 100)
		if def_roll <= def_stats.hit:
			var damage := maxi(def_stats.atk - atk_stats.def, 0)
			atk_unit.take_damage(damage)


func _is_player_controlled(unit: Unit) -> bool:
	return unit.current_allegiance.type == UnitAllegiance.AllegianceType.PLAYER
