using System.Collections;
using UnityEngine;
using UnityEngine.UI;

/// <summary>
/// Effet "ping" discret pour une épinglette UI (Option B).
/// Génère un sprite d'anneau en runtime, puis anime scale + alpha en boucle.
/// </summary>
[DisallowMultipleComponent]
public class MapPinPingEffect : MonoBehaviour
{
    [Header("Timing")]
    // Plus fréquent (1 ping / 0,5s) mais plus court pour rester discret
    [SerializeField] private float intervalSeconds = 0.5f;
    [SerializeField] private float pingDurationSeconds = 0.35f;
    [SerializeField] private Vector2 randomStartDelaySeconds = new Vector2(0f, 0.25f);

    [Header("Look")]
    [SerializeField] private float startScale = 1.0f;
    [SerializeField] private float endScale = 1.25f;
    [SerializeField, Range(0f, 1f)] private float maxAlpha = 0.10f;
    [SerializeField] private Color color = new Color(1f, 1f, 1f, 1f);

    [Header("Sprite (runtime)")]
    [SerializeField] private int textureSize = 128;      // plus = plus lisse, plus coûteux
    [SerializeField] private float ringThicknessPx = 6f; // épaisseur de l'anneau dans la texture
    [SerializeField] private float softEdgePx = 3f;      // adoucissement bord intérieur/extérieur

    private Image img;
    private Coroutine loopCo;

    private static Sprite cachedRingSprite;
    private static int cachedTextureSize;
    private static float cachedThickness;
    private static float cachedSoftEdge;

    public void ApplyConfig(MapPinPingEffectConfig cfg)
    {
        if (cfg == null) return;

        // Activation
        enabled = cfg.enabled;
        if (!cfg.enabled)
        {
            if (img != null) img.enabled = false;
            return;
        }

        // Timing
        intervalSeconds = cfg.intervalSeconds;
        pingDurationSeconds = cfg.pingDurationSeconds;
        randomStartDelaySeconds = cfg.randomStartDelaySeconds;

        // Look
        startScale = cfg.startScale;
        endScale = cfg.endScale;
        maxAlpha = cfg.maxAlpha;
        if (!string.IsNullOrEmpty(cfg.color) && ColorUtility.TryParseHtmlString(cfg.color, out var parsed))
        {
            color = parsed;
        }

        // Sprite runtime
        textureSize = cfg.textureSize;
        ringThicknessPx = cfg.ringThicknessPx;
        softEdgePx = cfg.softEdgePx;

        // Appliquer immédiatement
        if (img == null) img = GetComponent<Image>();
        if (img == null) img = gameObject.AddComponent<Image>();
        img.raycastTarget = false;
        img.enabled = true;

        EnsureSprite();
        ApplyInitialVisual();

        // Redémarrer proprement la boucle
        if (loopCo != null) StopCoroutine(loopCo);
        loopCo = StartCoroutine(Loop());
    }

    private void Awake()
    {
        img = GetComponent<Image>();
        if (img == null) img = gameObject.AddComponent<Image>();

        img.raycastTarget = false;
        img.enabled = true;

        EnsureSprite();
        ApplyInitialVisual();
    }

    private void OnEnable()
    {
        if (loopCo != null) StopCoroutine(loopCo);
        loopCo = StartCoroutine(Loop());
    }

    private void OnDisable()
    {
        if (loopCo != null)
        {
            StopCoroutine(loopCo);
            loopCo = null;
        }
    }

    private void EnsureSprite()
    {
        if (cachedRingSprite != null &&
            cachedTextureSize == textureSize &&
            Mathf.Approximately(cachedThickness, ringThicknessPx) &&
            Mathf.Approximately(cachedSoftEdge, softEdgePx))
        {
            img.sprite = cachedRingSprite;
            return;
        }

        cachedRingSprite = CreateRingSprite(textureSize, ringThicknessPx, softEdgePx);
        cachedTextureSize = textureSize;
        cachedThickness = ringThicknessPx;
        cachedSoftEdge = softEdgePx;
        img.sprite = cachedRingSprite;
    }

    private void ApplyInitialVisual()
    {
        transform.localScale = Vector3.one * startScale;
        var c = color;
        c.a = 0f;
        img.color = c;
    }

    private IEnumerator Loop()
    {
        float delay = Random.Range(randomStartDelaySeconds.x, randomStartDelaySeconds.y);
        if (delay > 0f) yield return new WaitForSeconds(delay);

        while (enabled && gameObject.activeInHierarchy)
        {
            yield return StartCoroutine(PingOnce());
            yield return new WaitForSeconds(Mathf.Max(0.05f, intervalSeconds));
        }
    }

    private IEnumerator PingOnce()
    {
        float t = 0f;
        float duration = Mathf.Max(0.05f, pingDurationSeconds);

        while (t < duration)
        {
            t += Time.deltaTime;
            float u = Mathf.Clamp01(t / duration);

            // easing (smoothstep) pour un rendu "premium"
            float eased = u * u * (3f - 2f * u);

            float s = Mathf.Lerp(startScale, endScale, eased);
            transform.localScale = Vector3.one * s;

            // alpha: pic au début puis fade out
            float a = Mathf.Lerp(maxAlpha, 0f, eased);
            var c = color;
            c.a = a;
            img.color = c;

            yield return null;
        }

        // reset propre entre 2 pings
        ApplyInitialVisual();
    }

    private static Sprite CreateRingSprite(int size, float thicknessPx, float softEdgePx)
    {
        size = Mathf.Clamp(size, 32, 512);
        thicknessPx = Mathf.Clamp(thicknessPx, 1f, size / 4f);
        softEdgePx = Mathf.Clamp(softEdgePx, 0f, size / 4f);

        var tex = new Texture2D(size, size, TextureFormat.RGBA32, false);
        tex.wrapMode = TextureWrapMode.Clamp;
        tex.filterMode = FilterMode.Bilinear;

        float cx = (size - 1) * 0.5f;
        float cy = (size - 1) * 0.5f;
        float rOuter = (size * 0.5f) - 1f;
        float rInner = Mathf.Max(0f, rOuter - thicknessPx);

        // Soft edges: on fait une transition alpha autour de rInner et rOuter
        float in0 = rInner - softEdgePx;
        float in1 = rInner + softEdgePx;
        float out0 = rOuter - softEdgePx;
        float out1 = rOuter + softEdgePx;

        var pixels = new Color32[size * size];

        for (int y = 0; y < size; y++)
        {
            for (int x = 0; x < size; x++)
            {
                float dx = x - cx;
                float dy = y - cy;
                float d = Mathf.Sqrt(dx * dx + dy * dy);

                // alpha ring = montée près de rInner puis descente près de rOuter
                float aIn = SoftStep(in0, in1, d);
                float aOut = 1f - SoftStep(out0, out1, d);
                float a = Mathf.Clamp01(aIn * aOut);

                byte alpha = (byte)Mathf.RoundToInt(a * 255f);
                pixels[y * size + x] = new Color32(255, 255, 255, alpha);
            }
        }

        tex.SetPixels32(pixels);
        tex.Apply(false, false);

        var sprite = Sprite.Create(tex, new Rect(0, 0, size, size), new Vector2(0.5f, 0.5f), 100f);
        sprite.name = "Runtime_RingSprite";
        return sprite;
    }

    private static float SoftStep(float edge0, float edge1, float x)
    {
        if (Mathf.Approximately(edge0, edge1)) return x >= edge1 ? 1f : 0f;
        float t = Mathf.Clamp01((x - edge0) / (edge1 - edge0));
        return t * t * (3f - 2f * t);
    }
}


