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

/// <summary>
/// CashCarrier — Attach to characters to track carried cash bags.
/// Tracks actual CashBag references (not just float), handles stacking
/// speed penalty, combo multiplier on deposit, death drops.
/// Uses GameEvents instead of FindObjectOfType.
/// </summary>
public class CashCarrier : MonoBehaviour
{
    [Header("Carry Settings")]
    [SerializeField] private int maxBags = 3;

    [Header("Movement Penalty")]
    [SerializeField] private float singleBagPenalty = 0.15f;      // -15% speed per bag
    [SerializeField] private float multiBagExtraPenalty = 0.10f;   // Additional -10% for 2+ bags
    [SerializeField] private float maxSpeedReduction = 0.40f;      // Never slower than 60% speed
    
    [Header("Death Drop")]
    [SerializeField] private float cashDropPercent = 1.0f;         // Drop 100% on death (all bags scatter)
    [SerializeField] private GameObject droppedCashPrefab;         // Optional: legacy prefab for dropped cash
    
    [Header("Visual Indicators")]
    [SerializeField] private GameObject carryingIndicator;
    [SerializeField] private ParticleSystem cashParticles;
    
    [Header("Events")]
    public UnityEvent<float> OnCashChanged;
    public UnityEvent<float> OnCashDropped;

    // ─── Runtime ─────────────────────────────────────────────
    private readonly List<CashBag> _carriedBags = new List<CashBag>(4);
    private float _legacyCash = 0f;                // Backwards compat: cash from old-style AddCash(float)
    private float _originalSpeed = -1f;
    private CharacterAIController _aiController;
    private HealthNew _health;
    private Animator _animator;
    private bool _isDead = false;

    // ─── Combo Tracking ──────────────────────────────────────
    private float _lastDepositTime;
    private int _comboCount;
    private const float COMBO_WINDOW = 10f;         // Seconds between deposits to maintain combo

    // ─── Properties ──────────────────────────────────────────
    public int BagCount => _carriedBags.Count;
    public int BoxCount => BagCount;                    // Legacy alias
    public float TotalValue => CarriedCash;             // Alias for AI compatibility
    public float CarriedCash
    {
        get
        {
            float total = _legacyCash;
            for (int i = 0; i < _carriedBags.Count; i++)
                if (_carriedBags[i] != null) total += _carriedBags[i].GetEffectiveValue();
            return total;
        }
    }
    public bool IsCarrying => _carriedBags.Count > 0 || _legacyCash > 0;
    public string TeamTag => gameObject.tag;
    public float SpeedMultiplier { get; private set; } = 1f;
    public int ComboCount => _comboCount;
    public List<CashBag> CarriedBags => _carriedBags;
    public List<CashBag> CarriedBoxes => _carriedBags;    // Legacy alias

    /// <summary>True when this character is carrying an ExtractionVault (managed by ExtractionVault, not CashCarrier).</summary>
    public bool IsCarryingVault
    {
        get
        {
            var vaults = EntityRegistry<ExtractionVault>.GetAll();
            for (int i = 0; i < vaults.Count; i++)
                if (vaults[i] != null && vaults[i].IsCarried && vaults[i].Carrier == gameObject)
                    return true;
            return false;
        }
    }

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

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

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

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

        // ── Explicit reset — guarantees 0 cash on every spawn/reuse ──
        _carriedBags.Clear();
        _legacyCash = 0f;
        _isDead = false;

        _aiController = GetComponent<CharacterAIController>();
        _health = GetComponent<HealthNew>();
        _animator = GetComponent<Animator>();

        if (_aiController != null)
            _originalSpeed = _aiController.GetSpeed();

