using UnityEngine;
using UnityEngine.AI;
using System.Collections;

/// <summary>
/// ExtractionVault — The carryable vault objective for The Finals-style extraction mode.
///
/// FLOW: Sealed → Opened ($1,000 instant) → Carried (encumbered) → Deposited at CashoutStation
///
/// When a character interacts with a sealed vault, it "opens" over a short channel,
/// awarding $1,000 immediately. The opener (or any teammate) can then pick it up.
/// While carried: 40% speed reduction, no sprint, no attack.
/// On carrier death: vault drops at death position (anyone can grab it).
///
/// Registers with EntityRegistry<ExtractionVault> for zero-allocation queries.
/// All notifications go through GameEvents.
/// </summary>
public class ExtractionVault : MonoBehaviour
{
    public enum VaultState { Sealed, Opening, Opened, Carried, Dropped, Deposited }

    [Header("Value")]
    [SerializeField] private float totalValue = 10000f;        // Total vault worth
    [SerializeField] private float openReward = 1000f;         // Instant reward on open

    [Header("Interaction")]
    [SerializeField] private float openTime = 3f;              // Seconds to open the vault
    [SerializeField] private float interactRange = 3f;         // Range to interact/open
    [SerializeField] private float pickupRange = 2.5f;         // Range to pick up opened vault
    [SerializeField] private float dropDespawnTime = 60f;      // Dropped vaults persist 60s before respawning elsewhere

    [Header("Encumbrance (while carried)")]
    [SerializeField] private float carrySpeedMultiplier = 0.60f; // 40% speed reduction
    [SerializeField] private bool disableAttackWhileCarrying = true;
    [SerializeField] private bool disableSprintWhileCarrying = true;

    [Header("Visual")]
    [SerializeField] private float rotationSpeed = 15f;
    [SerializeField] private float bobSpeed = 0.8f;
    [SerializeField] private float bobHeight = 0.15f;
    [SerializeField] private float floatOffset = 0.5f;
    [SerializeField] private GameObject sealedVisual;           // Visual when sealed
    [SerializeField] private GameObject openedVisual;           // Visual when opened/available
    [SerializeField] private ParticleSystem openEffect;
    [SerializeField] private ParticleSystem glowEffect;
    [SerializeField] private ParticleSystem dropImpactEffect;

    [Header("Audio")]
    [SerializeField] private AudioClip openSound;
    [SerializeField] private AudioClip pickupSound;
    [SerializeField] private AudioClip dropSound;
    [SerializeField] private float soundVolume = 2f;

    // ─── Runtime ─────────────────────────────────────────────
    private VaultState _state = VaultState.Sealed;
    private GameObject _carrier;                    // Who is carrying this vault
    private string _openerTeamTag;                  // Team that opened the vault
    private float _openProgress;                    // 0-1 opening progress
    private Coroutine _openCoroutine;
    private float _dropTime;
    private float _despawnTimer;
    private Vector3 _spawnPosition;
    private Collider _collider;
    private float _bobPhase;
    private float _originalCarrierSpeed = -1f;

    // ─── Properties ──────────────────────────────────────────
    public VaultState State => _state;
    public float TotalValue => totalValue;
    public float OpenReward => openReward;
    public float RemainingValue => totalValue - openReward;    // What goes through the cashout station
    public bool IsSealed => _state == VaultState.Sealed;
    public bool IsOpened => _state == VaultState.Opened;
    public bool IsCarried => _state == VaultState.Carried;
    public bool IsDropped => _state == VaultState.Dropped;
    public bool IsAvailableForPickup => _state == VaultState.Opened || _state == VaultState.Dropped;
    public GameObject Carrier => _carrier;
    public string OpenerTeamTag => _openerTeamTag;
    public float OpenProgress => _openProgress;
    public Vector3 SpawnPosition => _spawnPosition;

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

    private void Awake()
    {
        _collider = GetComponent<Collider>();
        if (_collider != null) _collider.isTrigger = true;
        _bobPhase = Random.Range(0f, Mathf.PI * 2f);
    }

    private void OnEnable()
    {
        EntityRegistry<ExtractionVault>.Register(this);
        _spawnPosition = transform.position;
        SetSealed();
    }

    private void OnDisable()
    {
        EntityRegistry<ExtractionVault>.Unregister(this);
        // If we're carrying when disabled, release the carrier
        if (_carrier != null)
            ReleaseCarrier();
    }

