using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;


[SerializeField]
public class Damageable : IDamageable {
  public event Action<int, Vector2> OnTakeDamage;
  public event Action OnDeath;
  public event Action<int> OnHeal;

  [SerializeField] public int BaseHealth { get; protected set; }
  public int CurrentHealth { get; protected set; }
  public bool IsDead => CurrentHealth <= 0;

  public bool IsInvincible => Time.time < InvincibleUntil;
  public float InvincibleUntil { get; protected set; } = 0f;
  public float InvincibilityLeft => Math.Max(0, InvincibleUntil - Time.time);
  [SerializeField] public float InvincibilityDuration { get; protected set; } = 0;

  public bool IsFrameFrozen => Time.time < FrameFrozenUntil;
  public float FrameFrozenUntil { get; protected set; }
  public float FrameFreezeLeft => Math.Max(0, FrameFrozenUntil - Time.time);
  [SerializeField] public float FrameFreezeDuration { get; protected set; } = 0;

  public int DamageTaken { get; protected set; }
  public Vector2 DirectionOfDamage { get; protected set; }
  [SerializeField] public bool IsKnockable { get; protected set; }
  public int Knockback { get; protected set; }

  public void Damage(int amount, Vector2 hitDirection, int knockback = 1) {
    if (IsDead || IsInvincible) return;

    CurrentHealth -= amount;
    CurrentHealth = Mathf.Max(CurrentHealth, 0);
    InvincibleUntil = Time.time + InvincibilityDuration;
    FrameFrozenUntil = Time.time + FrameFreezeDuration;
    DamageTaken = amount;
    if (IsKnockable) Knockback = knockback;

    hitDirection = hitDirection.normalized;
    if (hitDirection == Vector2.zero) hitDirection = Vector2.up;
    DirectionOfDamage = hitDirection;
    OnTakeDamage?.Invoke(amount, hitDirection);
    if (CurrentHealth == 0) OnDeath?.Invoke();
  }

  public void Heal(int amount) {
    if (IsDead) return;

    CurrentHealth += amount;
    CurrentHealth = Mathf.Min(CurrentHealth, BaseHealth);
    OnHeal?.Invoke(amount);
  }

  public void Reset() {
    CurrentHealth = BaseHealth;
    InvincibleUntil = Time.time;
    FrameFrozenUntil = Time.time;
  }
}



public interface IDamageable {
  int BaseHealth { get; }
  int CurrentHealth { get; }
  bool IsDead { get; }

  bool IsInvincible { get; }
  float InvincibleUntil { get; }
  float InvincibilityLeft { get; }
  float InvincibilityDuration { get; }

  bool IsFrameFrozen { get; }
  float FrameFrozenUntil { get; }
  float FrameFreezeLeft { get; }
  float FrameFreezeDuration { get; }

  int DamageTaken { get; }
  Vector2 DirectionOfDamage { get; }
  bool IsKnockable { get; }
  int Knockback { get; }

  public void Damage(int amount, Vector2 hitDirection, int knockback = 1);
  public void Heal(int amount);
  public void Reset();
}
