using System;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System.Collections;
using DG.Tweening;
using UniRx;
using Random = System.Random;

public class HUDManager : MonoBehaviour
{
    public static HUDManager Instance { get; private set; }

    [Header("References")]
    public UIManager uiManager;           
    public GrandmaAI grandmaAI;           
    public CatAI CatAI;
    
    [Header("Health UI")]
    public Image[] heartIcons;             
    public Sprite heartFull;
    public Sprite heartEmpty;

    [Header("Sanity UI")]
    public Image[] sanityIcons;           
    public Sprite sanityFull;
    public Sprite sanityEmpty;

    [Header("Balancing")]
    public TextMeshProUGUI timerText;
    public int startTimeSeconds = 40;     
    public int sanityRecoveryTime = 25;
    public int heartDrainTime = 10;
    public bool infiniteLevel = false;
    [Header("Tooltip")]
    public GameObject tooltipPanel;
    public TextMeshProUGUI tooltipText;

    [Header("Tooltip Follow")]
    public Vector2 tooltipOffset = new Vector2(0f, -15f);
    bool tooltipActive = false;
    
    [Header("Final Action Cinematic")]
    public bool usePaintingFinale = false;
    public GameObject Sparks; 
    public Transform paintingCameraPoint;
    public Camera mainCamera;
    
    public bool IsSanityZero => sanity == 0;

    int health = 3;
    int sanity = 3;
    int timer;
    
    [Header("Fullscreen Shader")]
    public Material fullscreenMaterial;   // material used in Renderer2D
    public float blendMaxValue = 0.05f;
    public float blendLerpSpeed = 2f;

    [Header("Grandma Reaction")]
    [SerializeField] public GrandmaReactionVisual grandmaReaction;
    
    float currentBlend = 0f;
    float targetBlend = 0f;

    static readonly int BlendID = Shader.PropertyToID("_Blend");
    
    float sanityRecoveryTimer = 200f;
    float heartDrainTimer = 0f;

    // Configuration - adjust these values for each digit
    public float secondsOnesInterval = 0.02f;
    public float secondsTensInterval = 0.03f;
    public float minutesOnesInterval = 0.04f;
    public float minutesTensInterval = 0.05f;
    
    private CompositeDisposable timerDisposables = new CompositeDisposable();
    
    private void Awake()
    {
        if (Instance != null && Instance != this)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
    }
    void OnDestroy()
    {
        if (fullscreenMaterial != null)
            fullscreenMaterial.SetFloat(BlendID, 0f);
        Debug.Log("HUDManager destroyed, cleaning up timer disposables.");
    }


    void Start()
    {
        timer = startTimeSeconds;
        UpdateHealthUI();
        UpdateSanityUI();
        UpdateTimerUI();

        tooltipPanel.SetActive(false);
        
        // initialize shader state
        if (fullscreenMaterial != null)
        {
            fullscreenMaterial.SetFloat(BlendID, 0f);
            currentBlend = 0f;
            targetBlend = 0f;
        }


        if (!infiniteLevel)
        {
            StartCoroutine(TimerCountdown());   
        }
        else
        {
            // Timer for rightmost seconds digit (ones place)
            Observable
                .Interval(TimeSpan.FromSeconds(secondsOnesInterval))
                .Subscribe(_ => UpdateDigit(0))
                .AddTo(timerDisposables);

            // Timer for leftmost seconds digit (tens place)
            Observable
                .Interval(TimeSpan.FromSeconds(secondsTensInterval))
                .Subscribe(_ => UpdateDigit(1))
                .AddTo(timerDisposables);

            // Timer for rightmost minutes digit (ones place)
            Observable
                .Interval(TimeSpan.FromSeconds(minutesOnesInterval))
                .Subscribe(_ => UpdateDigit(2))
                .AddTo(timerDisposables);

            // Timer for leftmost minutes digit (tens place)
            Observable
                .Interval(TimeSpan.FromSeconds(minutesTensInterval))
                .Subscribe(_ => UpdateDigit(3))
                .AddTo(timerDisposables);

            ShakeIcon(timerText.transform);

            foreach (var healthIcon in heartIcons)
            {
                ShakeIcon(healthIcon.transform);
            }

            foreach (var sanityIcon in sanityIcons)
            {
                ShakeIcon(sanityIcon.transform);
            }
            
        }
    }
    
    void ShakeIcon(Transform icon)
    {
        icon.DOShakePosition(0.5f, UnityEngine.Random.Range(4f, 6f), 10, UnityEngine.Random.Range(0f, 90f))
            .SetLoops(-1, LoopType.Yoyo)
            .OnStepComplete(() => {
                icon.DOKill();
                ShakeIcon(icon);
            })
            .SetUpdate(true);
    }

    void Update()
    {
        // sanity auto-recover (only when not full)
        if (sanity < sanityIcons.Length)
        {
            sanityRecoveryTimer += Time.deltaTime;
            if (sanityRecoveryTimer >= sanityRecoveryTime)
            {
                sanityRecoveryTimer = 0f;
                AddSanity(1);
            }
        }
        else
        {
            sanityRecoveryTimer = 0f; // stay reset while full
        }

        // drain hearts when sanity = 0
        if (sanity == 0)
        {
            heartDrainTimer += Time.deltaTime;
            if (heartDrainTimer >= heartDrainTime)
            {
                heartDrainTimer = 0f;
                AddHealth(-1);
            }
        }
        else
        {
            heartDrainTimer = 0f;
        }
        if (tooltipActive)
        {
            // follow mouse
            Vector2 mousePos;
            RectTransformUtility.ScreenPointToLocalPointInRectangle(
                tooltipPanel.transform.parent as RectTransform,
                Input.mousePosition,
                null,
                out mousePos
            );

            tooltipPanel.GetComponent<RectTransform>().anchoredPosition = mousePos + tooltipOffset;
        }
        
        if (fullscreenMaterial != null)
        {
            currentBlend = Mathf.Lerp(
                currentBlend,
                targetBlend,
                Time.deltaTime * blendLerpSpeed
            );

            fullscreenMaterial.SetFloat(BlendID, currentBlend);
        }
        
    }

