using UnityEngine;
using UnityEngine.InputSystem;
using System.Collections;

public class Player : MonoBehaviour
{
    public GameObject gameBoard;

    public AudioClip flip;

    public AudioClip normalShoot;

    public AudioClip laserCharge;

    public AudioClip laserDischarge;

    public AudioClip laserShoot;

    public AudioClip magnetIdle;

    public AudioClip magnetShoot;

    public AudioClip death;

    public AudioClip killed;

    public AudioClip defeat;

    public AudioClip denied;

    public MusicManager musicManager;

    public Fader fader;

    public GameObject horizontalBarrierPrefab;

    public GameObject verticalBarrierPrefab;

    public GameObject horizontalLaserPrefab;

    public GameObject verticalLaserPrefab;

    public int level = 1;

    public int lives = 8;

    public int score = 0;

    public int bonus = 3100;

    public float speedBonus = 0.25f;

    public Animator gunAnimator;

    public Animator muzzleAnimator;

    public SpriteRenderer muzzleRenderer;

    public GameObject debrisPrefab;

    public int magnetCount = 0;

    public int laserCount = 0;

    public GameObject magnetLeft;

    public GameObject magnetRight;

    public GameObject magnetTop;

    public GameObject magnetBottom;

    public BallSpawner ballSpawner;

    public int percentCovered = 0;

    public float multiplier = 1.0f;

    private bool flipped = false;

    private bool gameOver = false;

    private AudioSource audioSource;

    private bool canFire = true;

    private float damageTimer = 0.0f;

    private float damageCooldown = 0.5f;

    private bool exploding = false;

    private Bounds playerBounds;

    private Bounds boardBounds;

    private bool laserActive = false;

    private bool magnetActive = false;

    private float magnetDuration = 5.0f;

    private float magnetTimer = 0.0f;

    private bool killedByShark = false;

    private float bonusTimer = bonusDecrementInterval;

    private static float bonusDecrementInterval = 0.25f;

    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        audioSource = GetComponent<AudioSource>();
        Cursor.visible = false;

        if (!TryGetWorldBounds(gameObject, out playerBounds))
        {
            Debug.LogWarning("Could not get world bounds for player.");
        }

        if (!TryGetWorldBounds(gameBoard, out boardBounds))
        {
            Debug.LogWarning("Could not get world bounds for gameBoard.");
            return;
        }

        // TODO: Generalize this to work with subset of the board bounds.
        // Initialize the magnets.
        magnetLeft.GetComponent<SpriteRenderer>().enabled = false;
        magnetRight.GetComponent<SpriteRenderer>().enabled = false;
        magnetTop.GetComponent<SpriteRenderer>().enabled = false;
        magnetBottom.GetComponent<SpriteRenderer>().enabled = false;

        magnetLeft.transform.position = new Vector3(
            boardBounds.min.x + magnetLeft.GetComponent<SpriteRenderer>().bounds.extents.x,
            transform.position.y, 
            transform.position.z
        );
        
        magnetRight.transform.position = new Vector3(
            boardBounds.max.x - magnetRight.GetComponent<SpriteRenderer>().bounds.extents.x,
            transform.position.y, 
            transform.position.z
        );
        
        magnetTop.transform.position = new Vector3(
            transform.position.x, 
            boardBounds.max.y - magnetTop.GetComponent<SpriteRenderer>().bounds.extents.y, 
            transform.position.z
        );
        
