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

/// <summary>
/// VaultSpawner — Spawns ExtractionVault objectives at arena points.
///
/// Simpler than CashBagSpawner: spawns 1-2 vaults at a time at designated
/// spawn points spread across the map. When a vault is deposited or despawns,
/// a new one spawns at a different location after a delay.
///
/// Uses the same patterns as CashBagSpawner: NavMesh snap, EntityRegistry,
/// GameEvents, ObjectPool.
/// </summary>
public class VaultSpawner : MonoBehaviour
{
    [Header("Vault Settings")]
    [SerializeField] private GameObject vaultPrefab;
    [SerializeField] private float vaultValue = 10000f;

    [Header("Spawn Limits")]
    [SerializeField] private int maxActiveVaults = 2;
    [SerializeField] private int initialSpawnCount = 1;
    [SerializeField] private float respawnDelay = 15f;          // Seconds before a new vault spawns after one is consumed

    [Header("Spawn Points")]
    [SerializeField] private Transform[] spawnPoints;
    [SerializeField] private string spawnPointTag = "VaultSpawn"; // Fallback tag to find spawn points
    [SerializeField] private float minDistanceBetweenVaults = 30f;
    [SerializeField] private float playerAvoidDistance = 10f;
    [SerializeField] private float stationAvoidDistance = 15f;   // Don't spawn too close to cashout stations

    [Header("Random Spawning (Fallback)")]
    [SerializeField] private bool useRandomFallback = true;
    [SerializeField] private Vector3 spawnAreaCenter = Vector3.zero;
    [SerializeField] private Vector3 spawnAreaSize = new Vector3(80f, 0f, 80f);

    [Header("Debug")]
    [SerializeField] private bool showGizmos = true;

    // ─── Runtime ─────────────────────────────────────────────
    private readonly List<ExtractionVault> _activeVaults = new List<ExtractionVault>(4);
    private readonly HashSet<int> _usedSpawnPoints = new HashSet<int>();
    private readonly Dictionary<int, float> _spawnPointCooldowns = new Dictionary<int, float>();
    private const float SPAWN_POINT_COOLDOWN = 30f;
    private bool _isSpawning;

    public int ActiveVaultCount => _activeVaults.Count;

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

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

        // Find spawn points by tag if none assigned
        if (spawnPoints == null || spawnPoints.Length == 0)
        {
            GameObject[] objs = GameObject.FindGameObjectsWithTag(spawnPointTag);
            if (objs.Length > 0)
            {
                spawnPoints = new Transform[objs.Length];
                for (int i = 0; i < objs.Length; i++)
                    spawnPoints[i] = objs[i].transform;
                DevLog.Log($"[VaultSpawner] Found {objs.Length} spawn points by tag '{spawnPointTag}'");
            }
            else if (!useRandomFallback)
            {
                DevLog.LogWarning("[VaultSpawner] No spawn points found and random fallback disabled!");
            }
        }

