using System.Collections;
using TMPro;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// Controls the player's movement, actions, and interactions with the UI.
/// </summary>
public class PlayerController : MonoBehaviour
{
    [Header("Player Movement")]

    // Movement parameters
    public float rotateSpeed = 5f;
    public float moveSpeed = 5f;
    public float maxSpeed = 10f;
    public float deceleration = 2f;

    [Header("Player Customization")]

    // Player color customization
    public static Color playerColor;
    public Slider dashCooldownSlider;

    [Header("Dash")]

    // Dash parameters
    public float dashSpeedBoost = 20f;
    public float dashDuration = 1f;
    public float dashCooldownDuration = 5f;

    private float currentSpeed = 0f;
    private bool isDashing = false;

    public static bool gameOver;

    private BoundaryManager boundaryManager;
    public static PlayerController instance;

    private GameObject playerHud;

    // Event for when the player's shield is depleted
    public delegate void GameOverAction();
    public static event GameOverAction OnGameOver;

    // Player's shield
    [Header("Shield Class")]
    public Shield shield;

    private void Awake()
    {
        instance = this;
        boundaryManager = GameObject.FindGameObjectWithTag("MainCamera").GetComponent<BoundaryManager>();
        playerHud = GameObject.FindGameObjectWithTag("HUD");
    }

    private void Start()
    {
        // Initialize the shield
        shield.Start();

        // Game starts not over
        gameOver = false;

        // Disable the player HUD
        playerHud.SetActive(false); 

        // Set player color dynamically
        this.gameObject.GetComponent<SpriteRenderer>().color = playerColor;

        // Subscribe to OnGameOver event for displaying game over UI
        PlayerController.OnGameOver += HandleGameOver;

        // Set the dash cooldown slider initially invisible
        dashCooldownSlider.gameObject.SetActive(false);
    }

    private void Update()
    {
        // If a controller is connected, use the input from the analog sticks
        if (ControllerDetector.IsControllerConnected())
        {
            HandleControllerInput();
        }
        else
        {
            HandleMovementInput();
        }
        
        HandleActionInput();
        HandleUIInput();
    }

    /// <summary>
    /// Handles the input for player movement if a controller is detected.
    /// </summary>
    private void HandleControllerInput()
    {
        // Get the input values from the left and right analog sticks
        float moveVertical = Input.GetAxis("Vertical");
        float rotateHorizontal = Input.GetAxis("Horizontal");

        // Rotate and move the ship
        RotateShip(rotateHorizontal);
        MoveShip(moveVertical);
    }

    /// <summary>
    /// Handles the input for player movement.
    /// </summary>
    private void HandleMovementInput()
    {
        float horizontalInput;
        float verticalInput;

        // Get input for rotation and movement
        horizontalInput = GetKeyInput(Controls.instance.rotateLeft) - GetKeyInput(Controls.instance.rotateRight);
        verticalInput = GetKeyInput(Controls.instance.moveForwardKeys) - GetKeyInput(Controls.instance.moveBackwardKeys);

        // Rotate and move the ship
        RotateShip(horizontalInput);
        MoveShip(verticalInput);
    }


    /// <summary>
    /// Handles the input for player actions.
    /// </summary>
    private void HandleActionInput()
    {
        // Shooting
        foreach (KeyCode key in Controls.instance.shootKeys)
        {
            if (Input.GetKey(key))
            {
                if (!gameOver)
                {
                    // Shoot if not game over
                    Vector3 playerDirection = transform.up;
                    Vector3 spawnPosition = transform.position + playerDirection * 0.5f;
                    Weapon.instance.Shoot(spawnPosition, transform.rotation);
                    break; // Exit loop after shooting once
                }
            }
        }

        // Dashing
        foreach (KeyCode key in Controls.instance.dashKeys)
        {
            if (Input.GetKeyDown(key) && !isDashing)
            {
                if (!gameOver)
                {
                    StartCoroutine(Dash());
                    break; // Exit loop after dashing once
                }
            }
        }
    }

    /// <summary>
    /// Handles the input for the player's UI interactions.
    /// </summary>
    public void HandleUIInput()
    {
        // Toggle HUD
        foreach (KeyCode key in Controls.instance.HUDKeys)
        {
            if (Input.GetKeyDown(key))
            {
                playerHud.SetActive(true);
            }
            else if (Input.GetKeyUp(key))
            {
                playerHud.SetActive(false);
            }
        }

        // Toggle Pause
        foreach (KeyCode key in Controls.instance.pauseKeys)
        {
            if (Input.GetKeyDown(key) && !PauseMenu.isPaused)
            {
                PauseMenu.isPaused = true;
            }
            else if (Input.GetKeyDown(key) && PauseMenu.isPaused)
            {
                PauseMenu.isPaused = false;
            }
        }
    }


