using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System.Collections;
using System.Collections.Generic;

/// <summary>
/// CashSystemUI — Event-driven HUD for Cash System mode.
/// Subscribes to GameEvents; never polls via FindObjectOfType.
/// Includes: timer, vault bars, carried cash, kill feed, combo popup, vault attack warning, countdown.
/// </summary>
public class CashSystemUI : MonoBehaviour
{
    [Header("Timer Display")]
    [SerializeField] private TextMeshProUGUI timerText;

    [Header("Vault Displays")]
    [SerializeField] private TextMeshProUGUI playerVaultText;
    [SerializeField] private TextMeshProUGUI enemyVaultText;
    [SerializeField] private Slider playerVaultBar;
    [SerializeField] private Slider enemyVaultBar;

    [Header("Carried Cash Display")]
    [SerializeField] private TextMeshProUGUI carriedCashText;
    [SerializeField] private GameObject carriedCashPanel;

    [Header("Character Switch Indicator")]
    [SerializeField] private TextMeshProUGUI currentCharacterText;
    [SerializeField] private Image[] teamCharacterIcons;

    [Header("Steal Progress")]
    [SerializeField] private GameObject stealProgressPanel;
    [SerializeField] private Slider stealProgressBar;
    [SerializeField] private TextMeshProUGUI stealProgressText;

    [Header("Steal Dial (optional)")]
    [Tooltip("Optional sprite to use for the steal progress radial dial. If left empty the code will try to copy the import UI's dial sprite at runtime.")]
    [SerializeField] private Sprite stealDialSprite;
    [Header("Countdown Display")]
    [SerializeField] private TextMeshProUGUI countdownText;
    [SerializeField] private GameObject countdownPanel;

    [Header("Kill Feed")]
    [SerializeField] private TextMeshProUGUI killFeedText;
    [SerializeField] private int maxFeedEntries = 5;

    [Header("Combo Popup")]
    [SerializeField] private TextMeshProUGUI comboText;
    [SerializeField] private GameObject comboPanel;

    [Header("Popup Message")]
    [SerializeField] private TextMeshProUGUI popupText;
    [SerializeField] private GameObject popupPanel;

    [Header("Pickup Screen Effect")]
    [Tooltip("Full-screen flash image (stretch to fill, starts transparent)")]
    [SerializeField] private Image pickupFlashImage;
    [Tooltip("Color of the screen flash when picking up cash")]
    [SerializeField] private Color pickupFlashColor = new Color(1f, 0.84f, 0f, 0.35f); // Gold
    [Tooltip("How long the flash lasts")]
    [SerializeField] private float pickupFlashDuration = 0.3f;
    [Tooltip("Text popup showing collected amount (e.g. '+$100')")]
    [SerializeField] private TextMeshProUGUI pickupAmountText;
    [SerializeField] private GameObject pickupAmountPanel;

    [Header("Win Target")]
    [SerializeField] private float targetCash = 2000f;

    // ─── Runtime ─────────────────────────────────────────────
    private readonly List<string> _feedEntries = new List<string>();
    private Coroutine _comboFade;
    private Coroutine _popupFade;
    private Coroutine _pickupFlashCoroutine;
    private float _originalTimerFontSize = -1f;
    private bool _dirtyVault;

    // Cached score values (set by events, read in LateUpdate for dirty-flag refresh)
    private float _playerScore, _enemyScore;

    // Cached carrier reference — invalidated on character switch
    private CashCarrier _cachedCarrier;
    private GameObject _cachedCarrierCharacter;
    private bool _stealWasActive;

    // CanvasGroup for intro delay — hidden until intro panel finishes
    private CanvasGroup _canvasGroup;

    // ─── Lifecycle ───────────────────────────────────────────

