extends Node

var active_buffs: Dictionary = {}

func _ready() -> void:
  EventBus.effect_requested.connect(_on_effect_requested)
  EventBus.role_changed.connect(_on_role_changed)
  EventBus.entity_died.connect(_on_entity_died)

func _process(delta: float) -> void:
  for entity in active_buffs.keys():
    if not is_instance_valid(entity):
      active_buffs.erase(entity)
      continue
    var entries: Array = active_buffs[entity]
    var i: int = entries.size() - 1
    while i >= 0:
      var entry: Dictionary = entries[i]
      var effect: Effect = entry["effect"]
      if effect.duration > 0:
        entry["remaining"] -= delta
        if entry["remaining"] <= 0:
          var is_aura_buff: bool = entry.get("is_aura_buff", false)
          entries.remove_at(i)
          if not is_aura_buff:
            EventBus.effect_expired.emit(entity, effect)
          _recalc(entity)
          i -= 1
          continue
      if effect.tick_interval > 0:
        entry["tick_timer"] -= delta
        if entry["tick_timer"] <= 0:
          entry["tick_timer"] += effect.tick_interval
          if not effect.is_multiplier and effect.type == Effect.Type.BUFF:
            var source: Node = entry["source"]
            if not is_instance_valid(source):
              source = entity
            EventBus.heal_requested.emit(source, entity, effect.value)
      i -= 1

func apply(target: Node, effect: Effect, source: Node) -> void:
  if effect.type != Effect.Type.BUFF and effect.type != Effect.Type.AURA:
    return
  if not active_buffs.has(target):
    active_buffs[target] = []
  var replaced := false
  var entries: Array = active_buffs[target]
  for i in range(entries.size()):
    if entries[i]["effect"].effect_name == effect.effect_name:
      entries[i]["effect"] = effect
      entries[i]["source"] = source
      entries[i]["remaining"] = effect.duration
      entries[i]["tick_timer"] = effect.tick_interval
      replaced = true
      break
  if not replaced:
    entries.append({
      "effect": effect,
      "source": source,
      "remaining": effect.duration,
      "tick_timer": effect.tick_interval,
    })
    EventBus.effect_applied.emit(target, effect)
  if effect.is_multiplier:
    _recalc(target)

func apply_aura_buff(target: Node, effect: Effect, source: Node) -> void:
  if not active_buffs.has(target):
    active_buffs[target] = []
  var entry := {
    "effect": effect,
    "source": source,
    "remaining": effect.duration,
    "tick_timer": effect.tick_interval,
    "aura_source": source,
    "is_aura_buff": true,
  }
  active_buffs[target].append(entry)
  if effect.is_multiplier:
    _recalc(target)

func has_aura_buff(target: Node, aura_name: String, source: Node) -> bool:
  if not active_buffs.has(target):
    return false
  for entry in active_buffs[target]:
    if entry["effect"].effect_name == aura_name and entry.get("aura_source") == source:
      return true
  return false

func refresh_aura_buff(target: Node, aura_name: String, source: Node, duration: float) -> void:
  if not active_buffs.has(target):
    return
  for entry in active_buffs[target]:
    if entry["effect"].effect_name == aura_name and entry.get("aura_source") == source:
      entry["remaining"] = duration
      return

func clear(entity: Node) -> void:
  active_buffs.erase(entity)
  _recalc(entity)

func _on_effect_requested(target: Node, effect: Effect, source: Node) -> void:
  if effect.type == Effect.Type.BUFF or effect.type == Effect.Type.AURA:
    apply(target, effect, source)

func _on_entity_died(entity: Node) -> void:
  active_buffs.erase(entity)
  _recalc(entity)

func _on_role_changed(player: Node, _role_type: int) -> void:
  _remove_permanent(player)

func _remove_permanent(entity: Node) -> void:
  if not active_buffs.has(entity):
    return
  var entries: Array = active_buffs[entity]
  var i: int = entries.size() - 1
  while i >= 0:
    if entries[i]["effect"].duration < 0:
      EventBus.effect_expired.emit(entity, entries[i]["effect"])
      entries.remove_at(i)
    i -= 1
  _recalc(entity)

func _recalc(entity: Node) -> void:
  var mults := { "damage": 1.0, "heal": 1.0, "shield": 1.0 }
  if active_buffs.has(entity):
    for entry in active_buffs[entity]:
      var effect: Effect = entry["effect"]
      if effect.is_multiplier and effect.stat in mults:
        mults[effect.stat] += effect.value
  var player: Node = get_tree().get_first_node_in_group("player")
  if entity == player:
    PlayerData.buff_damage = mults["damage"]
    PlayerData.buff_heal = mults["heal"]
    PlayerData.buff_shield = mults["shield"]
    if PlayerData.base:
      var new_max: float = PlayerData.base.max_shield * mults["shield"]
      PlayerData.max_shield = new_max
      PlayerData.shield = min(PlayerData.shield, new_max)
      PlayerData.set_shield(PlayerData.shield)
  EventBus.buff_changed.emit(entity, "damage", mults["damage"])