    /// <summary>
    /// Gets the input value based on the pressed keys.
    /// </summary>
    /// <param name="keys">The array of keys to check for input.</param>
    /// <returns>The input value (1 if any of the keys are pressed, 0 otherwise).</returns>
    private float GetKeyInput(KeyCode[] keys)
    {
        foreach (KeyCode key in keys)
        {
            if (Input.GetKey(key))
            {
                return 1f;
            }
        }
        return 0f;
    }

    /// <summary>
    /// Rotates the ship based on the input.
    /// </summary>
    /// <param name="input">The input value determining the rotation direction.</param>
    private void RotateShip(float input)
    {
        float rotationAmount = rotateSpeed * input * Time.deltaTime;
        transform.Rotate(Vector3.forward * rotationAmount);
    }

    /// <summary>
    /// Moves the ship based on the input.
    /// </summary>
    /// <param name="input">The input value determining the movement direction.</param>
    private void MoveShip(float input)
    {
        if (!gameOver)
        {
            // Apply acceleration
            float accelerationAmount = input * (isDashing ? (moveSpeed + dashSpeedBoost) : moveSpeed) * Time.deltaTime;
            currentSpeed += accelerationAmount;

            // Apply deceleration when no input is given
            if (input == 0f)
            {
                currentSpeed = Mathf.Lerp(currentSpeed, 0f, deceleration * Time.deltaTime);
            }

            // Clamp speed to max speed
            currentSpeed = Mathf.Clamp(currentSpeed, -maxSpeed, maxSpeed);

            // Calculate next position
            Vector3 moveDirection = transform.up * currentSpeed;
            Vector3 nextPosition = transform.position + moveDirection * Time.deltaTime;

            // Check if next position is within camera's view
            if (boundaryManager.IsWithinBounds(nextPosition))
            {
                // Move the spaceship
                transform.position = nextPosition;
            }
        }
    }

    /// <summary>
    /// Initiates the dash action.
    /// </summary>
    /// <returns>An enumerator representing the dash coroutine.</returns>
    private IEnumerator Dash()
    {
        isDashing = true;

        // Play dash sound effect
        AudioManager.instance.PlayDashSound();

        // Store original speed
        float originalSpeed = moveSpeed;
        float originalMaxSpeed = maxSpeed;

        // Apply speed boost during dash
        moveSpeed += dashSpeedBoost;
        maxSpeed += dashSpeedBoost;

        // Wait for dash duration
        yield return new WaitForSeconds(dashDuration);

        // Reset speed back to original value
        moveSpeed = originalSpeed;
        maxSpeed = originalMaxSpeed;

        // Start cooldown
        StartCoroutine(DashCooldown());
    }

    /// <summary>
    /// Initiates the dash cooldown.
    /// </summary>
    /// <returns>An enumerator representing the dash cooldown coroutine.</returns>
    private IEnumerator DashCooldown()
    {
        // Enable UI slider during cooldown
        dashCooldownSlider.gameObject.SetActive(true);

        // Set initial value of slider to 0
        dashCooldownSlider.value = 0f;

        float cooldownTimer = 0f;

        while (cooldownTimer < dashCooldownDuration)
        {
            // Calculate normalized value based on cooldown progress
            float normalizedValue = cooldownTimer / dashCooldownDuration;

            // Set slider value based on normalized value
            dashCooldownSlider.value = normalizedValue;

            cooldownTimer += Time.deltaTime;
            yield return null;
        }

        // Reset variables and disable UI slider
        isDashing = false;
        dashCooldownSlider.value = 1f; // Set slider to 1 when dash is ready
        dashCooldownSlider.gameObject.SetActive(false);
    }

    /// <summary>
    /// Handles player taking damage.
    /// </summary>
    /// <param name="damageAmount">The amount of damage the player takes.</param>
    public void TakeDamage(int damageAmount)
    {
        // Check if shield has health
        if (shield.currentShieldHealth > 0)
        {
            // Handle damage if shield is active
            shield.TakeDamage(damageAmount);
        }
        else
        {
            // Reset player attributes
            Reset.instance.ResetGame();

            // Trigger game over if shield depleted
            if (OnGameOver != null)
            {
                OnGameOver();
            }
        }
    }

    /// <summary>
    /// Handles game over conditions.
    /// </summary>
    public void HandleGameOver()
    {
        gameOver = true;

        // Muffle audio
        AudioManager.instance.MuffleAudio();

        // Show game over UI
        UIManager.Instance.ShowGameOver();

        // Disable player movement and shooting
        if (gameObject != null)
        {
            maxSpeed = 0f;
            GetComponent<Weapon>().canShoot = false;
        }
    }

}