    private void Start()
    {
        if (SelectionOptions.Instance == null || !SelectionOptions.Instance.isCashSystemSelected)
        {
            gameObject.SetActive(false);
            return;
        }

        // Set up CanvasGroup so we can hide during intro without disabling the script
        _canvasGroup = GetComponent<CanvasGroup>();
        if (_canvasGroup == null) _canvasGroup = gameObject.AddComponent<CanvasGroup>();

        // Hide the HUD during the intro panel — CashSystemManager will show it after intro completes
        _canvasGroup.alpha = 0f;
        _canvasGroup.blocksRaycasts = false;
        _canvasGroup.interactable = false;

        if (playerVaultBar != null) playerVaultBar.maxValue = targetCash;
        if (enemyVaultBar != null) enemyVaultBar.maxValue = targetCash;
        if (stealProgressPanel != null) stealProgressPanel.SetActive(false);
        ConfigureStealDial();
        if (comboPanel != null) comboPanel.SetActive(false);
        if (popupPanel != null) popupPanel.SetActive(false);
        
        // Explicitly hide carried cash panel and clear text on start
        HideCarried();
        if (carriedCashText != null) carriedCashText.text = "";
    }

    /// <summary>Show the HUD (called by CashSystemManager after intro completes).</summary>
    public void ShowHUD()
    {
        if (_canvasGroup == null) return;
        _canvasGroup.alpha = 1f;
        _canvasGroup.blocksRaycasts = true;
        _canvasGroup.interactable = true;
        Debug.Log("[CashSystemUI] HUD shown");
    }

    /// <summary>
    /// Configure the steal progress dial to match the import UI's dial if possible.
    /// Tries in this order:
    /// 1) If `stealDialSprite` is assigned in inspector, use that.
    /// 2) If an ImportUIPanel exists in the scene (created by CashBoxImportController), copy its ProgressDial sprite.
    /// 3) If we have a Slider with a fill Rect, apply the sprite as a radial Image.
    /// </summary>
    private void ConfigureStealDial()
    {
        if (stealProgressBar == null) return;

        Sprite spriteToUse = stealDialSprite;

        // Try to find the runtime ImportUIPanel created by CashBoxImportController
        if (spriteToUse == null)
        {
            var importPanel = GameObject.Find("ImportUIPanel");
            if (importPanel != null)
            {
                var dial = importPanel.transform.Find("ProgressDial");
                if (dial != null)
                {
                    var img = dial.GetComponent<Image>();
                    if (img != null) spriteToUse = img.sprite;
                }
            }
        }

        if (spriteToUse == null) return;

        // Apply sprite to the Slider's fill image if available
        var fillRect = stealProgressBar.fillRect;
        if (fillRect != null)
        {
            var fillImg = fillRect.GetComponent<Image>();
            if (fillImg != null)
            {
                fillImg.sprite = spriteToUse;
                fillImg.type = Image.Type.Filled;
                fillImg.fillMethod = Image.FillMethod.Radial360;
                fillImg.fillOrigin = (int)Image.Origin360.Top;
                fillImg.fillClockwise = true;
                fillImg.preserveAspect = true;
            }
        }

        // COPY ADDITIONAL VISUAL SETTINGS FROM IMPORT UI
        var importPanelObj = GameObject.Find("ImportUIPanel");
        if (importPanelObj != null)
        {
            // Copy panel background color if stealProgressPanel has an Image
            var importImage = importPanelObj.GetComponent<Image>();
            if (importImage != null && stealProgressPanel != null)
            {
                var stealPanelImg = stealProgressPanel.GetComponent<Image>();
                if (stealPanelImg != null)
                {
                    stealPanelImg.color = importImage.color;
                }
            }

            // Copy font/size/color from Import's AmountText to our stealProgressText
            var amountTf = importPanelObj.transform.Find("AmountText");
            if (amountTf != null && stealProgressText != null)
            {
                var importAmountTmp = amountTf.GetComponent<TextMeshProUGUI>();
                if (importAmountTmp != null)
                {
                    // Copy common TMP settings
                    stealProgressText.font = importAmountTmp.font;
                    stealProgressText.fontSize = importAmountTmp.fontSize;
                    stealProgressText.color = importAmountTmp.color;
                    stealProgressText.fontStyle = importAmountTmp.fontStyle;
                    stealProgressText.enableAutoSizing = importAmountTmp.enableAutoSizing;
                    stealProgressText.outlineWidth = importAmountTmp.outlineWidth;
                    stealProgressText.outlineColor = importAmountTmp.outlineColor;
                    stealProgressText.alignment = importAmountTmp.alignment;
                }
            }

            // Also copy countdown/status text style if present (optional)
            var countdownTf = importPanelObj.transform.Find("CountdownText");
            if (countdownTf != null && timerText != null)
            {
                var importCountdownTmp = countdownTf.GetComponent<TextMeshProUGUI>();
                if (importCountdownTmp != null)
                {
                    timerText.font = importCountdownTmp.font;
                    timerText.fontSize = importCountdownTmp.fontSize;
                    timerText.color = importCountdownTmp.color;
                }
            }
        }
    }