    private void Update()
    {
        switch (_state)
        {
            case VaultState.Sealed:
            case VaultState.Opened:
                AnimateIdle();
                break;

            case VaultState.Carried:
                // Follow carrier
                if (_carrier != null)
                {
                    transform.position = _carrier.transform.position + Vector3.up * 1.8f;
                    transform.rotation = _carrier.transform.rotation;
                }
                else
                {
                    // Carrier destroyed — drop
                    Drop(transform.position);
                }
                break;

            case VaultState.Dropped:
                AnimateIdle();
                _despawnTimer += Time.deltaTime;
                if (_despawnTimer >= dropDespawnTime)
                {
                    // Respawn elsewhere via VaultSpawner
                    Despawn();
                }
                break;
        }
    }

    // ─── State Transitions ───────────────────────────────────

    /// <summary>Reset to sealed state.</summary>
    public void SetSealed()
    {
        _state = VaultState.Sealed;
        _carrier = null;
        _openerTeamTag = null;
        _openProgress = 0f;
        _despawnTimer = 0f;

        if (_collider != null) _collider.enabled = true;
        gameObject.SetActive(true);

        if (sealedVisual != null) sealedVisual.SetActive(true);
        if (openedVisual != null) openedVisual.SetActive(false);
        if (glowEffect != null) glowEffect.Play();
    }

    /// <summary>
    /// Start opening the vault. Takes openTime seconds.
    /// Awards $1,000 instant reward to the opener's team on completion.
    /// </summary>
    public void StartOpening(GameObject opener)
    {
        if (_state != VaultState.Sealed) return;
        if (opener == null) return;

        if (_openCoroutine != null) StopCoroutine(_openCoroutine);
        _openCoroutine = StartCoroutine(OpenRoutine(opener));
    }

    /// <summary>Cancel an in-progress open (e.g., opener moved away or got hit).</summary>
    public void CancelOpening()
    {
        if (_openCoroutine != null)
        {
            StopCoroutine(_openCoroutine);
            _openCoroutine = null;
        }
        _openProgress = 0f;
        if (_state == VaultState.Opening)
            _state = VaultState.Sealed;
    }