    // health 
    public void AddHealth(int amount)
    {
        Debug.Log($"Health changed by {amount}. New health: {health + amount}");
        health = Mathf.Clamp(health + amount, 0, heartIcons.Length);
        UpdateHealthUI();
        //TODO different reaction for health loss
        //grandmaReaction.ShowExclamation();

        if (health <= 0)
        {
            Debug.Log("Player has lost all health. Game Over!");
            uiManager.ShowLose();
        }
    }

    void UpdateHealthUI()
    {
        for (int i = 0; i < heartIcons.Length; i++)
        {
            heartIcons[i].sprite = (i < health) ? heartFull : heartEmpty;
        }
    }

    // sanity 
    public void AddSanity(int amount)
    {
        if (amount < 0)
            sanityRecoveryTimer = 0f; // start recovery from zero

        sanity = Mathf.Clamp(sanity + amount, 0, sanityIcons.Length);
        UpdateSanityUI();
        bool sanityDecreased = amount < 0;

        if (sanityDecreased && grandmaReaction != null)
        {
            grandmaReaction.ShowExclamation();
        }
    }

    void UpdateSanityUI()
    {
        for (int i = 0; i < sanityIcons.Length; i++)
        {
            sanityIcons[i].sprite = (i < sanity) ? sanityFull : sanityEmpty;
        }
        
        // update shader target blend
        if (sanity >= 2)
            targetBlend = 0f;
        else if (sanity == 1)
            targetBlend = blendMaxValue * 0.1f;
        else // sanity == 0
            targetBlend = blendMaxValue;
        
    }

    // timer
    IEnumerator TimerCountdown()
    {
        bool cinematicTriggered = false;
        while (timer > 0)
        {
            yield return new WaitForSeconds(1f);
            if(infiniteLevel)
            {
                if(timer == 1)
                {
                    timer = startTimeSeconds;
                }
            }
            timer--;
            // trigger cinematic shortly before timer ends
            if (usePaintingFinale && timer <= 3 && !cinematicTriggered)
            {
                cinematicTriggered = true;
                StartFinalCinematic();
            }
            
            UpdateTimerUI();
        }

        // timer reached 0
        grandmaAI.TriggerFinalAction();
        if (CatAI != null)
            CatAI.TriggerFinalAction();
    }
    
        
    void StartFinalCinematic()
    {
        //reaction grandma
        if (grandmaReaction != null)
        {
            grandmaReaction.ShowExclamation();
        }
        if (Sparks != null)
            Sparks.SetActive(true);

        if (mainCamera != null && paintingCameraPoint != null)
        {
            //disable camera movement
            mainCamera.GetComponent<CameraMovement>().enabled = false;
            // pan the camera slowly over 2 seconds interpolated
            StartCoroutine(PanCameraToPainting());

        }
    }
    
    // pan camera coroutine
    IEnumerator PanCameraToPainting()
    {
        Vector3 startPos = mainCamera.transform.position;
        Vector3 endPos = paintingCameraPoint.position;

        float duration = 2f;
        float elapsed = 0f;

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

            float x = Mathf.Lerp(startPos.x, endPos.x, t);
            float y = Mathf.Lerp(startPos.y, endPos.y, t);

            mainCamera.transform.position = new Vector3(
                x,
                y,
                startPos.z // keep original Z
            );

            yield return null;
        }
    }

    
    private void UpdateDigit(int digitIndex)
    {
        Random rand = new Random();

        string[] timeParts = timerText.text.Split(':');
        if (timeParts.Length != 2) return;

        string minutes = timeParts[0];
        string seconds = timeParts[1];

        switch (digitIndex)
        {
            case 0: // rightmost seconds digit
                if (seconds.Length >= 2)
                    seconds = seconds.Substring(0, 1) + rand.Next(0, 10);
                else if (seconds.Length == 1)
                    seconds = rand.Next(0, 10).ToString();
                break;
            case 1: // leftmost seconds digit
                if (seconds.Length >= 2)
                    seconds = rand.Next(0, 6) + seconds.Substring(1);
                else
                    seconds = rand.Next(0, 6).ToString();
                break;
            case 2: // rightmost minutes digit
                if (minutes.Length >= 2)
                    minutes = minutes.Substring(0, minutes.Length - 1) + rand.Next(0, 10);
                else if (minutes.Length == 1)
                    minutes = rand.Next(0, 10).ToString();
                break;
            case 3: // leftmost minutes digit
                if (minutes.Length >= 2)
                    minutes = rand.Next(0, 10) + minutes.Substring(1);
                else
                    minutes = (rand.Next(0, 10) + int.Parse(minutes)).ToString();
                break;
        }

        timerText.text = minutes + ":" + seconds;
    }
    
    
    void UpdateTimerUI()
    {
        int min = timer / 60;
        int sec = timer % 60;
        timerText.text = $"{min:00}:{sec:00}";
    }

    // tooltip 
    public void ShowTooltip(string message)
    {
        tooltipText.text = message;
        tooltipPanel.SetActive(true);
        tooltipActive = true;
    }

    public void HideTooltip()
    {
        tooltipPanel.SetActive(false);
        tooltipActive = false;
    }
    void OnDisable()
    {
#if UNITY_EDITOR
        if (!Application.isPlaying && fullscreenMaterial != null)
        {
            fullscreenMaterial.SetFloat(BlendID, 0f);
        }
#endif
    }
    
}