    private void OnEnable()
    {
        GameEvents.OnTimerUpdated     += OnTimerUpdated;
        GameEvents.OnScoreChanged     += OnScoreChanged;
        GameEvents.OnBagPickedUp      += OnBagPickedUp;
        GameEvents.OnCashDeposited    += OnCashDeposited;
        GameEvents.OnKillFeedEntry    += OnKillFeedEntry;
        GameEvents.OnComboMultiplier  += OnComboMultiplier;
        GameEvents.OnVaultUnderAttack += OnVaultUnderAttack;
        GameEvents.OnVaultDefended    += OnVaultDefended;
        GameEvents.OnCountdownTick    += OnCountdownTick;
        GameEvents.OnPopupMessage     += OnPopupMessage;
        GameEvents.OnCharacterSwitched += OnCharacterSwitched;
        GameEvents.OnCarrierKilled    += OnCarrierKilled;
        GameEvents.OnMatchEnd         += OnMatchEnd;
        GameEvents.OnBagDropped       += OnBagDropped;
    }

    private void OnDisable()
    {
        GameEvents.OnTimerUpdated     -= OnTimerUpdated;
        GameEvents.OnScoreChanged     -= OnScoreChanged;
        GameEvents.OnBagPickedUp      -= OnBagPickedUp;
        GameEvents.OnCashDeposited    -= OnCashDeposited;
        GameEvents.OnKillFeedEntry    -= OnKillFeedEntry;
        GameEvents.OnComboMultiplier  -= OnComboMultiplier;
        GameEvents.OnVaultUnderAttack -= OnVaultUnderAttack;
        GameEvents.OnVaultDefended    -= OnVaultDefended;
        GameEvents.OnCountdownTick    -= OnCountdownTick;
        GameEvents.OnPopupMessage     -= OnPopupMessage;
        GameEvents.OnCharacterSwitched -= OnCharacterSwitched;
        GameEvents.OnCarrierKilled    -= OnCarrierKilled;
        GameEvents.OnMatchEnd         -= OnMatchEnd;
        GameEvents.OnBagDropped       -= OnBagDropped;
    }

    private void LateUpdate()
    {
        // Dirty-flag: only refresh vault bars when data changed
        if (_dirtyVault)
        {
            _dirtyVault = false;
            RefreshVaultBars();
        }

        // Steal progress: check every frame only while active, otherwise throttle to ~6Hz
        if (_stealWasActive || Time.frameCount % 10 == 0)
        {
            UpdateStealProgress();
        }

        // Carried cash: event-driven via OnBagPickedUp/OnBagDropped/OnCashDeposited/OnCarrierKilled
        // Only poll at ~10Hz as a safety net for edge cases
        if (Time.frameCount % 6 == 0)
        {
            RefreshCarriedCash();
        }
    }

    // ─── Event Handlers ──────────────────────────────────────

    private void OnTimerUpdated(float remaining)
    {
        if (timerText == null) return;
        int m = Mathf.FloorToInt(remaining / 60);
        int s = Mathf.FloorToInt(remaining % 60);
        timerText.text = $"{m:00}:{s:00}";
        timerText.color = remaining <= 30f
            ? Color.Lerp(Color.red, Color.white, Mathf.PingPong(Time.time * 2, 1))
            : Color.white;
    }

    private void OnScoreChanged(string teamTag, float total)
    {
        if (teamTag == "Player") _playerScore = total;
        else _enemyScore = total;
        _dirtyVault = true;
    }

