using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;

/// <summary>
/// GAME 1303 Tier 2 — A boss is a loop: wait, pick an attack, do it, recover.
/// Phases change the list of attacks (and speed) as health drops.
///
/// SETUP
///  1. Boss root: Rigidbody2D + Health (big number, Invulnerability 0, Destroy On Death
///     off) + Knockback (Resistance 1) + this script. Hurtbox child (Team = Enemy).
///  2. Build attacks as UnityEvents. Each Attack entry has:
///       Name        – for your reference and the Animator trigger (optional)
///       Duration    – how long the boss is "busy" before choosing again
///       On Execute  – what happens: ProjectileShooter.Fire, MeleeAttack.Attack,
///                     Hitbox.gameObject.SetActive(true), a Dash script, etc.
///       Weight      – relative chance of being picked (2 = twice as likely as 1)
///  3. Phases: each has Health Percent (phase starts when health drops to/below it)
///     and its own list of attacks. Phase 0 should be at 100.
///     Example (Blaster): Phase 100% = [Shoot, Walk]; Phase 50% = [Shoot, TripleShot, Charge].
///  4. Wire Health.OnDied -> BossController.OnDefeated is automatic; hook
///     OnBossDefeated to a door, GameManager.LevelComplete, or a cutscene.
///  5. Optional Face Player: flips scale toward the player before each attack.
///
/// Movement between attacks is up to you: put an EnemyChase (with OnAttack empty)
/// or EnemyPatrol on the same object, or let it stand still like a turret.
/// </summary>
public class BossController : MonoBehaviour
{
    [System.Serializable]
    public class BossAttack
    {
        public string name = "Attack";
        public float duration = 1f;
        [Min(0.01f)] public float weight = 1f;
        public string animatorTrigger;
        public UnityEvent onExecute;
    }

    [System.Serializable]
    public class Phase
    {
        public string name = "Phase 1";
        [Range(0, 100)] public int healthPercent = 100;
        [Tooltip("Idle time between attacks in this phase.")]
        public float restBetweenAttacks = 1.5f;
        public List<BossAttack> attacks = new List<BossAttack>();
        public UnityEvent onPhaseStart;   // change music, roar, spawn adds
    }

    [Header("Setup")]
    public List<Phase> phases = new List<Phase>();
    public bool facePlayer = true;
    public bool artFacesRight = true;
    [Tooltip("ON = fight begins when the scene starts. OFF = wait until something calls StartBoss() (use BossArenaTrigger).")]
    public bool startAutomatically = true;
    [Tooltip("Don't repeat the same attack twice in a row.")]
    public bool avoidRepeats = true;

    [Header("Events")]
    public UnityEvent OnBossStarted;
    public UnityEvent<int> OnPhaseChanged;   // phase index
    public UnityEvent OnBossDefeated;

    public int CurrentPhaseIndex { get; private set; } = -1;
    public bool IsActive { get; private set; }

    private Health health;
    private Animator anim;
    private Transform player;
    private BossAttack lastAttack;
    private Coroutine loop;
    private bool facingRight;

    void Awake()
    {
        health = GetComponent<Health>();
        anim = GetComponent<Animator>();
        facingRight = artFacesRight ? transform.localScale.x > 0 : transform.localScale.x < 0;
        if (health != null)
        {
            health.OnHealthChanged.AddListener(CheckPhase);
            health.OnDied.AddListener(OnDefeated);
        }
        // Sort phases by health percent descending so 100 is first
        phases.Sort((a, b) => b.healthPercent.CompareTo(a.healthPercent));
    }

    void Start()
    {
        var p = GameObject.FindGameObjectWithTag("Player");
        if (p != null) player = p.transform;
        if (startAutomatically) StartBoss();
    }

    /// <summary>Call from an arena trigger to begin the fight.</summary>
    public void StartBoss()
    {
        if (IsActive || phases.Count == 0) return;
        IsActive = true;
        EnterPhase(0);
        OnBossStarted?.Invoke();
        loop = StartCoroutine(AttackLoop());
    }

    IEnumerator AttackLoop()
    {
        while (IsActive)
        {
            Phase phase = phases[CurrentPhaseIndex];
            yield return new WaitForSeconds(phase.restBetweenAttacks);
            if (!IsActive) yield break;

            BossAttack atk = PickAttack(phase);
            if (atk == null) continue;

            if (facePlayer && player != null) FaceToward(player.position.x);
            if (anim != null && !string.IsNullOrEmpty(atk.animatorTrigger)) anim.SetTrigger(atk.animatorTrigger);
            atk.onExecute?.Invoke();
            lastAttack = atk;

            yield return new WaitForSeconds(atk.duration);
        }
    }

    BossAttack PickAttack(Phase phase)
    {
        if (phase.attacks.Count == 0) return null;
        if (phase.attacks.Count == 1) return phase.attacks[0];

        float total = 0f;
        foreach (var a in phase.attacks)
            if (!(avoidRepeats && a == lastAttack)) total += a.weight;

        float r = Random.value * total;
        foreach (var a in phase.attacks)
        {
            if (avoidRepeats && a == lastAttack) continue;
            r -= a.weight;
            if (r <= 0f) return a;
        }
        return phase.attacks[0];
    }

    void CheckPhase(int current, int max)
    {
        if (!IsActive) return;
        float pct = 100f * current / max;
        // Find the lowest-threshold phase we qualify for
        int best = CurrentPhaseIndex;
        for (int i = 0; i < phases.Count; i++)
            if (pct <= phases[i].healthPercent) best = i;
        if (best != CurrentPhaseIndex) EnterPhase(best);
    }

    void EnterPhase(int index)
    {
        CurrentPhaseIndex = index;
        phases[index].onPhaseStart?.Invoke();
        OnPhaseChanged?.Invoke(index);
    }

    void OnDefeated()
    {
        IsActive = false;
        if (loop != null) StopCoroutine(loop);
        // Turn off any attack hitboxes still active
        foreach (var hb in GetComponentsInChildren<Hitbox>()) hb.gameObject.SetActive(false);
        OnBossDefeated?.Invoke();
    }

    void FaceToward(float worldX)
    {
        bool shouldFaceRight = worldX > transform.position.x;
        if (shouldFaceRight != facingRight)
        {
            facingRight = shouldFaceRight;
            Vector3 s = transform.localScale; s.x *= -1f; transform.localScale = s;
        }
    }
}