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

/// <summary>
/// TeamVault — Deposit zone for team cash with steal mechanic.
/// Uses GameEvents for all notifications (no FindObjectOfType in hot paths).
/// EntityRegistry for fast lookups. Magnet zone for auto-deposit at close range.
/// </summary>
public class TeamVault : MonoBehaviour
{
    [Header("Team Settings")]
    [SerializeField] private string teamTag = "Player"; // "Player" or "Enemy"
    [SerializeField] private float storedCash = 0f;

    [Header("Interaction Settings")]
    [SerializeField] private float depositTime = 1.5f;
    [SerializeField] private float stealTime = 3f;
    [SerializeField] private float stealAmount = 100f;
    [SerializeField] private float magnetRange = 2.5f;      // Auto-deposit range
    [SerializeField] private float magnetInterval = 0.3f;    // How often to check magnet

    [Header("Visual/Audio")]
    [SerializeField] private GameObject depositEffect;
    [SerializeField] private GameObject stealEffect;
    [SerializeField] private ParticleSystem activeParticles;
    
    [Header("Import / Spawn")]
    [SerializeField] private GameObject importCashBagPrefab;
    [SerializeField] private bool spawnImportOnStart = false;
    [SerializeField] private int importCount = 1;
    
    [Header("CashBox Import Animation")]
    [Tooltip("Enable animated import with countdown, camera focus, and sounds")]
    [SerializeField] private bool useAnimatedImport = true;
    [Tooltip("CashBox prefab to spawn at vault (has Animator with 'close' animation)")]
    [SerializeField] private GameObject cashBoxPrefab;
    [SerializeField] private Vector3 cashBoxOffset = Vector3.zero;
    [SerializeField] private Vector3 cashBoxRotation = new Vector3(-90f, 0f, 0f);
    [SerializeField] private Vector3 cashBoxScale = new Vector3(60f, 60f, 60f);

    [Header("Events (Inspector)")]
    public UnityEvent<float> OnCashDepositedLocal;
    public UnityEvent<float> OnCashStolenLocal;
    public UnityEvent OnVaultUnderAttackLocal;

    // ─── Runtime ─────────────────────────────────────────────
    private bool _isBeingStolen;
    private CashCarrier _currentThief;
    private float _stealProgress;
    private Coroutine _stealCoroutine;
    private Coroutine _magnetCoroutine;
    private readonly HashSet<CashCarrier> _depositingCarriers = new HashSet<CashCarrier>();
    private CashBoxImportController _importController;

    public float StoredCash => storedCash;
    public string TeamTag => teamTag;
    public bool IsBeingStolen => _isBeingStolen;
    public float StealProgress => _stealProgress;

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

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

        _magnetCoroutine = StartCoroutine(MagnetPulseRoutine());

        // Optional: spawn imported cash bags at vault position
        if (spawnImportOnStart && importCashBagPrefab != null)
        {
            for (int i = 0; i < Mathf.Max(1, importCount); i++)
            {
                SpawnImportedBag(importCashBagPrefab);
            }
        }
        