    private void OnBagPickedUp(CashBag bag, CashCarrier carrier)
    {
        // Update carrier display immediately
        RefreshCarriedCash();
        
        // Only show screen effects for the player-controlled character
        CashSystemManager mgr = CashSystemManager.Instance;
        if (mgr == null) return;
        GameObject controlled = mgr.CurrentControlledCharacter;
        if (controlled == null || carrier == null) return;
        if (carrier.gameObject != controlled) return;
        
        // Screen flash effect
        ShowPickupFlash();
        
        // Floating "+$X" amount text
        float amount = bag != null ? bag.CashValue : 0f;
        ShowPickupAmount(amount);
    }

    private void OnBagDropped(CashBag bag, Vector3 pos)
    {
        RefreshCarriedCash();
    }

    private void OnCashDeposited(string teamTag, float value, int combo)
    {
        _dirtyVault = true;
        RefreshCarriedCash();
    }

    private void OnKillFeedEntry(string killer, string action, string victim)
    {
        string entry = $"<b>{killer}</b> {action} <b>{victim}</b>";
        _feedEntries.Add(entry);
        if (_feedEntries.Count > maxFeedEntries)
            _feedEntries.RemoveAt(0);

        if (killFeedText != null)
            killFeedText.text = string.Join("\n", _feedEntries);
    }

    private void OnComboMultiplier(int multiplier)
    {
        if (comboText == null) return;
        comboText.text = $"x{multiplier} COMBO!";
        if (comboPanel != null) comboPanel.SetActive(true);
        if (_comboFade != null) StopCoroutine(_comboFade);
        _comboFade = StartCoroutine(FadePanel(comboPanel, 2f));
    }

    private void OnVaultUnderAttack(TeamVault vault)
    {
        if (vault.TeamTag != "Player") return;
        if (stealProgressPanel != null) stealProgressPanel.SetActive(true);
        if (stealProgressText != null) stealProgressText.text = "VAULT UNDER ATTACK!";
    }

    private void OnVaultDefended(TeamVault vault)
    {
        if (vault.TeamTag != "Player") return;
        if (stealProgressPanel != null) stealProgressPanel.SetActive(false);
    }

    private void OnCountdownTick(string text)
    {
        ShowCountdown(text);
    }

    private void OnPopupMessage(string message, float duration)
    {
        if (popupText != null) popupText.text = message;
        if (popupPanel != null) popupPanel.SetActive(true);
        if (_popupFade != null) StopCoroutine(_popupFade);
        _popupFade = StartCoroutine(FadePanel(popupPanel, duration));
    }

    private void OnCharacterSwitched(GameObject oldChar, GameObject newChar)
    {
        if (newChar == null) return;
        if (currentCharacterText != null)
            currentCharacterText.text = newChar.name;

        // Highlight icon
        if (teamCharacterIcons != null)
        {
            CashSystemManager mgr = CashSystemManager.Instance;
            if (mgr != null)
            {
                var team = mgr.GetTeamCharacters("Player");
                int idx = team.IndexOf(newChar);
                for (int i = 0; i < teamCharacterIcons.Length; i++)
                    if (teamCharacterIcons[i] != null)
                        teamCharacterIcons[i].color = i == idx ? Color.white : new Color(1, 1, 1, 0.5f);
            }
        }
    }

    private void OnCarrierKilled(CashCarrier carrier)
    {
        RefreshCarriedCash();
    }

    private void OnMatchEnd(string winnerTag)
    {
        // Could show final score overlay
    }

    // ─── Helpers ─────────────────────────────────────────────

    private void RefreshVaultBars()
    {
        if (playerVaultText != null) playerVaultText.text = $"${_playerScore:F0}";
        if (enemyVaultText != null) enemyVaultText.text = $"${_enemyScore:F0}";
        if (playerVaultBar != null) playerVaultBar.value = _playerScore;
        if (enemyVaultBar != null) enemyVaultBar.value = _enemyScore;
    }