        UpdateVisuals();
        OnCashChanged?.Invoke(0f);   // Tell listeners we start at zero
    }

    private void Update()
    {
        // Death check
        if (_health != null && _health.isDead && !_isDead)
        {
            _isDead = true;
            OnDeath();
        }
        else if (_health != null && !_health.isDead)
        {
            _isDead = false;
        }

        ApplyMovementPenalty();
    }

    // ─── Bag Management ─────────────────────────────────────

    /// <summary>Can this carrier pick up more bags?</summary>
    public bool CanPickUp() => _carriedBags.Count < maxBags && !_isDead;

    /// <summary>Called by CashBag when picked up.</summary>
    public void OnBagPickedUp(CashBag bag)
    {
        if (bag == null || _carriedBags.Contains(bag)) return;

        _carriedBags.Add(bag);
        UpdateVisuals();
        OnCashChanged?.Invoke(CarriedCash);

        GameEvents.FireKillFeedEntry(gameObject.name, "picked up", $"Cash (${bag.CashValue})");
    }

    /// <summary>Legacy alias for OnBagPickedUp.</summary>
    public void OnBoxPickedUp(CashBag bag) => OnBagPickedUp(bag);

    /// <summary>Drop all carried bags.</summary>
    public void DropAllBags()
    {
        if (_carriedBags.Count == 0) return;

        Vector3 dropPos = transform.position;
        float totalDropped = 0f;

        for (int i = _carriedBags.Count - 1; i >= 0; i--)
        {
            CashBag bag = _carriedBags[i];
            if (bag != null)
            {
                // Scatter bags slightly around drop point
                Vector3 offset = Random.insideUnitSphere * 1.5f;
                offset.y = 0;
                bag.Drop(dropPos + offset);
                totalDropped += bag.CashValue;
            }
        }

        _carriedBags.Clear();
        UpdateVisuals();
        OnCashDropped?.Invoke(totalDropped);
        OnCashChanged?.Invoke(CarriedCash);
    }

    /// <summary>Legacy alias for DropAllBags.</summary>
    public void DropAllBoxes() => DropAllBags();

    /// <summary>Deposit all to vault. Returns total value deposited.</summary>
    public float DepositToVault(TeamVault vault)
    {
        if (vault == null) return 0f;
        if (_carriedBags.Count == 0 && _legacyCash <= 0) return 0f;

        // Calculate combo
        if (Time.time - _lastDepositTime <= COMBO_WINDOW)
            _comboCount++;
        else
            _comboCount = 1;
        _lastDepositTime = Time.time;

        float totalValue = 0f;
        int comboMultiplier = Mathf.Min(_comboCount, 3); // Cap at 3x

        // Deposit tracked bags
        for (int i = _carriedBags.Count - 1; i >= 0; i--)
        {
            CashBag bag = _carriedBags[i];
            if (bag != null)
            {
                float value = bag.GetEffectiveValue();
                switch (comboMultiplier)
                {
                    case 2: value *= 1.5f; break;
                    case 3: value *= 2.0f; break;
                }
                totalValue += value;
                bag.Despawn();
            }
        }
        _carriedBags.Clear();

        // Deposit any legacy cash too
        if (_legacyCash > 0)
        {
            float legacyValue = _legacyCash;
            switch (comboMultiplier)
            {
                case 2: legacyValue *= 1.5f; break;
                case 3: legacyValue *= 2.0f; break;
            }
            totalValue += legacyValue;
            _legacyCash = 0f;
        }

        vault.DepositCash(totalValue);
        UpdateVisuals();
        OnCashChanged?.Invoke(0f);

        // Fire events
        GameEvents.FireCashDeposited(TeamTag, totalValue, comboMultiplier);
        if (comboMultiplier > 1)
            GameEvents.FireComboMultiplier(comboMultiplier);

        GameEvents.FireKillFeedEntry(
            gameObject.name,
            comboMultiplier > 1 ? $"SCORED {comboMultiplier}x" : "scored",
            $"${totalValue:F0}"
        );

        return totalValue;
    }

    // ─── Legacy API (backwards compat with old CashBag/CashSystemAI) ─

    /// <summary>Add cash directly (legacy float-based API).</summary>
    public void AddCash(float amount)
    {
        _legacyCash += amount;
        UpdateVisuals();
        OnCashChanged?.Invoke(CarriedCash);
    }

    /// <summary>Remove cash amount (legacy API).</summary>
    public float RemoveCash(float amount)
    {
        float removed = Mathf.Min(amount, _legacyCash);
        _legacyCash -= removed;
        UpdateVisuals();
        OnCashChanged?.Invoke(CarriedCash);
        return removed;
    }

    /// <summary>Remove all cash and return amount (legacy API).</summary>
    public float RemoveAllCash()
    {
        float removed = CarriedCash;
        _legacyCash = 0f;
        _carriedBags.Clear();
        UpdateVisuals();
        OnCashChanged?.Invoke(0f);
        return removed;
    }

    // ─── Death ───────────────────────────────────────────────

    private void OnDeath()
    {
        // Fire character died for respawn system (always, even if not carrying)
        GameEvents.FireCharacterDied(gameObject);

        if (!IsCarrying) return;

        GameEvents.FireCarrierKilled(this);

        // Drop tracked bags (scatter around)
        DropAllBags();

        // Drop legacy cash as a physical pickup
        if (_legacyCash > 0 && droppedCashPrefab != null)
        {
            float dropAmount = _legacyCash * cashDropPercent;
            GameObject dropped = Instantiate(droppedCashPrefab, transform.position + Vector3.up, Quaternion.identity);
            CashBag droppedBag = dropped.GetComponent<CashBag>();
            if (droppedBag != null)
                droppedBag.SetCashValue(dropAmount);
            _legacyCash -= dropAmount;
            OnCashDropped?.Invoke(dropAmount);
        }
    }

    // ─── Movement Penalty ────────────────────────────────────

    private void ApplyMovementPenalty()
    {
        int count = _carriedBags.Count;
        if (count == 0 && _legacyCash <= 0)
        {
            SpeedMultiplier = 1f;
        }
        else if (count == 0)
        {
            // Legacy cash — simple penalty
            float penaltyFactor = Mathf.Clamp01(_legacyCash / 500f);
            SpeedMultiplier = 1f - (singleBagPenalty * penaltyFactor);
        }
        else
        {
            // Stacking penalty: -15% per bag, -10% extra for 2+
            float penalty = singleBagPenalty * count;
            if (count >= 2)
                penalty += multiBagExtraPenalty * (count - 1);
            penalty = Mathf.Min(penalty, maxSpeedReduction);
            SpeedMultiplier = 1f - penalty;
        }

        // Apply to AI controller (skip if carrying ExtractionVault — vault manages speed directly)
        if (_aiController != null && _originalSpeed > 0 && !IsCarryingVault)
            _aiController.SetSpeed(_originalSpeed * SpeedMultiplier);

        // Apply to animator
        if (_animator != null)
        {
            if (IsCarrying)
            {
                float currentSpeed = _animator.GetFloat("Speed");
                if (currentSpeed > 0) _animator.speed = SpeedMultiplier;
            }
            else
            {
                _animator.speed = 1f;
            }
        }
    }

    // ─── Visuals ─────────────────────────────────────────────

    private void UpdateVisuals()
    {
        bool carrying = IsCarrying;

        if (carryingIndicator != null)
            carryingIndicator.SetActive(carrying);

        if (cashParticles != null)
        {
            if (carrying && !cashParticles.isPlaying)
                cashParticles.Play();
            else if (!carrying && cashParticles.isPlaying)
                cashParticles.Stop(true, ParticleSystemStopBehavior.StopEmitting);
        }
    }

    // ─── Queries ─────────────────────────────────────────────

    /// <summary>Should AI deposit? (has bags or enough cash)</summary>
    public bool ShouldDeposit(float threshold = 200f)
    {
        return _carriedBags.Count >= 1 || _legacyCash >= threshold;
    }

    /// <summary>Check if carrying a specific bag.</summary>
    public bool IsCarryingBag(CashBag bag) => _carriedBags.Contains(bag);

    /// <summary>Legacy alias for IsCarryingBag.</summary>
    public bool IsCarryingBox(CashBag bag) => IsCarryingBag(bag);

    // ─── Cleanup ─────────────────────────────────────────────

    private void OnDestroy()
    {
        if (_originalSpeed >= 0 && _aiController != null)
            _aiController.SetSpeed(_originalSpeed);
        if (_animator != null) _animator.speed = 1f;
    }
}
