using UnityEngine;

public class NukeBall : BallBase
{
    public AudioClip criticalSound;

    public AudioClip explosionSound;

    // Tracks escalating danger/critical phases for audio cues.
    private int criticalState = 0;

    private float criticalTime = 1.0f;

    private float criticalTimer = 0.0f;

    // Initial resting height the ball bounces back toward after a floor hit.
    private float height;

    private Collider2D selfCollider;

    private bool isBouncingToHeight = false;

    private float bounceStartY = 0.0f;

    private float fallTargetY = 0.0f;

    private float bounceTimer = 0.0f;

    private float bounceDuration = 1.00f;

    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        // NukeBall is not affected by magnets, unlike other balls.
        affectedByMagnet = false;

        // Cache frequently used components once.
        audioSource = GetComponent<AudioSource>();
        animator = GetComponent<Animator>();
        selfCollider = GetComponent<Collider2D>();

        // Start with a small random horizontal drift.
        speed = new Vector2(Random.Range(-0.09f, 0.09f), 0.0f);
        height = GetComponent<Transform>().position.y;
        fallTargetY = height;

        // Find the closest valid floor point under the current position.
        UpdateFallTargetFromCurrentPosition();

        audioSource.PlayOneShot(spawnSound);
    }

    // Update is called once per frame
    void FixedUpdate()
    {
        AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(0);

        if (stateInfo.IsName("NuclearBallBounce")) {
            transform.Translate(speed);

            Vector3 currentPosition = transform.position;

            if (isBouncingToHeight)
            {
                bounceTimer += Time.fixedDeltaTime;

                float t = Mathf.Clamp01(bounceTimer / bounceDuration);
                // Ease-out ascent: fast start, gentle finish near target height.
                float easedT = 1.0f - Mathf.Pow(1.0f - t, 2.0f);

                currentPosition.y = Mathf.Lerp(bounceStartY, height, easedT);

                if (t >= 1.0f)
                {
                    // Reached top of bounce arc; resume falling phase.
                    isBouncingToHeight = false;
                    bounceTimer = 0.0f;
                    currentPosition.y = height;
                    UpdateFallTargetFromCurrentPosition();
                }
            }
            else
            {
                bounceTimer += Time.fixedDeltaTime;

                float t = Mathf.Clamp01(bounceTimer / bounceDuration);
                // Ease-in descent: gentle start, faster approach to floor.
                float easedT = t * t;

                currentPosition.y = Mathf.Lerp(height, fallTargetY, easedT);
            }

            transform.position = currentPosition;
        }

        // When in critical state 2, play the critical sound every half a second.
        if (criticalState == 2)
        {
            criticalTimer = 0.5f;
        }

        // If in a critical state, increment the critical timer and play the
        // critical sound if the timer exceeds the critical time.
        if (criticalState >= 1)
        {
            criticalTimer += Time.deltaTime;

            if (criticalTimer >= criticalTime)
            {
                audioSource.PlayOneShot(criticalSound);
            }

            // Restart timing window for the next critical beep.
            criticalTimer = 0.0f;
        }
    }

    void OnCollisionEnter2D(Collision2D collision)
    {
        // NukeBall should bounce off walls only. It cannot collide with other balls, not even other NukeBalls.
        if (collision.gameObject.CompareTag("Wall"))
        {
            ContactPoint2D contact = collision.GetContact(0);

            // Reflect horizontal movement from wall surfaces.
            speed = Vector2.Reflect(speed, contact.normal);
            speed.y = 0.0f;

            audioSource.PlayOneShot(bounceSound);

            // A floor hit (normal pointing upward) starts an ease-out bounce back to initial height.
            if (contact.normal.y > 0.5f)
            {
                isBouncingToHeight = true;
                bounceTimer = 0.0f;

                // Use exact contact point as bounce origin so visual contact feels grounded.
                bounceStartY = contact.point.y;
                fallTargetY = bounceStartY;

                Vector3 currentPosition = transform.position;
                currentPosition.y = bounceStartY;
                transform.position = currentPosition;
            }
        }
        else
        {
            Physics2D.IgnoreCollision(collision.collider, GetComponent<Collider2D>(), true);
        }
    }

    private void UpdateFallTargetFromCurrentPosition()
    {
        // Default to current y in case no valid floor is found.
        fallTargetY = transform.position.y;

        // Probe straight down and choose nearest upward-facing wall surface.
        RaycastHit2D[] hits = Physics2D.RaycastAll(transform.position, Vector2.down, 100.0f);
        float closestDistance = float.MaxValue;

        foreach (RaycastHit2D hit in hits)
        {
            if (hit.collider == null || hit.collider == selfCollider)
            {
                continue;
            }

            if (!hit.collider.CompareTag("Wall"))
            {
                continue;
            }

            if (hit.normal.y <= 0.5f)
            {
                continue;
            }

            if (hit.distance < closestDistance)
            {
                closestDistance = hit.distance;
                fallTargetY = hit.point.y;
            }
        }
    }
}
