using UnityEngine;

// Simple 2D enemy brain:
// detects the player, chases on the X axis, and deals damage after an attack windup.
[RequireComponent(typeof(Enemy))]
[RequireComponent(typeof(Rigidbody2D))]
public class EnemyAI : MonoBehaviour
{
    private enum AIState
    {
        Idle,
        Chase,
        Attack
    }

    [Header("Target")]
    [SerializeField] private Transform _target;
    [SerializeField] private bool _autoFindPlayer = true;
    [SerializeField] private LayerMask _targetLayer = 1 << 7; // Player layer
    [SerializeField] private float _detectRange = 6f;
    [SerializeField] private float _detectVerticalRange = 2.5f;
    [SerializeField] private float _loseRange = 8f;
    [SerializeField] private float _loseVerticalRange = 4f;

    [Header("Movement")]
    [SerializeField] private float _moveSpeed = 2f;
    [SerializeField] private float _stopDistance = 0.75f;

    [Header("Attack")]
    [SerializeField] private float _attackRange = 1f;
    [SerializeField] private float _attackVerticalTolerance = 1f;
    [SerializeField] private int _attackDamage = 10;
    [SerializeField] private float _attackWindup = 0.25f;
    [SerializeField] private float _attackLockDuration = 0.45f;
    [SerializeField] private float _attackCooldown = 1f;
    [SerializeField] private Vector2 _attackHitVelocity = new Vector2(3f, 0f);
    [SerializeField] private string _targetHitReactionAnimationState;

    [Header("Animation")]
    [SerializeField] private string _idleAnimationState = "";
    [SerializeField] private string _runAnimationState = "";
    [SerializeField] private string _attackAnimationState = "";

    private Enemy _enemy;
    private Health _health;
    private Rigidbody2D _rb;
    private Animator _anim;
    private SpriteRenderer _spriteRenderer;
    private Health _targetHealth;
    private IDamageable _targetDamageable;

    private AIState _state = AIState.Idle;
    private bool _hasAggro;
    private bool _isAttacking;
    private bool _hasAppliedAttackDamage;
    private float _attackTimer;
    private float _attackCooldownTimer;
    private float _facingDirection = 1f;
    private string _activeAnimationState;

    private void Awake()
    {
        _enemy = GetComponent<Enemy>();
        _health = GetComponent<Health>();
        _rb = GetComponent<Rigidbody2D>();
        _anim = GetComponentInChildren<Animator>();
        _spriteRenderer = GetComponentInChildren<SpriteRenderer>();
    }

    private void OnEnable()
    {
        _state = AIState.Idle;
        _hasAggro = false;
        _isAttacking = false;
        _hasAppliedAttackDamage = false;
        _attackTimer = 0f;
        _attackCooldownTimer = 0f;
        ResolveTarget();
    }

    private void Update()
    {
        TickCooldown();

        if (_health != null && _health.IsDead)
        {
            StopHorizontalMovement();
            enabled = false;
            return;
        }

        ResolveTarget();

        if (_target == null)
        {
            if (_isAttacking)
                CancelAttack();

            _state = AIState.Idle;
            PlayStateAnimation();
            return;
        }

        if (!CanControl())
        {
            if (_isAttacking)
                CancelAttack();

            if (_enemy != null && !_enemy.CanUseAI)
                return;

            _state = AIState.Idle;
            PlayStateAnimation();
            return;
        }

        RefreshAggro();
        UpdateFacing();

        if (_isAttacking)
        {
            TickAttack();
            PlayStateAnimation();
            return;
        }

        if (!_hasAggro)
        {
            _state = AIState.Idle;
            PlayStateAnimation();
            return;
        }

        if (IsTargetInAttackRange() && _attackCooldownTimer <= 0f)
            BeginAttack();
        else
            _state = IsTargetInAttackRange() ? AIState.Idle : AIState.Chase;

        PlayStateAnimation();
    }

    private void FixedUpdate()
    {
        if (_rb == null) return;

        if (_health != null && _health.IsDead)
        {
            StopHorizontalMovement();
            return;
        }

        if (_enemy != null && !_enemy.CanUseAI)
            return;

        if (!CanMove())
        {
            StopHorizontalMovement();
            return;
        }

        Vector2 velocity = _rb.linearVelocity;
        float dx = _target.position.x - transform.position.x;

        if (Mathf.Abs(dx) <= _stopDistance)
            velocity.x = 0f;
        else
            velocity.x = Mathf.Sign(dx) * _moveSpeed;

        _rb.linearVelocity = velocity;
    }

