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

/// <summary>
/// CashoutStation — Deposit point for extraction vaults.
///
/// FLOW: Carrier deposits vault → 30% instant payout → 45s defense timer for 70% → 
///       Enemy can contest (pause timer) or hack (10s to steal cashout).
///
/// Replaces the role of TeamVault as the primary deposit zone in extraction mode.
/// TeamVault still exists for backwards compatibility in the old bag-collection mode.
///
/// Registers with EntityRegistry<CashoutStation> for zero-allocation queries.
/// All notifications go through GameEvents.
/// </summary>
public class CashoutStation : MonoBehaviour
{
    public enum StationState { Idle, CashingOut, Contested, Hacked, Completed }

    [Header("Team Settings")]
    [SerializeField] private string teamTag = "Player";     // "Player" or "Enemy"
    [SerializeField] private float storedCash = 0f;         // Total team cash (all sources)

    [Header("Cashout Settings")]
    [SerializeField] private float instantPayoutPercent = 0.30f;  // 30% on deposit
    [SerializeField] private float cashoutDuration = 45f;          // 45s to cash out the remaining 70%
    [SerializeField] private float hackDuration = 10f;             // 10s for enemy to steal a cashout
    [SerializeField] private float contestPauseDelay = 2f;         // Seconds before contest pauses timer

    [Header("Interaction")]
    [SerializeField] private float depositRange = 4f;             // Range to deposit vault
    [SerializeField] private float defenseRadius = 12f;           // Area to defend during cashout

    [Header("Visual/Audio")]
    [SerializeField] private GameObject depositEffect;
    [SerializeField] private GameObject cashoutActiveEffect;
    [SerializeField] private GameObject contestedEffect;
    [SerializeField] private GameObject hackedEffect;
    [SerializeField] private ParticleSystem idleParticles;
    [SerializeField] private AudioClip depositSound;
    [SerializeField] private AudioClip cashoutCompleteSound;
    [SerializeField] private AudioClip hackedSound;

    [Header("Events (Inspector)")]
    public UnityEvent<float> OnCashChanged;
    public UnityEvent OnCashoutStartedLocal;
    public UnityEvent<float> OnCashoutProgressLocal;
    public UnityEvent OnCashoutCompletedLocal;
    public UnityEvent OnContestedLocal;
    public UnityEvent OnHackedLocal;

    // ─── Runtime ─────────────────────────────────────────────
    private StationState _state = StationState.Idle;
    private float _cashoutProgress;                          // 0-1
    private float _cashoutRemainingValue;                    // The 70% being cashed out
    private float _hackProgress;                             // 0-1
    private string _cashoutOwnerTeamTag;                     // Team that deposited the vault
    private Coroutine _cashoutCoroutine;
    private Coroutine _hackCoroutine;
    private readonly HashSet<GameObject> _contestingEnemies = new HashSet<GameObject>();
    private ExtractionVault _currentVault;                   // Vault being cashed out

    // ─── Properties ──────────────────────────────────────────
    public float StoredCash => storedCash;
    public string TeamTag => teamTag;
    public StationState CurrentState => _state;
    public float CashoutProgress => _cashoutProgress;
    public float HackProgress => _hackProgress;
    public float CashoutRemainingValue => _cashoutRemainingValue;
    public bool IsCashingOut => _state == StationState.CashingOut || _state == StationState.Contested;
    public bool IsIdle => _state == StationState.Idle || _state == StationState.Completed;
    public float DefenseRadius => defenseRadius;

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

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

    private void OnEnable()
    {
        EntityRegistry<CashoutStation>.Register(this);
    }

    private void OnDisable()
    {
        EntityRegistry<CashoutStation>.Unregister(this);
        StopAllCoroutines();
    }

    // ─── Trigger Detection ───────────────────────────────────

    private void OnTriggerEnter(Collider other)
    {
        GameObject character = GetCharacterRoot(other);
        if (character == null) return;

        HealthNew hp = character.GetComponent<HealthNew>();
        if (hp != null && hp.isDead) return;

        bool isSameTeam = character.CompareTag(teamTag);

        if (isSameTeam)
        {
            // Same team: try to deposit a carried vault
            TryDepositVault(character);
        }
        else
        {
            // Enemy team: contest or hack an active cashout
            if (IsCashingOut)
            {
                _contestingEnemies.Add(character);
                if (_contestingEnemies.Count == 1) // First contester
                    OnContestStart(character);
            }
        }
    }