        // Setup CashBox import controller if animated import is enabled
        if (useAnimatedImport && cashBoxPrefab != null)
        {
            SetupImportController();
        }
    }
    
    private void SetupImportController()
    {
        _importController = GetComponent<CashBoxImportController>();
        if (_importController == null)
            _importController = gameObject.AddComponent<CashBoxImportController>();
        
        // Pass the TeamVault's prefab and transform settings to the controller
        _importController.Configure(cashBoxPrefab, cashBoxOffset, cashBoxRotation, cashBoxScale);
        
        // Spawn the CashBox above the vault
        _importController.SpawnCashBox();
    }

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

    private void OnDisable()
    {
        EntityRegistry<TeamVault>.Unregister(this);
        if (_magnetCoroutine != null) StopCoroutine(_magnetCoroutine);
    }

    // ─── Trigger ─────────────────────────────────────────────

    private void OnTriggerEnter(Collider other)
    {
        CashCarrier carrier = other.GetComponentInParent<CashCarrier>();
        if (carrier == null) return;

        // Same team → deposit
        if (carrier.TeamTag == teamTag)
        {
            if (carrier.CarriedCash > 0 && !_depositingCarriers.Contains(carrier))
                StartCoroutine(DepositRoutine(carrier));
        }
        // Enemy team → steal
        else
        {
            if (storedCash > 0 && !_isBeingStolen)
                StartStealing(carrier);
        }
    }

    private void OnTriggerExit(Collider other)
    {
        CashCarrier carrier = other.GetComponentInParent<CashCarrier>();
        if (carrier == null) return;

        if (carrier == _currentThief)
            CancelSteal();
    }

    // ─── Magnet Auto-Deposit ─────────────────────────────────

    private IEnumerator MagnetPulseRoutine()
    {
        var wait = new WaitForSeconds(magnetInterval);
        while (true)
        {
            yield return wait;
            var carriers = EntityRegistry<CashCarrier>.GetAll();
            for (int i = 0; i < carriers.Count; i++)
            {
                var c = carriers[i];
                if (c == null || c.CarriedCash <= 0 || c.TeamTag != teamTag) continue;
                if (_depositingCarriers.Contains(c)) continue;
                if (Vector3.Distance(c.transform.position, transform.position) <= magnetRange)
                    StartCoroutine(DepositRoutine(c));
            }
        }
    }

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

    private IEnumerator DepositRoutine(CashCarrier carrier)
    {
        _depositingCarriers.Add(carrier);
        
        // Check if this is a player and we should use animated import
        bool isPlayer = carrier.CompareTag("Player") || carrier.gameObject.layer == LayerMask.NameToLayer("Player");
        bool useImport = useAnimatedImport && _importController != null && isPlayer;
        
        if (useImport && _importController.CanImport(carrier))
        {
            // Use animated import with countdown, camera focus, sounds
            _importController.StartImport(carrier);
            
            // Wait for import to complete
            while (_importController.IsImporting)
                yield return null;
        }
        else
        {
            // Standard deposit flow (for AI or when import controller unavailable)
            yield return new WaitForSeconds(depositTime);

            if (carrier != null && carrier.CarriedCash > 0)
            {
                // Let carrier handle combo scoring via DepositToVault
                carrier.DepositToVault(this);

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

        _depositingCarriers.Remove(carrier);
    }

    /// <summary>Direct deposit (called by CashCarrier.DepositToVault or legacy code).</summary>
    public void DepositCash(float amount)
    {
        storedCash += amount;

        OnCashDepositedLocal?.Invoke(amount);

        // Fire global events instead of FindObjectOfType
        GameEvents.FireScoreChanged(teamTag, storedCash);
        GameEvents.FireKillFeedEntry(teamTag, "deposited", $"${amount:F0}");
    }

    // ─── Steal ───────────────────────────────────────────────

    private void StartStealing(CashCarrier thief)
    {
        if (_isBeingStolen) return;

        _isBeingStolen = true;
        _currentThief = thief;
        _stealProgress = 0f;

        OnVaultUnderAttackLocal?.Invoke();
        GameEvents.FireVaultUnderAttack(this);

        _stealCoroutine = StartCoroutine(StealRoutine(thief));
    }

    private IEnumerator StealRoutine(CashCarrier thief)
    {
        float elapsed = 0f;

        while (elapsed < stealTime)
        {
            if (thief == null || !thief.enabled)
            {
                CancelSteal();
                yield break;
            }

            HealthNew health = thief.GetComponent<HealthNew>();
            if (health != null && health.isDead)
            {
                CancelSteal();
                yield break;
            }

            elapsed += Time.deltaTime;
            _stealProgress = elapsed / stealTime;
            yield return null;
        }

        CompleteSteal(thief);
    }

    private void CompleteSteal(CashCarrier thief)
    {
        if (thief == null) return;

        float stolen = Mathf.Min(stealAmount, storedCash);
        storedCash -= stolen;
        thief.AddCash(stolen);

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

        OnCashStolenLocal?.Invoke(stolen);

        _isBeingStolen = false;
        _currentThief = null;
        _stealProgress = 0f;

        // Fire global events — replaces FindObjectOfType<CashSystemManager/CashSystemUI>
        GameEvents.FireVaultStolen(this, stolen);
        GameEvents.FireScoreChanged(teamTag, storedCash);
        GameEvents.FireKillFeedEntry(thief.gameObject.name, "stole", $"${stolen:F0}");
    }

    private void CancelSteal()
    {
        if (_stealCoroutine != null)
            StopCoroutine(_stealCoroutine);

        _isBeingStolen = false;
        _currentThief = null;
        _stealProgress = 0f;

        GameEvents.FireVaultDefended(this);
    }

    /// <summary>Force interrupt steal (e.g., when thief takes damage).</summary>
    public void InterruptSteal()
    {
        CancelSteal();
    }

    /// <summary>
    /// Spawn an imported cash bag at this vault's position. Uses PoolManager if available.
    /// Returns the spawned CashBag GameObject or null.
    /// </summary>
    public GameObject SpawnImportedBag(GameObject prefab)
    {
        if (prefab == null) return null;

        GameObject obj = null;
        // Use PoolManager if present
        var poolManagerType = typeof(PoolManager);
        try
        {
            obj = PoolManager.Instance != null ? PoolManager.Instance.Spawn(prefab, transform.position + Vector3.up * 0.5f, Quaternion.identity) : null;
        }
        catch
        {
            obj = null;
        }

        if (obj == null)
        {
            obj = Instantiate(prefab, transform.position + Vector3.up * 0.5f, Quaternion.identity);
        }

        var bag = obj.GetComponent<CashBag>();
        if (bag != null)
        {
            bag.SetCashValue(bag.CashValue);
            bag.Drop(transform.position); // Ensure bag is in dropped/available state
        }

        return obj;
    }

    /// <summary>Get the opposing vault via EntityRegistry (no FindObjectsOfType).</summary>
    public TeamVault GetOpposingVault()
    {
        var vaults = EntityRegistry<TeamVault>.GetAll();
        for (int i = 0; i < vaults.Count; i++)
        {
            if (vaults[i] != null && vaults[i].teamTag != this.teamTag)
                return vaults[i];
        }
        return null;
    }

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

        Gizmos.color = new Color(1, 1, 0, 0.15f);
        Gizmos.DrawSphere(transform.position, magnetRange);

        #if UNITY_EDITOR
        UnityEditor.Handles.Label(transform.position + Vector3.up * 3f, $"{teamTag} Vault\n${storedCash}");
        #endif
    }
}