    private void RefreshCarriedCash()
    {
        CashSystemManager mgr = CashSystemManager.Instance;
        if (mgr == null) { HideCarried(); return; }
        GameObject cur = mgr.CurrentControlledCharacter;
        if (cur == null) { HideCarried(); return; }
        
        // Cache CashCarrier — only re-fetch when controlled character changes
        if (_cachedCarrierCharacter != cur)
        {
            _cachedCarrierCharacter = cur;
            _cachedCarrier = cur.GetComponent<CashCarrier>();
        }
        
        if (_cachedCarrier == null || _cachedCarrier.CarriedCash < 0.01f) { HideCarried(); return; }

        if (carriedCashText != null) carriedCashText.text = $"Carrying: ${_cachedCarrier.CarriedCash:F0}";
        if (carriedCashPanel != null) carriedCashPanel.SetActive(true);
    }

    private void HideCarried()
    {
        if (carriedCashPanel != null) carriedCashPanel.SetActive(false);
    }

    private void UpdateStealProgress()
    {
        // Check player vault steal progress (uses EntityRegistry now)
        var vaults = EntityRegistry<TeamVault>.GetAll();
        TeamVault playerVault = null;
        for (int i = 0; i < vaults.Count; i++)
            if (vaults[i] != null && vaults[i].TeamTag == "Player") { playerVault = vaults[i]; break; }

        if (playerVault != null && playerVault.IsBeingStolen)
        {
            _stealWasActive = true;
            if (stealProgressPanel != null) stealProgressPanel.SetActive(true);
            if (stealProgressBar != null) stealProgressBar.value = playerVault.StealProgress;
        }
        else
        {
            _stealWasActive = false;
            if (stealProgressPanel != null) stealProgressPanel.SetActive(false);
        }
    }

    private IEnumerator FadePanel(GameObject panel, float duration)
    {
        yield return new WaitForSeconds(duration);
        if (panel != null) panel.SetActive(false);
    }

    // ─── Pickup Screen Effects ───────────────────────────────

    private void ShowPickupFlash()
    {
        if (_pickupFlashCoroutine != null) StopCoroutine(_pickupFlashCoroutine);
        
        // If no flash image assigned, create one at runtime
        if (pickupFlashImage == null)
        {
            CreatePickupFlashImage();
        }
        
        if (pickupFlashImage != null)
        {
            _pickupFlashCoroutine = StartCoroutine(PickupFlashRoutine());
        }
    }
    
    private void CreatePickupFlashImage()
    {
        Canvas canvas = GetComponentInParent<Canvas>();
        if (canvas == null) canvas = FindObjectOfType<Canvas>();
        if (canvas == null) return;
        
        GameObject flashObj = new GameObject("PickupFlash");
        flashObj.transform.SetParent(canvas.transform, false);
        
        RectTransform rt = flashObj.AddComponent<RectTransform>();
        rt.anchorMin = Vector2.zero;
        rt.anchorMax = Vector2.one;
        rt.offsetMin = Vector2.zero;
        rt.offsetMax = Vector2.zero;
        
        pickupFlashImage = flashObj.AddComponent<Image>();
        pickupFlashImage.color = Color.clear;
        pickupFlashImage.raycastTarget = false;
        
        // Make sure it renders on top
        flashObj.transform.SetAsLastSibling();
    }
    
    private IEnumerator PickupFlashRoutine()
    {
        pickupFlashImage.color = pickupFlashColor;
        
        float elapsed = 0f;
        while (elapsed < pickupFlashDuration)
        {
            elapsed += Time.deltaTime;
            float t = elapsed / pickupFlashDuration;
            pickupFlashImage.color = Color.Lerp(pickupFlashColor, Color.clear, t);
            yield return null;
        }
        
        pickupFlashImage.color = Color.clear;
    }
    
    private void ShowPickupAmount(float amount)
    {
        if (amount <= 0) return;
        
        // If no pickup amount panel, create one dynamically
        if (pickupAmountText == null)
        {
            CreatePickupAmountText();
        }
        
        if (pickupAmountText != null)
        {
            pickupAmountText.text = $"+${amount:F0}";
            if (pickupAmountPanel != null) pickupAmountPanel.SetActive(true);
            
            // Auto-hide after a short duration
            StartCoroutine(FadePickupAmount());
        }
    }
    