    private void ResolveTarget()
    {
        if (_target != null)
        {
            CacheTargetComponents();
            return;
        }

        if (!_autoFindPlayer) return;

        PlayerController player = FindFirstObjectByType<PlayerController>();
        if (player == null) return;

        _target = player.transform;
        CacheTargetComponents();
    }

    private void CacheTargetComponents()
    {
        if (_target == null)
        {
            _targetHealth = null;
            _targetDamageable = null;
            return;
        }

        _targetHealth = _target.GetComponent<Health>();
        _targetDamageable = _target.GetComponent<IDamageable>();
    }

    private bool CanControl()
    {
        if (_enemy != null && !_enemy.CanUseAI) return false;
        if (_targetHealth != null && _targetHealth.IsDead) return false;

        return true;
    }

    private bool CanMove()
    {
        return _rb != null
            && _target != null
            && CanControl()
            && _hasAggro
            && !_isAttacking
            && _state == AIState.Chase
            && !IsTargetInAttackRange();
    }

    private void RefreshAggro()
    {
        if (_target == null)
        {
            _hasAggro = false;
            return;
        }

        Vector2 delta = _target.position - transform.position;
        float absX = Mathf.Abs(delta.x);
        float absY = Mathf.Abs(delta.y);

        if (_hasAggro)
        {
            _hasAggro = absX <= _loseRange && absY <= _loseVerticalRange;
            return;
        }

        _hasAggro = absX <= _detectRange && absY <= _detectVerticalRange;
    }

    private bool IsTargetInAttackRange()
    {
        if (_target == null) return false;

        Vector2 delta = _target.position - transform.position;
        return Mathf.Abs(delta.x) <= _attackRange
            && Mathf.Abs(delta.y) <= _attackVerticalTolerance;
    }

    private void BeginAttack()
    {
        _state = AIState.Attack;
        _isAttacking = true;
        _hasAppliedAttackDamage = false;
        _attackTimer = 0f;
        _attackCooldownTimer = Mathf.Max(_attackCooldown, _attackLockDuration);
        StopHorizontalMovement();
        UpdateFacing();
    }

    private void TickAttack()
    {
        _attackTimer += Time.deltaTime;
        StopHorizontalMovement();

        if (!_hasAppliedAttackDamage && _attackTimer >= _attackWindup)
        {
            _hasAppliedAttackDamage = true;
            TryApplyAttackDamage();
        }

        if (_attackTimer >= _attackLockDuration)
        {
            _isAttacking = false;
            _state = _hasAggro && !IsTargetInAttackRange() ? AIState.Chase : AIState.Idle;
        }
    }

    private void CancelAttack()
    {
        _isAttacking = false;
        _hasAppliedAttackDamage = false;
        _attackTimer = 0f;
    }

    private void TryApplyAttackDamage()
    {
        if (!IsTargetInAttackRange()) return;

        if (_targetDamageable == null)
            CacheTargetComponents();

        if (_targetDamageable == null) return;
        if (!IsTargetLayerValid()) return;

        Vector2 hitVelocity = new Vector2(_attackHitVelocity.x * _facingDirection, _attackHitVelocity.y);
        _targetDamageable.TakeDamage(_attackDamage, hitVelocity, _targetHitReactionAnimationState);
    }

    private bool IsTargetLayerValid()
    {
        if (_target == null) return false;
        if (_targetLayer.value == 0) return true;

        return (_targetLayer.value & (1 << _target.gameObject.layer)) != 0;
    }

    private void TickCooldown()
    {
        if (_attackCooldownTimer > 0f)
            _attackCooldownTimer -= Time.deltaTime;
    }

    private void StopHorizontalMovement()
    {
        if (_rb == null) return;

        Vector2 velocity = _rb.linearVelocity;
        velocity.x = 0f;
        _rb.linearVelocity = velocity;
    }

    private void UpdateFacing()
    {
        if (_target == null) return;

        float dx = _target.position.x - transform.position.x;
        if (Mathf.Abs(dx) <= 0.01f) return;

        _facingDirection = Mathf.Sign(dx);
        if (_spriteRenderer != null)
            _spriteRenderer.flipX = _facingDirection < 0f;
    }

    private void PlayStateAnimation()
    {
        string nextState = _state switch
        {
            AIState.Chase => _runAnimationState,
            AIState.Attack => _attackAnimationState,
            _ => _idleAnimationState
        };

        if (_anim == null || string.IsNullOrEmpty(nextState)) return;
        if (_activeAnimationState == nextState) return;

        _anim.Play(nextState);
        _activeAnimationState = nextState;
    }

    private void OnDrawGizmosSelected()
    {
        Gizmos.color = Color.yellow;
        Gizmos.DrawWireSphere(transform.position, _detectRange);

        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(transform.position, _attackRange);
    }
}