        magnetBottom.transform.position = new Vector3(
            transform.position.x, 
            boardBounds.min.y + magnetBottom.GetComponent<SpriteRenderer>().bounds.extents.y,
            transform.position.z
        );
    }

    // Update is called once per frame
    void Update()
    {
        // Foce reset the rotation.
        transform.rotation = Quaternion.identity;
        transform.eulerAngles = new Vector3(0, 0, flipped ? 90 : 0);

        // Update the damage timer.
        if (damageTimer > 0.0f)
        {
            damageTimer -= Time.deltaTime;
        }

        // Player movement is based on the mouse position, clamped to the game board bounds.
        if (!exploding)
        {
            Vector2 mouseScreen = Mouse.current.position.ReadValue();
            Camera cam = Camera.main;

            // Keep movement on the same depth plane as the current player position.
            float screenZ = cam.WorldToScreenPoint(transform.position).z;
            Vector3 mouseWorld = cam.ScreenToWorldPoint(new Vector3(mouseScreen.x, mouseScreen.y, screenZ));

            Vector3 extents = playerBounds.extents;
            float clampedX = Mathf.Clamp(mouseWorld.x, boardBounds.min.x + extents.x / 2.0f, boardBounds.max.x - extents.x / 2.0f);
            float clampedY = Mathf.Clamp(mouseWorld.y, boardBounds.min.y + extents.y / 2.0f, boardBounds.max.y - extents.y / 2.0f);

            transform.position = new Vector3(clampedX, clampedY, transform.position.z);
        }

        // Handle bonus countdown and decrement.
        if (bonus > 0 && percentCovered < 80)
        {
            bonusTimer -= Time.deltaTime;

            if (bonusTimer <= 0.0f)
            {
                bonus -= 10;

                if (bonus < 0)
                {
                    bonus = 0;
                }

                bonusTimer = bonusDecrementInterval;
            }
        }

        // Handle keyboard input for flipping, game over, and toggling laser/magnet modes.
        // Disable keyboard input if the player has covered 80% or more of the area.
        if (Keyboard.current != null && percentCovered < 80)
        {
            if (Keyboard.current.spaceKey.wasPressedThisFrame)
            {
                flipped = !flipped;
                audioSource.PlayOneShot(flip);
            }

            if (Keyboard.current.escapeKey.wasPressedThisFrame)
            {
                gameOver = true;
            }

            if (Keyboard.current.wKey.wasPressedThisFrame)
            {
                if (laserActive)
                {
                    DisableLaser();
                }
                else
                {
                    EnableLaser();
                }
                
            }

            if (Keyboard.current.dKey.wasPressedThisFrame)
            {
                if (magnetActive)
                {
                    DisableMagnet();
                }
                else
                {
                    EnableMagnet();
                }
            }
        }

        // Mouse input handling for firing lasers, magnets, or normal barriers.
        if (Mouse.current.leftButton.wasPressedThisFrame && canFire && !exploding)
        {
            if (laserActive)
            {
                FireLaser();
            }
            else if (magnetActive)
            {
                FireMagnet();
            }
            else
            {
                FireNormal();
            }
        }


        // Player has covered enough area to win the level.
        if (percentCovered >= 80)
        {
            canFire = false;
        }

        // Check for game over or revival conditions.
        if (gameOver)
        {
            StartCoroutine(EndGame());
            gameOver = false; // Reset gameOver to prevent multiple coroutine starts
        }

        if (killedByShark)
        {
            killedByShark = false; // Reset the flag to prevent multiple sound plays.
            StartCoroutine(Revive());
        }

        // Handle magnet timer countdown and deactivation.
        if (magnetTimer > 0.0f)
        {
            magnetTimer -= Time.deltaTime;
            if (magnetTimer <= 0.0f)
            {
                magnetLeft.GetComponent<SpriteRenderer>().enabled = false;
                magnetRight.GetComponent<SpriteRenderer>().enabled = false;
                magnetTop.GetComponent<SpriteRenderer>().enabled = false;
                magnetBottom.GetComponent<SpriteRenderer>().enabled = false;

                ballSpawner.MagnetInactive();
            }
        }
    }

    private static bool TryGetWorldBounds(GameObject target, out Bounds bounds)
    {
        Collider2D collider2D = target.GetComponent<Collider2D>();
        if (collider2D != null)
        {
            bounds = collider2D.bounds;
            return true;
        }

        Renderer renderer = target.GetComponent<Renderer>();
        if (renderer != null)
        {
            bounds = renderer.bounds;
            return true;
        }

        Collider collider3D = target.GetComponent<Collider>();
        if (collider3D != null)
        {
            bounds = collider3D.bounds;
            return true;
        }

        bounds = default;
        return false;
    }

    private IEnumerator Revive()
    {
        yield return new WaitForSeconds(1.0f);
        if (lives > 0)
        {
            audioSource.PlayOneShot(killed);
            yield return new WaitForSeconds(1.0f);
            canFire = true;
            exploding = false;
            gunAnimator.SetTrigger("revived");
            
            SpriteRenderer[] renderers = GetComponentsInChildren<SpriteRenderer>();
            foreach (SpriteRenderer renderer in renderers)
            {
                renderer.enabled = true;
            }
        }
        else
        {
            gameOver = true;
        }
    }

    private IEnumerator EndGame()
    {
        musicManager.PauseMusic();
        audioSource.PlayOneShot(defeat);
        yield return new WaitForSeconds(1.3f);
        fader.ResetFader();
    }

    public void BarrierFinished() 
    {
        canFire = true;
    }

    public IEnumerator SetGameOver()
    {
        yield return new WaitForSeconds(3.0f);
        gameOver = true;
    }

    private void EnableLaser()
    {
        if (laserCount > 0)
        {
            laserActive = true;
            magnetActive = false;
            audioSource.PlayOneShot(laserCharge);
            gunAnimator.SetBool("laserActivated", true);
            gunAnimator.SetBool("magnetActivated", false);
        } 
        else
        {
            if (!audioSource.isPlaying)
            {
                audioSource.PlayOneShot(denied);
            }
        }
    }

    private void EnableMagnet()
    {
        if (magnetCount > 0)
        {
            laserActive = false;
            magnetActive = true;
            audioSource.PlayOneShot(magnetIdle);
            gunAnimator.SetBool("laserActivated", false);
            gunAnimator.SetBool("magnetActivated", true);
        } 
        else
        {
            if (!audioSource.isPlaying)
            {
                audioSource.PlayOneShot(denied);
            }
        }
    }

    private void DisableLaser()
    {
        if (laserActive)
        {
            laserActive = false;
            audioSource.PlayOneShot(laserDischarge);
            gunAnimator.SetBool("laserActivated", false);
        }
    }

    private void DisableMagnet()
    {
        if (magnetActive)
        {
            magnetActive = false;
            gunAnimator.SetBool("magnetActivated", false);
        }
    }

    private void FireNormal()
    {
        canFire = false;
        audioSource.PlayOneShot(normalShoot);
        muzzleAnimator.SetTrigger("firing");
        
        if (flipped)
        {
            Instantiate(verticalBarrierPrefab, transform.position, Quaternion.identity);
        }
        else
        {
            Instantiate(horizontalBarrierPrefab, transform.position, Quaternion.identity);
        }
    }

    private void FireLaser()
    {
        canFire = false;
        audioSource.PlayOneShot(laserShoot);
        muzzleAnimator.SetTrigger("firing");
        laserCount--;

        if (flipped)
        {
            Instantiate(verticalLaserPrefab, transform.position, Quaternion.identity);
        }
        else
        {
            Instantiate(horizontalLaserPrefab, transform.position, Quaternion.identity);
        }

        DisableLaser();
    }

    private void FireMagnet()
    {
        audioSource.PlayOneShot(magnetShoot);
        muzzleAnimator.SetTrigger("firing");
        magnetCount--;
        magnetTimer = magnetDuration;

        if (flipped)
        {
            magnetTop.GetComponent<SpriteRenderer>().enabled = true;
            magnetBottom.GetComponent<SpriteRenderer>().enabled = true;
        }
        else
        {
            magnetLeft.GetComponent<SpriteRenderer>().enabled = true;
            magnetRight.GetComponent<SpriteRenderer>().enabled = true;
        }

        ballSpawner.MagnetActive(flipped);
        DisableMagnet();
    }

    private void Explode()
    {
        if (exploding)
        {
            return;
        }

        exploding = true;
        gunAnimator.SetTrigger("playerKilled");
        muzzleRenderer.enabled = false;
        audioSource.PlayOneShot(death);

        for (int i = 0; i < 8; i++)
        {
            SpawnDebris();
        }
    }

    private void SpawnDebris()
    {
        Vector2 randomOffset = Random.insideUnitCircle * playerBounds.extents.magnitude;

        Instantiate(
            debrisPrefab, 
            transform.position + new Vector3(randomOffset.x, randomOffset.y, 0),
            Quaternion.identity
        );
    }

    public void OnBarHit()
    {
        if (damageTimer > 0.0f)
        {
            return;
        }

        lives--;
        damageTimer = damageCooldown;

        if (lives <= 0)
        {
            Explode();
            StartCoroutine(SetGameOver());
        }

        canFire = true; // Allow firing again after bar hit.

        speedBonus -= 0.05f; // Decrease speed bonus on bar hit.
        if (speedBonus < 0.0f)
        {
            speedBonus = 0.0f; // Clamp to minimum value.
        }
    }

    void OnCollisionEnter2D(Collision2D collision)
    {
        if (collision.gameObject.CompareTag("Shark"))
        {
            if (exploding)
            {
                return;
            }

            killedByShark = true;
            lives--;
            Explode();
        }
        else
        {
            Physics2D.IgnoreCollision(collision.collider, GetComponent<Collider2D>(), true);
        }
    }

    public void OnWallSpawned(int areaPercentage)
    {
        canFire = true;
        percentCovered += areaPercentage;
        score += areaPercentage * 10;
    }

    public void OnLevelEnd()
    {
        // Reset the player state for the next level.
        canFire = true;
        exploding = false;
        laserActive = false;
        magnetActive = false;
        bonus = 3100;
        level++;
        multiplier = 1.0f;
        flipped = false;
        percentCovered = 0;
    }
}