    private void CreatePickupAmountText()
    {
        Canvas canvas = GetComponentInParent<Canvas>();
        if (canvas == null) canvas = FindObjectOfType<Canvas>();
        if (canvas == null) return;
        
        pickupAmountPanel = new GameObject("PickupAmountPanel");
        pickupAmountPanel.transform.SetParent(canvas.transform, false);
        
        RectTransform panelRect = pickupAmountPanel.AddComponent<RectTransform>();
        panelRect.anchorMin = new Vector2(0.5f, 0.6f);
        panelRect.anchorMax = new Vector2(0.5f, 0.6f);
        panelRect.sizeDelta = new Vector2(700f, 160f);
        panelRect.anchoredPosition = Vector2.zero;
        
        GameObject textObj = new GameObject("PickupAmountText");
        textObj.transform.SetParent(pickupAmountPanel.transform, false);
        
        RectTransform textRect = textObj.AddComponent<RectTransform>();
        textRect.anchorMin = Vector2.zero;
        textRect.anchorMax = Vector2.one;
        textRect.offsetMin = Vector2.zero;
        textRect.offsetMax = Vector2.zero;
        
        pickupAmountText = textObj.AddComponent<TextMeshProUGUI>();
        pickupAmountText.fontSize = 96;
        pickupAmountText.alignment = TextAlignmentOptions.Center;
        pickupAmountText.color = new Color(1f, 0.92f, 0.23f); // Bright gold
        pickupAmountText.fontStyle = FontStyles.Bold;
        pickupAmountText.enableAutoSizing = false;
        
        // Add outline for readability
        pickupAmountText.outlineWidth = 0.35f;
        pickupAmountText.outlineColor = new Color32(0, 0, 0, 200);

        pickupAmountPanel.SetActive(false);
    }
    
    private IEnumerator FadePickupAmount()
    {
        float displayDuration = 1.2f;
        float fadeDuration = 0.4f;
        
        if (pickupAmountText == null) yield break;
        
        // Float upward animation
        RectTransform rt = pickupAmountPanel.GetComponent<RectTransform>();
        Vector2 startPos = new Vector2(0f, 0f);
        Vector2 endPos = new Vector2(0f, 50f);
        
        Color startColor = pickupAmountText.color;
        
        float elapsed = 0f;
        while (elapsed < displayDuration)
        {
            elapsed += Time.deltaTime;
            float t = elapsed / displayDuration;
            
            // Float upward
            if (rt != null)
                rt.anchoredPosition = Vector2.Lerp(startPos, endPos, t);
            
            // Fade out in last portion
            if (t > 0.6f)
            {
                float fadeT = (t - 0.6f) / 0.4f;
                pickupAmountText.color = new Color(startColor.r, startColor.g, startColor.b, 1f - fadeT);
            }
            
            yield return null;
        }
        
        // Reset
        pickupAmountText.color = startColor;
        if (rt != null) rt.anchoredPosition = startPos;
        if (pickupAmountPanel != null) pickupAmountPanel.SetActive(false);
    }

    // ─── Legacy API (called from other scripts) ──────────────

    public void UpdateTimerDisplay(float timeRemaining) => OnTimerUpdated(timeRemaining);
    public void UpdateVaultDisplay(TeamVault vault)
    {
        if (vault == null) return;
        if (vault.TeamTag == "Player") _playerScore = vault.StoredCash;
        else _enemyScore = vault.StoredCash;
        _dirtyVault = true;
    }
    public void UpdateCarriedCashDisplay(CashCarrier carrier) => RefreshCarriedCash();
    public void UpdateCurrentCharacter(string characterName, int index)
    {
        if (currentCharacterText != null) currentCharacterText.text = characterName;
    }

    public void ShowCountdown(string text)
    {
        if (countdownText != null)
            countdownText.text = text;
        if (countdownPanel != null)
            countdownPanel.SetActive(!string.IsNullOrEmpty(text));

        // Fallback: use timer text for countdown
        if (countdownText == null && timerText != null && !string.IsNullOrEmpty(text))
        {
            if (_originalTimerFontSize < 0) _originalTimerFontSize = timerText.fontSize;
            timerText.text = text;
            timerText.fontSize = 72;
        }
        else if (countdownText == null && timerText != null && string.IsNullOrEmpty(text))
        {
            if (_originalTimerFontSize > 0) timerText.fontSize = _originalTimerFontSize;
        }
    }
}