    private void OnTriggerStay(Collider other)
    {
        // Check if enemy is still alive while contesting
        if (!IsCashingOut) return;

        GameObject character = GetCharacterRoot(other);
        if (character == null) return;
        if (character.CompareTag(teamTag)) return; // Same team, ignore

        if (_contestingEnemies.Contains(character))
        {
            HealthNew hp = character.GetComponent<HealthNew>();
            if (hp != null && hp.isDead)
            {
                _contestingEnemies.Remove(character);
                if (_contestingEnemies.Count == 0)
                    OnContestEnd();
            }
        }
    }

    private void OnTriggerExit(Collider other)
    {
        GameObject character = GetCharacterRoot(other);
        if (character == null) return;

        if (_contestingEnemies.Remove(character))
        {
            if (_contestingEnemies.Count == 0)
                OnContestEnd();
        }
    }

    // ─── Deposit ─────────────────────────────────────────────

    /// <summary>Try to deposit a vault carried by this character.</summary>
    private void TryDepositVault(GameObject character)
    {
        // Find if this character is carrying an ExtractionVault
        var vaults = EntityRegistry<ExtractionVault>.GetAll();
        ExtractionVault carriedVault = null;

        for (int i = 0; i < vaults.Count; i++)
        {
            if (vaults[i] != null && vaults[i].IsCarried && vaults[i].Carrier == character)
            {
                carriedVault = vaults[i];
                break;
            }
        }

        if (carriedVault == null) return;
        if (!IsIdle) return; // Can't deposit while another cashout is in progress

        DepositVault(carriedVault, character);
    }

    /// <summary>Deposit a vault and start the cashout timer.</summary>
    public void DepositVault(ExtractionVault vault, GameObject depositor)
    {
        if (vault == null || depositor == null) return;

        // Remove vault from world
        vault.OnDeposited();
        _currentVault = vault;
        _cashoutOwnerTeamTag = depositor.tag;

        // Calculate payouts
        float remainingValue = vault.RemainingValue; // 70% of total vault value
        float instantPayout = remainingValue * instantPayoutPercent; // 30% of remaining = 21% of total
        // Actually the design says 30% instant on deposit, 70% over timer
        // So: instant = totalValue * 0.30, timer = totalValue * 0.70
        // But openReward ($1000) was already awarded, so:
        // remainingValue = totalValue - openReward = $9,000
        // instant = remainingValue * 0.30 = $2,700
        // timer = remainingValue * 0.70 = $6,300
        instantPayout = remainingValue * instantPayoutPercent;
        _cashoutRemainingValue = remainingValue - instantPayout;

        // Award instant payout
        storedCash += instantPayout;

        if (depositEffect != null)
            Instantiate(depositEffect, transform.position + Vector3.up, Quaternion.identity);

        PlaySound(depositSound);

        OnCashChanged?.Invoke(storedCash);
        GameEvents.FireScoreChanged(teamTag, storedCash);
        GameEvents.FireCashoutStarted(this, teamTag, instantPayout);
        GameEvents.FireKillFeedEntry(depositor.name, "DEPOSITED VAULT", $"+${instantPayout:F0} instant");
        GameEvents.FirePopupMessage($"CASHOUT STARTED! +${instantPayout:F0}", 2f);

        DevLog.Log($"[CashoutStation] {depositor.name} deposited vault. Instant: ${instantPayout:F0}, Timer: ${_cashoutRemainingValue:F0} over {cashoutDuration}s");

        // Start cashout timer
        _cashoutCoroutine = StartCoroutine(CashoutRoutine());
    }

    private IEnumerator CashoutRoutine()
    {
        _state = StationState.CashingOut;
        _cashoutProgress = 0f;

        if (cashoutActiveEffect != null)
            cashoutActiveEffect.SetActive(true);

        float elapsed = 0f;
        float totalToCashout = _cashoutRemainingValue;

        while (elapsed < cashoutDuration)
        {
            // Pause while contested
            while (_state == StationState.Contested)
            {
                yield return null;
            }

            // Abort if hacked
            if (_state == StationState.Hacked)
                yield break;

            elapsed += Time.deltaTime;
            _cashoutProgress = Mathf.Clamp01(elapsed / cashoutDuration);

            // Drip cash over time
            float cashThisTick = (totalToCashout / cashoutDuration) * Time.deltaTime;
            storedCash += cashThisTick;

            // Fire progress events (throttled to ~4Hz)
            if (Time.frameCount % 15 == 0)
            {
                GameEvents.FireCashoutProgress(this, _cashoutProgress);
                OnCashoutProgressLocal?.Invoke(_cashoutProgress);
                GameEvents.FireScoreChanged(teamTag, storedCash);
            }

            yield return null;
        }

        // Cashout complete!
        CompleteCashout();
    }

