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

/// <summary>
/// GAME 1303 Tier 4 — Spikes: always out, popping up on a timer, or springing when you
/// step near. Cutthroat Course, Roach Invasion, Stardust, Hot Air (mountain peaks).
///
/// SETUP
///  1. Parent "SpikeTrap" (empty, or a floor-plate sprite) with this script.
///  2. Child "Spikes": the spike sprite + BoxCollider2D (Is Trigger) + DamageOnContact.
///     Drag the child into Spikes. Set Hidden Offset (e.g. y = -0.6) — where the spikes
///     sit when retracted, relative to the parent. Shown = (0,0).
///  3. Mode:
///       Static     – always out. (You could skip this script entirely — it's just
///                    DamageOnContact — but keeping it lets you toggle later.)
///       Timed      – out for Out Time, in for In Time, forever. Warning Time shakes/plays
///                    a click before popping so it's fair.
///       Triggered  – a separate trigger child ("TriggerArea", drag into Trigger Zone
///                    slot) pops the spikes when the Player enters, after Warning Time.
///  4. Damage only applies while extended (the child's DamageOnContact is toggled).
///
/// Start Extended / Start Delay let you offset traps in a row so they pop in sequence.
/// </summary>
public class SpikeTrap : MonoBehaviour
{
    public enum Mode { Static, Timed, Triggered }

    public Mode mode = Mode.Timed;
    public Transform spikes;
    public Vector2 hiddenOffset = new Vector2(0f, -0.6f);
    public float moveSpeed = 12f;

    [Header("Timed")]
    public float outTime = 1.5f;
    public float inTime = 1.5f;
    public float startDelay = 0f;
    public bool startExtended = false;

    [Header("Triggered")]
    public Collider2D triggerZone;
    public float retractAfter = 1f;
    public bool rearm = true;

    [Header("Warning")]
    public float warningTime = 0.3f;
    public float shakeAmount = 0.05f;
    public string warningSfx = "spike_warn";
    public string popSfx = "spike_pop";

    public UnityEvent OnExtended;
    public UnityEvent OnRetracted;

    public bool IsExtended { get; private set; }

    private Vector3 shownLocal;
    private Vector3 hiddenLocal;
    private DamageOnContact damage;
    private bool busy;
    private readonly Collider2D[] overlapResults = new Collider2D[8];
    private ContactFilter2D noFilter;

    void Awake()
    {
        if (spikes == null) { Debug.LogWarning("SpikeTrap: assign the Spikes child."); enabled = false; return; }
        shownLocal = Vector3.zero;
        hiddenLocal = (Vector3)hiddenOffset;
        damage = spikes.GetComponent<DamageOnContact>();
        noFilter = new ContactFilter2D();
        noFilter.NoFilter();
    }

    void Start()
    {
        switch (mode)
        {
            case Mode.Static:
                SetInstant(true);
                break;
            case Mode.Timed:
                SetInstant(startExtended);
                StartCoroutine(TimedLoop());
                break;
            case Mode.Triggered:
                SetInstant(false);
                if (triggerZone != null) triggerZone.isTrigger = true;
                break;
        }
    }

    IEnumerator TimedLoop()
    {
        yield return new WaitForSeconds(startDelay);
        while (true)
        {
            if (IsExtended)
            {
                yield return new WaitForSeconds(outTime);
                yield return Move(false);
            }
            else
            {
                yield return new WaitForSeconds(Mathf.Max(0f, inTime - warningTime));
                yield return Warn();
                yield return Move(true);
            }
        }
    }

    // Triggered mode: relay from the trigger zone. The zone needs a tiny forwarder —
    // we handle it by checking overlaps ourselves in Update to avoid extra scripts.
    void Update()
    {
        if (mode != Mode.Triggered || triggerZone == null || busy || IsExtended) return;
        int n = triggerZone.Overlap(noFilter, overlapResults);   // Unity 2022+/6 API (older: OverlapCollider)
        for (int i = 0; i < n; i++)
            if (overlapResults[i] != null && overlapResults[i].CompareTag("Player")) { StartCoroutine(TriggeredPop()); break; }
    }

    IEnumerator TriggeredPop()
    {
        busy = true;
        yield return Warn();
        yield return Move(true);
        yield return new WaitForSeconds(retractAfter);
        yield return Move(false);
        if (!rearm) enabled = false;
        busy = false;
    }

    IEnumerator Warn()
    {
        if (!string.IsNullOrEmpty(warningSfx)) AudioManager.Instance?.PlaySFX(warningSfx);
        float t = 0f;
        Vector3 basePos = spikes.localPosition;
        while (t < warningTime)
        {
            t += Time.deltaTime;
            spikes.localPosition = basePos + (Vector3)Random.insideUnitCircle * shakeAmount;
            yield return null;
        }
        spikes.localPosition = basePos;
    }

    IEnumerator Move(bool extend)
    {
        Vector3 target = extend ? shownLocal : hiddenLocal;
        if (extend)
        {
            if (damage) damage.enabled = true;
            if (!string.IsNullOrEmpty(popSfx)) AudioManager.Instance?.PlaySFX(popSfx);
        }
        while (Vector3.Distance(spikes.localPosition, target) > 0.01f)
        {
            spikes.localPosition = Vector3.MoveTowards(spikes.localPosition, target, moveSpeed * Time.deltaTime);
            yield return null;
        }
        spikes.localPosition = target;
        IsExtended = extend;
        if (!extend && damage) damage.enabled = false;
        if (extend) OnExtended?.Invoke(); else OnRetracted?.Invoke();
    }

    void SetInstant(bool extended)
    {
        spikes.localPosition = extended ? shownLocal : hiddenLocal;
        IsExtended = extended;
        if (damage) damage.enabled = extended;
    }
}