using System;
using System.Collections;
using UnityEngine;
using UnityEngine.UI;
using RetroDev.Random;

/// <summary>
/// Controls behavior and properties of enemy objects.
/// </summary>
public class Enemy : MonoBehaviour
{
    [Header("Enemy Weapon")]
    public GameObject bulletPrefab; // Prefab for the bullet shot by the enemy
    public float bulletSpeed = 5f; // Speed of the bullet
    public int bulletDamage = 1; // Damage inflicted by the bullet
    public float shootCooldown = 1f; // Cooldown between shots

    [Header("Enemy Properties")]
    public int maxHealth = 3; // Maximum health of the enemy
    private int currentHealth; // Current health of the enemy
    public float moveSpeed = 3f; // Movement speed of the enemy
    public int killReward = 10; // Score reward for killing the enemy

    // Callback event when an enemy is destroyed
    public static event Action OnEnemyDestroyed;

    // Static variable to keep track of the number of enemies in the current wave
    public static int enemyCount;

    public Slider healthSlider; // Reference to the health slider UI element
    public GameObject explosionPrefab; // Prefab for the explosion effect

    private Vector3 currentDirection; // Current movement direction of the enemy

    /// <summary>
    /// Initializes enemy properties and behaviors when the enemy is created.
    /// </summary>
    private void Start()
    {
        healthSlider.gameObject.SetActive(false);

        currentHealth = maxHealth;

        // Start shooting coroutine
        StartCoroutine(ShootRoutine());

        // Set initial random movement direction
        SetRandomDirection();

        // Scale properties based on the current wave
        ScaleProperties();

        // Increment the enemy count when an enemy is created
        enemyCount++;
    }

    /// <summary>
    /// Updates enemy movement and behavior each frame.
    /// </summary>
    private void Update()
    {
        // Move the enemy
        Move();
    }

    private void Move()
    {
        // Move the enemy in the current direction
        transform.Translate(currentDirection * moveSpeed * Time.deltaTime);

        // Check if the enemy is out of bounds
        if (!BoundaryManager.instance.IsWithinBounds(transform.position))
        {
            // If out of bounds, bounce off the boundary and set a new random direction
            BounceOffBoundary();
            SetRandomDirection();
        }
    }

    /// <summary>
    /// Bounces the enemy off the boundary when it collides with it and clamps its position within the boundary.
    /// </summary>
    private void BounceOffBoundary()
    {
        Vector3 position = transform.position;

        // Determine if the object is out of bounds on the x or y axis
        bool outOfBoundsX = position.x < BoundaryManager.instance.MinBounds.x || position.x > BoundaryManager.instance.MaxBounds.x;
        bool outOfBoundsY = position.y < BoundaryManager.instance.MinBounds.y || position.y > BoundaryManager.instance.MaxBounds.y;

        // Reflect the direction based on which axis is out of bounds
        if (outOfBoundsX)
        {
            currentDirection = Vector3.Reflect(currentDirection, Vector3.right).normalized;
            // Clamp the position within the x bounds
            position.x = Mathf.Clamp(position.x, BoundaryManager.instance.MinBounds.x, BoundaryManager.instance.MaxBounds.x);
        }

        if (outOfBoundsY)
        {
            currentDirection = Vector3.Reflect(currentDirection, Vector3.up).normalized;
            // Clamp the position within the y bounds
            position.y = Mathf.Clamp(position.y, BoundaryManager.instance.MinBounds.y, BoundaryManager.instance.MaxBounds.y);
        }

        // Apply the clamped position back to the object
        transform.position = position;
    }

    /// <summary>
    /// Sets a new random movement direction for the enemy.
    /// </summary>
    private void SetRandomDirection()
    {
        // Set a new random direction
        currentDirection = new Vector3(RandomUtil.RandomFloat(-1f, 1f), RandomUtil.RandomFloat(-1f, 1f), 0f).normalized;
    }

    /// <summary>
    /// Coroutine for shooting at regular intervals.
    /// </summary>
    private IEnumerator ShootRoutine()
    {
        while (true)
        {
            // Shoot at regular intervals
            Shoot();
            yield return new WaitForSeconds(shootCooldown);
        }
    }