    private void CompleteCashout()
    {
        _state = StationState.Completed;
        _cashoutProgress = 1f;

        if (cashoutActiveEffect != null)
            cashoutActiveEffect.SetActive(false);

        PlaySound(cashoutCompleteSound);

        OnCashoutCompletedLocal?.Invoke();
        GameEvents.FireCashoutCompleted(this, teamTag, _cashoutRemainingValue);
        GameEvents.FireScoreChanged(teamTag, storedCash);
        GameEvents.FireKillFeedEntry(teamTag, "CASHED OUT", $"${_cashoutRemainingValue:F0}!");
        GameEvents.FirePopupMessage("CASHOUT COMPLETE!", 3f);

        DevLog.Log($"[CashoutStation] Cashout complete! Total team cash: ${storedCash:F0}");

        // Reset to idle after a short delay
        StartCoroutine(ResetToIdleDelayed(3f));
    }

    private IEnumerator ResetToIdleDelayed(float delay)
    {
        yield return new WaitForSeconds(delay);
        _state = StationState.Idle;
        _currentVault = null;
        _cashoutRemainingValue = 0f;
        _cashoutProgress = 0f;
    }

    // ─── Contest / Hack ──────────────────────────────────────

    private void OnContestStart(GameObject contester)
    {
        if (_state != StationState.CashingOut) return;

        _state = StationState.Contested;

        if (contestedEffect != null)
            contestedEffect.SetActive(true);

        OnContestedLocal?.Invoke();
        GameEvents.FireCashoutContested(this, contester.tag);
        GameEvents.FirePopupMessage("CASHOUT CONTESTED!", 1.5f);

        DevLog.Log($"[CashoutStation] Cashout contested by {contester.name}!");

        // Start hack timer — if enemy stays long enough, they steal the cashout
        _hackCoroutine = StartCoroutine(HackRoutine(contester));
    }

    private void OnContestEnd()
    {
        if (_state != StationState.Contested) return;

        // Cancel hack if in progress
        if (_hackCoroutine != null)
        {
            StopCoroutine(_hackCoroutine);
            _hackCoroutine = null;
        }
        _hackProgress = 0f;

        if (contestedEffect != null)
            contestedEffect.SetActive(false);

        _state = StationState.CashingOut;
        DevLog.Log($"[CashoutStation] Contest ended, cashout resumed");
    }

    private IEnumerator HackRoutine(GameObject hacker)
    {
        _hackProgress = 0f;
        float elapsed = 0f;

        while (elapsed < hackDuration)
        {
            if (hacker == null) { OnContestEnd(); yield break; }

            HealthNew hp = hacker.GetComponent<HealthNew>();
            if (hp != null && hp.isDead) { OnContestEnd(); yield break; }

            // Check hacker is still in range
            float dist = Vector3.Distance(hacker.transform.position, transform.position);
            if (dist > defenseRadius) { OnContestEnd(); yield break; }

            elapsed += Time.deltaTime;
            _hackProgress = Mathf.Clamp01(elapsed / hackDuration);
            yield return null;
        }

        // Hack successful!
        CompleteHack(hacker);
    }

    private void CompleteHack(GameObject hacker)
    {
        _state = StationState.Hacked;

        // Stop the cashout coroutine
        if (_cashoutCoroutine != null)
        {
            StopCoroutine(_cashoutCoroutine);
            _cashoutCoroutine = null;
        }

        // Calculate stolen amount (remaining uncashed value)
        float stolen = _cashoutRemainingValue * (1f - _cashoutProgress);

        // Remove from this team
        storedCash -= stolen;
        storedCash = Mathf.Max(storedCash, 0f);

        // Award to hacking team
        string hackerTeamTag = hacker.tag;
        AwardToTeam(hackerTeamTag, stolen);

        if (hackedEffect != null)
            hackedEffect.SetActive(true);
        if (cashoutActiveEffect != null)
            cashoutActiveEffect.SetActive(false);
        if (contestedEffect != null)
            contestedEffect.SetActive(false);

        PlaySound(hackedSound);

        OnHackedLocal?.Invoke();
        GameEvents.FireCashoutHacked(this, hackerTeamTag, stolen);
        GameEvents.FireScoreChanged(teamTag, storedCash);
        GameEvents.FireScoreChanged(hackerTeamTag, GetTeamCash(hackerTeamTag));
        GameEvents.FireKillFeedEntry(hacker.name, "HACKED CASHOUT", $"STOLE ${stolen:F0}!");
        GameEvents.FirePopupMessage($"CASHOUT HACKED! ${stolen:F0} STOLEN!", 3f);

        DevLog.Log($"[CashoutStation] HACKED by {hacker.name}! ${stolen:F0} stolen to {hackerTeamTag}");

        // Reset to idle
        StartCoroutine(ResetToIdleDelayed(3f));
    }

