using System.Collections.Generic;
using UnityEngine;

[RequireComponent(typeof(CircleCollider2D))]
public class AttackHitbox : MonoBehaviour
{
    private CircleCollider2D _collider;
    private int _damage;
    private Vector2 _hitVelocity;
    private Vector2 _hitSourcePosition;
    private Vector2? _hitTargetPosition;
    private float _hitPositionMinDistance;
    private bool _correctHitTargetY;
    private int _hitPositionSolidMask;
    private float _hitPositionCorrectionDuration;
    private string _hitReactionState;
    private LayerMask _targetLayer;
    private readonly HashSet<IDamageable> _alreadyHit = new();

    private void Awake()
    {
        _collider = GetComponent<CircleCollider2D>();
        // 플레이어 몸체는 적과 물리 충돌하지 않으므로, 공격 판정은 이 트리거만 사용한다.
        _collider.isTrigger = true;
        _collider.enabled = false;
    }

    public void Activate(ActionData data, Vector2 localPosition, Vector2 hitVelocity, Vector2 sourcePosition, Vector2? hitTargetPosition, bool correctHitTargetY, int hitPositionSolidMask, LayerMask targetLayer)
    {
        transform.localPosition = localPosition;
        _collider.radius = data.Radius;
        _damage = data.Damage;
        _hitVelocity = hitVelocity;
        _hitSourcePosition = sourcePosition;
        _hitTargetPosition = hitTargetPosition;
        _hitPositionMinDistance = Mathf.Abs(data.HitTargetOffset.x);
        _correctHitTargetY = correctHitTargetY;
        _hitPositionSolidMask = hitPositionSolidMask;
        _hitPositionCorrectionDuration = data.HitPositionCorrectionDuration;
        _hitReactionState = data.HitReactionAnimationState;
        _targetLayer = targetLayer;
        _alreadyHit.Clear();
        _collider.enabled = true;

        // 판정이 켜진 순간 이미 범위 안에 있던 적도 같은 프레임에 잡아낸다.
        ScanImmediateOverlap();
    }

    public void Deactivate()
    {
        _collider.enabled = false;
        _alreadyHit.Clear();
    }

    private void ScanImmediateOverlap()
    {
        Collider2D[] hits = Physics2D.OverlapCircleAll(transform.position, _collider.radius, _targetLayer);
        foreach (var hit in hits)
            TryDamage(hit);
    }

    private void OnTriggerEnter2D(Collider2D other) => TryDamage(other);
    private void OnTriggerStay2D(Collider2D other) => TryDamage(other);

    private void TryDamage(Collider2D other)
    {
        if ((_targetLayer.value & (1 << other.gameObject.layer)) == 0) return;

        // 피격 콜라이더는 자식에 있고, 데미지 처리는 루트에 있을 수 있다.
        if (!other.TryGetComponent<IDamageable>(out var target))
            target = other.GetComponentInParent<IDamageable>();
        if (target == null) return;
        if (_alreadyHit.Contains(target)) return;

        _alreadyHit.Add(target);
        Vector2? targetPosition = GetCorrectionTargetPosition(other);
        target.TakeDamage(_damage, _hitVelocity, _hitReactionState, targetPosition, _correctHitTargetY, _hitPositionSolidMask, _hitPositionCorrectionDuration);
    }

    private Vector2? GetCorrectionTargetPosition(Collider2D other)
    {
        if (!_hitTargetPosition.HasValue) return null;
        if (_hitPositionMinDistance <= 0.001f) return null;

        Vector2 currentPosition = other.attachedRigidbody != null
            ? other.attachedRigidbody.position
            : (Vector2)other.transform.root.position;
        float currentDistance = Mathf.Abs(currentPosition.x - _hitSourcePosition.x);

        return currentDistance < _hitPositionMinDistance ? _hitTargetPosition : null;
    }
}