    private IEnumerator OpenRoutine(GameObject opener)
    {
        _state = VaultState.Opening;
        _openProgress = 0f;

        float elapsed = 0f;
        while (elapsed < openTime)
        {
            // Abort if opener died or moved too far
            if (opener == null) { CancelOpening(); yield break; }

            HealthNew openerHP = opener.GetComponent<HealthNew>();
            if (openerHP != null && openerHP.isDead) { CancelOpening(); yield break; }

            float dist = Vector3.Distance(opener.transform.position, transform.position);
            if (dist > interactRange * 1.5f) { CancelOpening(); yield break; }

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

        // Successfully opened!
        CompleteOpen(opener);
    }

    private void CompleteOpen(GameObject opener)
    {
        _state = VaultState.Opened;
        _openProgress = 1f;
        _openerTeamTag = opener.tag;
        _openCoroutine = null;

        // Visual swap
        if (sealedVisual != null) sealedVisual.SetActive(false);
        if (openedVisual != null) openedVisual.SetActive(true);
        if (openEffect != null) openEffect.Play();

        // Sound
        PlaySound(openSound);

        // Award $1,000 instant reward to the opener's team vault
        AwardOpenReward(opener);

        // Fire global event
        GameEvents.FireVaultOpened(this, opener);
        GameEvents.FireKillFeedEntry(opener.name, "CRACKED", $"VAULT (${openReward})");
        GameEvents.FirePopupMessage($"VAULT OPENED! +${openReward}", 2f);

        DevLog.Log($"[ExtractionVault] {opener.name} opened vault at {transform.position}, awarded ${openReward} to {opener.tag}");
    }

    private void AwardOpenReward(GameObject opener)
    {
        // Find the team's vault (TeamVault/CashoutStation) and deposit directly
        string teamTag = opener.tag;
        var stations = EntityRegistry<CashoutStation>.GetAll();
        for (int i = 0; i < stations.Count; i++)
        {
            if (stations[i] != null && stations[i].TeamTag == teamTag)
            {
                stations[i].AddDirectCash(openReward);
                GameEvents.FireScoreChanged(teamTag, stations[i].StoredCash);
                return;
            }
        }

        // Fallback: use legacy TeamVault
        var vaults = EntityRegistry<TeamVault>.GetAll();
        for (int i = 0; i < vaults.Count; i++)
        {
            if (vaults[i] != null && vaults[i].TeamTag == teamTag)
            {
                vaults[i].DepositCash(openReward);
                return;
            }
        }
    }

    /// <summary>Pick up the opened/dropped vault. Applies encumbrance to carrier.</summary>
    public void PickUp(GameObject carrier)
    {
        if (!IsAvailableForPickup || carrier == null) return;

        _state = VaultState.Carried;
        _carrier = carrier;
        _despawnTimer = 0f;

        // Disable collider while carried
        if (_collider != null) _collider.enabled = false;
        if (glowEffect != null) glowEffect.Stop();

        // Apply encumbrance
        ApplyEncumbrance(carrier);

        PlaySound(pickupSound);

        GameEvents.FireVaultPickedUp(this, carrier);
        GameEvents.FireKillFeedEntry(carrier.name, "GRABBED", "VAULT");

        DevLog.Log($"[ExtractionVault] {carrier.name} picked up vault");
    }

    /// <summary>Drop the vault at a world position.</summary>
    public void Drop(Vector3 dropPosition)
    {
        if (_carrier != null)
            ReleaseCarrier();

        _state = VaultState.Dropped;
        _carrier = null;
        _dropTime = Time.time;
        _despawnTimer = 0f;

        // Snap to NavMesh
        NavMeshHit hit;
        if (NavMesh.SamplePosition(dropPosition, out hit, 10f, NavMesh.AllAreas))
            dropPosition = hit.position + Vector3.up * 0.5f;
        else
            dropPosition += Vector3.up * 0.5f;

        transform.position = dropPosition;
        if (_collider != null) _collider.enabled = true;

        if (sealedVisual != null) sealedVisual.SetActive(false);
        if (openedVisual != null) openedVisual.SetActive(true);

        if (dropImpactEffect != null)
        {
            dropImpactEffect.transform.position = dropPosition;
            dropImpactEffect.Play();
        }
        if (glowEffect != null) glowEffect.Play();

        PlaySound(dropSound);

        GameEvents.FireVaultDropped(this, dropPosition);
        GameEvents.FireKillFeedEntry("", "VAULT DROPPED!", "");

        DevLog.Log($"[ExtractionVault] Vault dropped at {dropPosition}");
    }

    /// <summary>Called when vault is deposited at a CashoutStation. Removes from world.</summary>
    public void OnDeposited()
    {
        if (_carrier != null)
            ReleaseCarrier();

        _state = VaultState.Deposited;
        if (glowEffect != null) glowEffect.Stop();
        gameObject.SetActive(false);

        DevLog.Log($"[ExtractionVault] Vault deposited and removed from world");
    }

    /// <summary>Remove from play (despawn timer expired, will respawn elsewhere).</summary>
    public void Despawn()
    {
        if (_carrier != null)
            ReleaseCarrier();

        _state = VaultState.Sealed;
        if (glowEffect != null) glowEffect.Stop();
        gameObject.SetActive(false);

        DevLog.Log($"[ExtractionVault] Vault despawned (timeout)");
    }

    // ─── Encumbrance ─────────────────────────────────────────

    private void ApplyEncumbrance(GameObject carrier)
    {
        // Speed reduction via NavMeshAgent (AI) or CashCarrier speed mult
        NavMeshAgent agent = carrier.GetComponent<NavMeshAgent>();
        if (agent != null && agent.enabled)
        {
            _originalCarrierSpeed = agent.speed;
            agent.speed *= carrySpeedMultiplier;
        }

        // Also set CashCarrier speed multiplier for animation sync
        CashCarrier cashCarrier = carrier.GetComponent<CashCarrier>();
        if (cashCarrier != null)
        {
            // CashCarrier.SpeedMultiplier is read-only property driven by bags — 
            // we handle vault encumbrance via a separate flag
        }

        // Disable attacks
        if (disableAttackWhileCarrying)
        {
            CharacterAIController aiCtrl = carrier.GetComponent<CharacterAIController>();
            if (aiCtrl != null) aiCtrl.attack = false;

            PlayerScript playerScript = carrier.GetComponent<PlayerScript>();
            if (playerScript != null) playerScript.attack = false;
        }

        // Disable sprint via stamina drain (conceptual — sprint uses stamina)
        // The AI won't sprint because attack is disabled and speed is reduced
    }

    private void ReleaseCarrier()
    {
        if (_carrier == null) return;

        // Restore speed
        NavMeshAgent agent = _carrier.GetComponent<NavMeshAgent>();
        if (agent != null && agent.enabled && _originalCarrierSpeed > 0)
        {
            agent.speed = _originalCarrierSpeed;
            _originalCarrierSpeed = -1f;
        }

        // Re-enable attacks
        if (disableAttackWhileCarrying)
        {
            CharacterAIController aiCtrl = _carrier.GetComponent<CharacterAIController>();
            if (aiCtrl != null) aiCtrl.attack = true;

            PlayerScript playerScript = _carrier.GetComponent<PlayerScript>();
            if (playerScript != null) playerScript.attack = true;
        }

        _carrier = null;
    }

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

    private void OnTriggerEnter(Collider other)
    {
        if (_state == VaultState.Carried || _state == VaultState.Deposited) return;
        if (SelectionOptions.Instance == null || !SelectionOptions.Instance.isCashSystemSelected) return;

        GameObject character = GetCharacterRoot(other);
        if (character == null) return;

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

        if (_state == VaultState.Sealed)
        {
            // Character approach triggers opening (AI will call StartOpening explicitly)
            // For the player, auto-start opening on trigger enter
            PlayerScript ps = character.GetComponent<PlayerScript>();
            if (ps != null && ps.enabled)
            {
                StartOpening(character);
            }
        }
        else if (IsAvailableForPickup)
        {
            // Anyone can pick up an opened/dropped vault
            PickUp(character);
        }
    }

    private void OnTriggerExit(Collider other)
    {
        if (_state != VaultState.Opening) return;

        GameObject character = GetCharacterRoot(other);
        if (character == null) return;

        // If the opener leaves range, cancel opening
        CancelOpening();
    }

    private GameObject GetCharacterRoot(Collider col)
    {
        // Check for TeamMember on parent hierarchy
        TeamMember tm = col.GetComponentInParent<TeamMember>();
        if (tm != null) return tm.gameObject;

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

        return null;
    }

    // ─── Animation ───────────────────────────────────────────

    private void AnimateIdle()
    {
        // Rotate
        transform.Rotate(Vector3.up, rotationSpeed * Time.deltaTime, Space.World);

        // Bob
        float y = _spawnPosition.y + floatOffset + Mathf.Sin((Time.time + _bobPhase) * bobSpeed) * bobHeight;
        transform.position = new Vector3(transform.position.x, y, transform.position.z);
    }

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

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

    // ─── Static Query ────────────────────────────────────────

    /// <summary>Find the nearest available vault (opened or dropped, not carried).</summary>
    public static ExtractionVault FindNearestAvailable(Vector3 position)
    {
        var vaults = EntityRegistry<ExtractionVault>.GetAll();
        ExtractionVault best = null;
        float bestDist = float.MaxValue;

        for (int i = 0; i < vaults.Count; i++)
        {
            if (vaults[i] == null) continue;
            if (!vaults[i].IsAvailableForPickup) continue;

            float dist = Vector3.Distance(position, vaults[i].transform.position);
            if (dist < bestDist)
            {
                bestDist = dist;
                best = vaults[i];
            }
        }
        return best;
    }

    /// <summary>Find the nearest sealed vault.</summary>
    public static ExtractionVault FindNearestSealed(Vector3 position)
    {
        var vaults = EntityRegistry<ExtractionVault>.GetAll();
        ExtractionVault best = null;
        float bestDist = float.MaxValue;

        for (int i = 0; i < vaults.Count; i++)
        {
            if (vaults[i] == null) continue;
            if (!vaults[i].IsSealed) continue;

            float dist = Vector3.Distance(position, vaults[i].transform.position);
            if (dist < bestDist)
            {
                bestDist = dist;
                best = vaults[i];
            }
        }
        return best;
    }

    /// <summary>Check if any vault is currently being carried by a specific team.</summary>
    public static ExtractionVault FindCarriedByTeam(string teamTag)
    {
        var vaults = EntityRegistry<ExtractionVault>.GetAll();
        for (int i = 0; i < vaults.Count; i++)
        {
            if (vaults[i] == null || !vaults[i].IsCarried) continue;
            if (vaults[i].Carrier != null && vaults[i].Carrier.CompareTag(teamTag))
                return vaults[i];
        }
        return null;
    }

#if UNITY_EDITOR
    private void OnDrawGizmos()
    {
        Color c = _state switch
        {
            VaultState.Sealed => new Color(1f, 0.84f, 0f, 0.5f),  // Gold
            VaultState.Opened => new Color(0f, 1f, 0f, 0.5f),     // Green
            VaultState.Carried => new Color(0f, 0.5f, 1f, 0.5f),  // Blue
            VaultState.Dropped => new Color(1f, 0.5f, 0f, 0.5f),  // Orange
            _ => new Color(0.5f, 0.5f, 0.5f, 0.3f)
        };

        Gizmos.color = c;
        Gizmos.DrawSphere(transform.position, 1f);
        Gizmos.DrawWireSphere(transform.position, interactRange);

        if (Application.isPlaying)
        {
            UnityEditor.Handles.Label(transform.position + Vector3.up * 2f,
                $"Vault [{_state}]\n${totalValue}\nOpen: {_openProgress:P0}" +
                (_carrier != null ? $"\nCarrier: {_carrier.name}" : ""));
        }
    }
#endif
}