    // ─── Direct Cash API ─────────────────────────────────────

    /// <summary>Add cash directly (kill rewards, vault open reward, etc.).</summary>
    public void AddDirectCash(float amount)
    {
        storedCash += amount;
        OnCashChanged?.Invoke(storedCash);
        GameEvents.FireScoreChanged(teamTag, storedCash);
    }

    /// <summary>Remove cash (team wipe penalty, etc.).</summary>
    public void RemoveCash(float amount)
    {
        storedCash -= amount;
        storedCash = Mathf.Max(storedCash, 0f);
        OnCashChanged?.Invoke(storedCash);
        GameEvents.FireScoreChanged(teamTag, storedCash);
    }

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

    private void AwardToTeam(string targetTeamTag, float amount)
    {
        var stations = EntityRegistry<CashoutStation>.GetAll();
        for (int i = 0; i < stations.Count; i++)
        {
            if (stations[i] != null && stations[i].teamTag == targetTeamTag)
            {
                stations[i].AddDirectCash(amount);
                return;
            }
        }

        // Fallback to TeamVault
        var vaults = EntityRegistry<TeamVault>.GetAll();
        for (int i = 0; i < vaults.Count; i++)
        {
            if (vaults[i] != null && vaults[i].TeamTag == targetTeamTag)
            {
                vaults[i].DepositCash(amount);
                return;
            }
        }
    }

    private float GetTeamCash(string targetTeamTag)
    {
        var stations = EntityRegistry<CashoutStation>.GetAll();
        for (int i = 0; i < stations.Count; i++)
        {
            if (stations[i] != null && stations[i].teamTag == targetTeamTag)
                return stations[i].storedCash;
        }
        return 0f;
    }

    private GameObject GetCharacterRoot(Collider col)
    {
        TeamMember tm = col.GetComponentInParent<TeamMember>();
        if (tm != null) return tm.gameObject;

        CashCarrier carrier = col.GetComponentInParent<CashCarrier>();
        if (carrier != null) return carrier.gameObject;

        return null;
    }

    private void PlaySound(AudioClip clip)
    {
        if (clip == null) return;
        Vector3 pos = Camera.main != null ? Camera.main.transform.position : transform.position;
        AudioSource.PlayClipAtPoint(clip, pos, 2f);
    }

    /// <summary>Get the opposing station via EntityRegistry.</summary>
    public CashoutStation GetOpposingStation()
    {
        var stations = EntityRegistry<CashoutStation>.GetAll();
        for (int i = 0; i < stations.Count; i++)
        {
            if (stations[i] != null && stations[i].teamTag != this.teamTag)
                return stations[i];
        }
        return null;
    }

    // ─── Gizmos ──────────────────────────────────────────────

    private void OnDrawGizmos()
    {
        Gizmos.color = teamTag == "Player" ? new Color(0, 0.5f, 1f, 0.3f) : new Color(1, 0.2f, 0.2f, 0.3f);
        Gizmos.DrawSphere(transform.position, 2f);

        Gizmos.color = new Color(1, 1, 0, 0.1f);
        Gizmos.DrawWireSphere(transform.position, defenseRadius);

#if UNITY_EDITOR
        string stateLabel = _state switch
        {
            StationState.CashingOut => $"CASHING OUT {_cashoutProgress:P0}",
            StationState.Contested => $"CONTESTED! Hack:{_hackProgress:P0}",
            StationState.Hacked => "HACKED!",
            StationState.Completed => "COMPLETE",
            _ => "IDLE"
        };
        UnityEditor.Handles.Label(transform.position + Vector3.up * 3f,
            $"{teamTag} CashoutStation\n${storedCash:F0}\n{stateLabel}");
#endif
    }
}