        StartSpawning();
    }

    private void OnEnable()
    {
        GameEvents.OnCashoutCompleted += OnVaultCashedOut;
        GameEvents.OnCashoutHacked += OnVaultCashedOut;
    }

    private void OnDisable()
    {
        GameEvents.OnCashoutCompleted -= OnVaultCashedOut;
        GameEvents.OnCashoutHacked -= OnVaultCashedOut;
    }

    // ─── Spawning ────────────────────────────────────────────

    public void StartSpawning()
    {
        if (_isSpawning) return;
        _isSpawning = true;

        for (int i = 0; i < initialSpawnCount; i++)
            SpawnVault();

        GameEvents.FirePopupMessage("VAULTS DETECTED!", 2f);
    }

    public void StopSpawning()
    {
        _isSpawning = false;
    }

    private void SpawnVault()
    {
        if (vaultPrefab == null)
        {
            DevLog.LogWarning("[VaultSpawner] No vault prefab assigned!");
            return;
        }

        Vector3 spawnPos = GetValidSpawnPosition();
        if (spawnPos == Vector3.zero)
        {
            DevLog.LogWarning("[VaultSpawner] Could not find valid spawn position");
            return;
        }

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

        GameObject vaultObj = Instantiate(vaultPrefab, spawnPos, Quaternion.identity);
        ExtractionVault vault = vaultObj.GetComponent<ExtractionVault>();
        if (vault == null)
            vault = vaultObj.AddComponent<ExtractionVault>();

        _activeVaults.Add(vault);

        // Add minimap marker
        if (vaultObj.GetComponent<MinimapObjectMarker>() == null)
            vaultObj.AddComponent<MinimapObjectMarker>();

        GameEvents.FireExtractionVaultSpawned(vault);
        GameEvents.FireKillFeedEntry("", "NEW VAULT SPAWNED", "");

        DevLog.Log($"[VaultSpawner] Vault spawned at {spawnPos}");
    }

    // ─── Position Validation ─────────────────────────────────

    private Vector3 GetValidSpawnPosition()
    {
        if (spawnPoints != null && spawnPoints.Length > 0)
            return GetSpawnPointPosition();

        if (useRandomFallback)
            return GetRandomSpawnPosition();

        return Vector3.zero;
    }

    private Vector3 GetSpawnPointPosition()
    {
        // Clean expired cooldowns
        List<int> expired = null;
        foreach (var kvp in _spawnPointCooldowns)
        {
            if (Time.time >= kvp.Value)
            {
                if (expired == null) expired = new List<int>(4);
                expired.Add(kvp.Key);
            }
        }
        if (expired != null)
            foreach (int idx in expired)
                _spawnPointCooldowns.Remove(idx);

        // Shuffle indices
        List<int> indices = new List<int>(spawnPoints.Length);
        for (int i = 0; i < spawnPoints.Length; i++)
            indices.Add(i);
        for (int i = indices.Count - 1; i > 0; i--)
        {
            int j = Random.Range(0, i + 1);
            (indices[i], indices[j]) = (indices[j], indices[i]);
        }

        // Pick first valid, non-cooldown point
        foreach (int idx in indices)
        {
            if (spawnPoints[idx] == null) continue;
            if (_usedSpawnPoints.Contains(idx)) continue;
            if (_spawnPointCooldowns.ContainsKey(idx)) continue;
            if (IsPositionValid(spawnPoints[idx].position))
            {
                _usedSpawnPoints.Add(idx);
                return spawnPoints[idx].position;
            }
        }

        // Fallback: allow cooldown points
        foreach (int idx in indices)
        {
            if (spawnPoints[idx] == null) continue;
            if (_usedSpawnPoints.Contains(idx)) continue;
            if (IsPositionValid(spawnPoints[idx].position))
            {
                _usedSpawnPoints.Add(idx);
                return spawnPoints[idx].position;
            }
        }

        return useRandomFallback ? GetRandomSpawnPosition() : Vector3.zero;
    }

    private Vector3 GetRandomSpawnPosition()
    {
        for (int attempt = 0; attempt < 30; attempt++)
        {
            Vector3 pos = spawnAreaCenter + new Vector3(
                Random.Range(-spawnAreaSize.x / 2, spawnAreaSize.x / 2),
                0.5f,
                Random.Range(-spawnAreaSize.z / 2, spawnAreaSize.z / 2)
            );

            if (IsPositionValid(pos))
                return pos;
        }

        // Last resort
        return spawnAreaCenter + new Vector3(Random.Range(-10f, 10f), 0.5f, Random.Range(-10f, 10f));
    }

    private bool IsPositionValid(Vector3 pos)
    {
        // Check distance from active vaults
        for (int i = 0; i < _activeVaults.Count; i++)
        {
            if (_activeVaults[i] != null && Vector3.Distance(pos, _activeVaults[i].transform.position) < minDistanceBetweenVaults)
                return false;
        }

        // Check distance from characters
        foreach (TeamMember member in TeamMember.AllMembers)
        {
            if (member != null && Vector3.Distance(pos, member.transform.position) < playerAvoidDistance)
                return false;
        }

        // Check distance from cashout stations
        var stations = EntityRegistry<CashoutStation>.GetAll();
        for (int i = 0; i < stations.Count; i++)
        {
            if (stations[i] != null && Vector3.Distance(pos, stations[i].transform.position) < stationAvoidDistance)
                return false;
        }

        return true;
    }

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

    /// <summary>Called when a vault's cashout completes or gets hacked — spawn replacement.</summary>
    private void OnVaultCashedOut(CashoutStation station, string teamTag, float amount)
    {
        // Clean up null/inactive vaults
        _activeVaults.RemoveAll(v => v == null || !v.gameObject.activeInHierarchy);

        if (_isSpawning && _activeVaults.Count < maxActiveVaults)
        {
            StartCoroutine(DelayedRespawn());
        }
    }

    private IEnumerator DelayedRespawn()
    {
        yield return new WaitForSeconds(respawnDelay);
        if (!_isSpawning) yield break;

        _activeVaults.RemoveAll(v => v == null || !v.gameObject.activeInHierarchy);

        if (_activeVaults.Count < maxActiveVaults)
        {
            SpawnVault();
        }
    }

    /// <summary>Periodically check for despawned vaults and replace them.</summary>
    private void Update()
    {
        // Clean up null/inactive vaults every few seconds
        if (Time.frameCount % 180 == 0)
        {
            int before = _activeVaults.Count;
            _activeVaults.RemoveAll(v => v == null || !v.gameObject.activeInHierarchy);
            int removed = before - _activeVaults.Count;

            if (removed > 0 && _isSpawning && _activeVaults.Count < maxActiveVaults)
            {
                StartCoroutine(DelayedRespawn());
            }

            // Also release spawn points for inactive vaults
            ReleaseInactiveSpawnPoints();
        }
    }

    private void ReleaseInactiveSpawnPoints()
    {
        if (spawnPoints == null) return;

        List<int> toRelease = null;
        foreach (int idx in _usedSpawnPoints)
        {
            if (idx < 0 || idx >= spawnPoints.Length || spawnPoints[idx] == null) continue;

            bool stillOccupied = false;
            for (int i = 0; i < _activeVaults.Count; i++)
            {
                if (_activeVaults[i] != null &&
                    Vector3.Distance(_activeVaults[i].SpawnPosition, spawnPoints[idx].position) < 5f)
                {
                    stillOccupied = true;
                    break;
                }
            }

            if (!stillOccupied)
            {
                if (toRelease == null) toRelease = new List<int>(4);
                toRelease.Add(idx);
            }
        }

        if (toRelease != null)
        {
            foreach (int idx in toRelease)
            {
                _usedSpawnPoints.Remove(idx);
                _spawnPointCooldowns[idx] = Time.time + SPAWN_POINT_COOLDOWN;
            }
        }
    }

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

    private void OnDrawGizmos()
    {
        if (!showGizmos) return;

        Gizmos.color = new Color(1f, 0.84f, 0f, 0.2f);
        Gizmos.DrawCube(spawnAreaCenter, spawnAreaSize);
        Gizmos.color = new Color(1f, 0.84f, 0f, 0.5f);
        Gizmos.DrawWireCube(spawnAreaCenter, spawnAreaSize);

        if (spawnPoints != null)
        {
            Gizmos.color = new Color(1f, 0.84f, 0f, 0.8f);
            foreach (Transform point in spawnPoints)
                if (point != null)
                    Gizmos.DrawSphere(point.position, 1f);
        }
    }
}
