using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using Random = UnityEngine.Random;

public class Professor : MonoBehaviour
{
    
    [SerializeField] private List<Transform> chargingPoints;
    [SerializeField] private Animator animator;
    [SerializeField] private int maxChargingVisitsCount = 10;
    [SerializeField] private float rechargeTime = 4f;
    [SerializeField] private float speed = 5f;
    [SerializeField] private float attackRadius = 2f;
    [SerializeField] private int damage = 4;
    [SerializeField] private LayerMask playerMask;
    [SerializeField] private Image fillImage;
    [SerializeField] private Transform spriteTransform;
    
    private BoxCollider2D _hitbox;
    private Rigidbody2D _rb;
    private Health _health;
    private int _nextIdx = 1;
    private bool _isMoving = false;
    private float _arriveThreshold = 0.05f;
    private bool _facingRight = true;
    private int maxChargingVisitCountHolder;
    private void Awake()
    {
        _hitbox = GetComponent<BoxCollider2D>();
        _rb = GetComponent<Rigidbody2D>();
        _health = GetComponent<Health>();
        maxChargingVisitCountHolder = maxChargingVisitsCount;
    }

    void Start()
    {
        _health.OnHealthChanged += HandleHealthChanged;
        _hitbox.enabled = true;
        StartCoroutine(StartTheBossFight());
    }

    private void Update()
    {
        HandleFacing(chargingPoints[_nextIdx].position.x);
    }
    
    private void FixedUpdate()
    {
        Move();
    }

    private void OnDestroy()
    {
        _health.OnHealthChanged -= HandleHealthChanged;
    }

    private void Move()
    {
        if (!_isMoving) return;

        Vector2 pos = _rb.position;
        Vector2 target = chargingPoints[_nextIdx].position;

        float dist = Vector2.Distance(pos, target);

        if (dist <= _arriveThreshold)
        {
            _rb.MovePosition(target);   // snap cleanly
            Recharge();
            return;
        }

        Vector2 direction = (target - pos).normalized;
        _rb.MovePosition(pos + direction * (speed * Time.fixedDeltaTime));
    }
    private void GoToNextChargingPoint()
    {
        _hitbox.enabled = false;
        ChangeFillColor(Color.black);
        if (maxChargingVisitsCount <= 0)
        {
            animator.Play("Disappear");
            return;
        }
        
        //moving towards the next charging point, so, do the updates
        _isMoving = true;
        animator.Play("Charge");
        //update the next Charging point
        _nextIdx = (_nextIdx + 1) % chargingPoints.Count;
        maxChargingVisitsCount--;
    }
    
    private void Recharge()
    {
        ChangeFillColor(Color.red);
        _isMoving = false;
        _hitbox.enabled = true;
        animator.Play("Recharge");
        StartCoroutine(Continue());
    }

    private IEnumerator StartTheBossFight()
    {
        yield return new WaitForSeconds(3f);
        GoToNextChargingPoint();
    }

    private void ChangeFillColor(Color col)
    {
        fillImage.color = col;
    }
    private IEnumerator Continue()
    {
        yield return new WaitForSeconds(rechargeTime);
        GoToNextChargingPoint();
    }
    public void RestartTheSequence()
    {
        FindFirstObjectByType<CinemachineShakeService>()?.Shake(1.2f, 5f, 0.2f);

        //set the first position
        maxChargingVisitsCount = maxChargingVisitCountHolder;
        transform.position = chargingPoints[0].position;
        //play the reappear animation 
        animator.Play("Reappear");
        RefillHealth();
        fillImage.fillAmount = Mathf.Round(_health.CurrentHealth / _health.GetMaxHealth());
        StartCoroutine(StartTheBossFight());
    }

    private void RefillHealth()
    {
        _health.RefillHealth();
    }

    private void HandleHealthChanged(int currentHealth)
    {
        float t = (_health.GetMaxHealth() <= 0) ? 0f : (float)currentHealth / _health.GetMaxHealth();
        fillImage.fillAmount = t;
    }

    private void HandleFacing(float targetX)
    {
        float dirX = targetX - transform.position.x;
        if (dirX > 0 && !_facingRight) Flip();
        else if (dirX < 0 && _facingRight) Flip();
    }

    private void Flip()
    {
        _facingRight = !_facingRight;
        Vector3 scale = spriteTransform.localScale;
        scale.x *= -1;
        spriteTransform.localScale = scale;
    }
    
    public void DealDamage()
    {
        Collider2D[] hits = Physics2D.OverlapCircleAll(
            transform.position,
            attackRadius,
            playerMask
        );

        foreach (var hit in hits)
        {
            if (hit.TryGetComponent(out Health playerHealth))
            {
                playerHealth.TakeDamage(damage);
                SpawnHitVFX(hit);
            }

            return;
        }
    }
    
    private void SpawnHitVFX(Collider2D hit)
    {
        if (HitEffectManager.Instance != null)
        {
            Vector2 hitPos = hit.bounds.center;
            Vector2 dir = (hitPos - (Vector2) transform.position).normalized;
            
            var type = ReturnRandomHitEffectType(Random.Range(0, 3));
            
            HitEffectManager.Instance.Spawn(
                type,
                hitPos,
                dir
            );
        }
    }
    
    private HitEffectType ReturnRandomHitEffectType(int randomNumber)
    {
        HitEffectType type = HitEffectType.SmackHit;
        if (randomNumber == 0) type = HitEffectType.ZapHit;
        else if (randomNumber == 1) type = HitEffectType.SmackHit;
        else if (randomNumber == 2) type = HitEffectType.SplashHit;

        return type;
    }
}