    /// <summary>
    /// Fires a bullet towards the player's direction.
    /// </summary>
    private void Shoot()
    {
        // Play shooting sound effect
        AudioManager.instance.PlayShootingSound();

        // Get direction towards the player
        Vector3 playerDirection = (PlayerController.instance.transform.position - transform.position).normalized;

        // Instantiate a bullet
        GameObject bullet = InstantiateBullet(transform.position, Quaternion.LookRotation(Vector3.forward, playerDirection));

        // Set bullet properties (speed, damage, despawnTime, bullet owner)
        BulletController bulletController = bullet.GetComponent<BulletController>();
        bulletController.Initialize(bulletSpeed, bulletDamage, 2f, this.gameObject);
    }

    /// <summary>
    /// Instantiates a bullet at the specified position and rotation.
    /// </summary>
    private GameObject InstantiateBullet(Vector3 spawnPosition, Quaternion spawnRotation)
    {
        return Instantiate(bulletPrefab, spawnPosition, spawnRotation);
    }

    /// <summary>
    /// Applies damage to the enemy and handles destruction if health reaches zero.
    /// </summary>
    public void TakeDamage(int damageAmount)
    {
        // Update damage dealt statistic
        Performance.damageDealt += damageAmount;

        // Reduce health by the damage amount
        currentHealth -= damageAmount;

        // Update health slider value
        if (healthSlider != null)
        {
            healthSlider.value = (float)currentHealth / maxHealth;
            healthSlider.gameObject.SetActive(true); // Show health slider when taking damage
        }

        // Check if the enemy is destroyed
        if (currentHealth <= 0)
        {
            DestroyEnemy();
        }
        else
        {
            // If not destroyed, start coroutine to hide health slider after delay
            StartCoroutine(HideHealthSliderAfterDelay());
        }
    }

    /// <summary>
    /// Destroys the enemy, plays explosion effect, and awards score.
    /// </summary>
    private void DestroyEnemy()
    {
        // Play enemy destroyed sound effect
        AudioManager.instance.PlayEnemyDestroyedSound();

        // Update ships destroyed statistic
        Performance.shipsDestroyed++;

        // Instantiate explosion effect at enemy's position
        Instantiate(explosionPrefab, transform.position, Quaternion.identity);

        // Update score based on kill reward
        ScoreManager.instance.AddScore(killReward);

        // Destroy the enemy
        Destroy(gameObject);
    }

    /// <summary>
    /// Coroutine to hide the health slider after a delay.
    /// </summary>
    private IEnumerator HideHealthSliderAfterDelay()
    {
        yield return new WaitForSeconds(1f); // Adjust delay as needed

        // Hide health slider
        if (healthSlider != null)
        {
            healthSlider.gameObject.SetActive(false);
        }
    }

    /// <summary>
    /// Handles cleanup when the enemy object is destroyed.
    /// </summary>
    private void OnDestroy()
    {
        // Decrement enemy count when enemy is destroyed
        enemyCount--;

        // Trigger callback when enemy is destroyed
        OnEnemyDestroyed?.Invoke();
    }

    /// <summary>
    /// Returns the current count of enemies.
    /// </summary>
    public static int GetEnemyCount()
    {
        return enemyCount;
    }

    /// <summary>
    /// Scales enemy properties based on the current wave.
    /// </summary>
    private void ScaleProperties()
    {
        // Adjust health and damage based on the current wave
        int scaledHealth = Mathf.RoundToInt(maxHealth * (1 + 0.1f * WaveManager.currentWave));
        int scaledDamage = Mathf.RoundToInt(bulletDamage * (1 + 0.1f * WaveManager.currentWave));

        // Apply scaled properties to the enemy
        SetEnemyProperties(scaledHealth, scaledDamage);
    }

    /// <summary>
    /// Sets the health and damage properties of the enemy.
    /// </summary>
    private void SetEnemyProperties(int health, int damage)
    {
        // Set health and damage properties
        currentHealth = health;
        maxHealth = health;
        bulletDamage = damage;
    }
}
