using UnityEngine;
using System.Collections.Generic;

/// <summary>
/// AIBlackboard — Per-AI knowledge store.
/// Holds perception results, targets, and state without allocations.
/// Updated by AIPerceptionSystem and read by AIActions/UtilityScorer.
/// </summary>
[System.Serializable]
public class AIBlackboard
{
    // ─── Identity ────────────────────────────────────────────
    public string teamTag;
    public TeamMember.Team team;

    // ─── Current Targets ─────────────────────────────────────
    public Transform currentTarget;
    public CashBag targetBag;
    public TeamVault myVault;
    public TeamVault enemyVault;
    public CashCarrier nearestEnemyCarrier;     // Priority target
    public Transform escortTarget;                   // Ally to escort

    // ─── Self State ──────────────────────────────────────────
    public float currentHealth;
    public float maxHealth;
    public float healthPercent;
    public bool isDead;
    public int carriedBagCount;
    public float totalCarriedValue;
    public bool isCarrying;

    // ─── Perception Results (updated by AIPerceptionSystem) ──
    public readonly List<PerceivedEntity> perceivedEnemies = new List<PerceivedEntity>(8);
    public readonly List<PerceivedEntity> perceivedAllies = new List<PerceivedEntity>(8);
    public readonly List<CashBag> nearbyAvailableBags = new List<CashBag>(8);
    public readonly List<CashBag> nearbyDroppedBags = new List<CashBag>(4);
    public CashCarrier nearestAllyCarrier;
    public int nearbyEnemyCount;
    public int nearbyAllyCount;
    public float nearestEnemyDistance;
    public float nearestBagDistance;

    // ─── Team State (from TeamBlackboard) ────────────────────
    public float myTeamScore;
    public float enemyTeamScore;
    public bool myVaultUnderAttack;
    public float scoreDifference;                    // positive = winning

    // ─── Navigation ──────────────────────────────────────────
    public Vector3 patrolDestination;
    public bool hasPatrolDestination;
    public float timeAtCurrentState;

    /// <summary>Clear all perception data (called before perception update).</summary>
    public void ClearPerception()
    {
        perceivedEnemies.Clear();
        perceivedAllies.Clear();
        nearbyAvailableBags.Clear();
        nearbyDroppedBags.Clear();
        nearestEnemyCarrier = null;
        nearestAllyCarrier = null;
        nearbyEnemyCount = 0;
        nearbyAllyCount = 0;
        nearestEnemyDistance = float.MaxValue;
        nearestBagDistance = float.MaxValue;
    }
}

/// <summary>
/// Data about a perceived entity (enemy or ally).
/// Reused from pool to avoid allocation.
/// </summary>
[System.Serializable]
public struct PerceivedEntity
{
    public Transform transform;
    public GameObject gameObject;
    public float distance;
    public float sqrDistance;
    public float healthPercent;
    public bool isCarryingBag;
    public int carriedBagCount;
    public bool isVisible;           // In line of sight
    public Vector3 lastKnownPosition;
    public float timeSinceLastSeen;
}

/// <summary>
/// TeamBlackboard — Shared knowledge between all AI on a team.
/// Prevents clustering and enables coordination.
/// Singleton per team — accessed via TeamBlackboard.Get(teamTag).
/// </summary>
public class TeamBlackboard : MonoBehaviour
{
    // ─── Static Access ───────────────────────────────────────
    private static TeamBlackboard _playerTeam;
    private static TeamBlackboard _enemyTeam;

    public static TeamBlackboard Get(string teamTag)
    {
        if (teamTag == "Player")
        {
            if (_playerTeam == null)
            {
                var go = new GameObject("TeamBlackboard_Player");
                _playerTeam = go.AddComponent<TeamBlackboard>();
                _playerTeam._teamTag = "Player";
            }
            return _playerTeam;
        }
        else
        {
            if (_enemyTeam == null)
            {
                var go = new GameObject("TeamBlackboard_Enemy");
                _enemyTeam = go.AddComponent<TeamBlackboard>();
                _enemyTeam._teamTag = "Enemy";
            }
            return _enemyTeam;
        }
    }

    public static void ClearAll()
    {
        if (_playerTeam != null) { Destroy(_playerTeam.gameObject); _playerTeam = null; }
        if (_enemyTeam != null) { Destroy(_enemyTeam.gameObject); _enemyTeam = null; }
    }

    // ─── Team Data ───────────────────────────────────────────
    [SerializeField] private string _teamTag;

    /// <summary>Which bags are claimed by AI (prevents all AI chasing same bag).</summary>
    public readonly Dictionary<CashBag, AIBrain> claimedBags = new Dictionary<CashBag, AIBrain>(8);

    /// <summary>Which enemies are being targeted (limit engagement per enemy).</summary>
    public readonly Dictionary<Transform, int> targetedEnemies = new Dictionary<Transform, int>(8);

    /// <summary>Who on our team is carrying bags?</summary>
    public readonly List<CashCarrier> teamCarriers = new List<CashCarrier>(4);

    /// <summary>Is our vault under attack?</summary>
    public bool vaultUnderAttack;

    /// <summary>Our team's score.</summary>
    public float teamScore;

    // ─── Claim System ────────────────────────────────────────

    /// <summary>Try to claim a bag for this AI. Returns true if claimed successfully.</summary>
    public bool TryClaimBag(CashBag bag, AIBrain claimant)
    {
        if (bag == null) return false;

        // Clean stale claims
        if (claimedBags.TryGetValue(bag, out AIBrain existing))
        {
            if (existing == null || !existing.gameObject.activeInHierarchy)
            {
                claimedBags[bag] = claimant;
                return true;
            }
            return existing == claimant; // Already claimed by us
        }

        claimedBags[bag] = claimant;
        return true;
    }

    /// <summary>Release claim on a bag.</summary>
    public void ReleaseBagClaim(AIBrain claimant)
    {
        CashBag toRemove = null;
        foreach (var kvp in claimedBags)
        {
            if (kvp.Value == claimant) { toRemove = kvp.Key; break; }
        }
        if (toRemove != null) claimedBags.Remove(toRemove);
    }

    /// <summary>Is this bag already claimed by a teammate?</summary>
    public bool IsBagClaimed(CashBag bag, AIBrain excludeSelf = null)
    {
        if (!claimedBags.TryGetValue(bag, out AIBrain claimant)) return false;
        if (claimant == null || !claimant.gameObject.activeInHierarchy)
        {
            claimedBags.Remove(bag);
            return false;
        }
        return claimant != excludeSelf;
    }

    /// <summary>Register an enemy as being targeted. Returns current count.</summary>
    public int AddEnemyTarget(Transform enemy)
    {
        if (enemy == null) return 0;
        if (!targetedEnemies.ContainsKey(enemy))
            targetedEnemies[enemy] = 0;
        targetedEnemies[enemy]++;
        return targetedEnemies[enemy];
    }

    /// <summary>Unregister from targeting an enemy.</summary>
    public void RemoveEnemyTarget(Transform enemy)
    {
        if (enemy == null) return;
        if (targetedEnemies.ContainsKey(enemy))
        {
            targetedEnemies[enemy]--;
            if (targetedEnemies[enemy] <= 0)
                targetedEnemies.Remove(enemy);
        }
    }

    /// <summary>How many teammates are targeting this enemy?</summary>
    public int GetEnemyTargetCount(Transform enemy)
    {
        if (enemy == null) return 0;
        return targetedEnemies.TryGetValue(enemy, out int count) ? count : 0;
    }

    /// <summary>Update team carriers list.</summary>
    public void RefreshCarriers()
    {
        teamCarriers.Clear();
        var allCarriers = EntityRegistry<CashCarrier>.All;
        for (int i = 0; i < allCarriers.Count; i++)
        {
            if (allCarriers[i] != null && allCarriers[i].TeamTag == _teamTag && allCarriers[i].IsCarrying)
                teamCarriers.Add(allCarriers[i]);
        }
    }

    /// <summary>Clean stale entries (call periodically).</summary>
    public void CleanStaleEntries()
    {
        // Clean bag claims
        var staleBags = new List<CashBag>();
        foreach (var kvp in claimedBags)
        {
            if (kvp.Key == null || kvp.Value == null || !kvp.Value.gameObject.activeInHierarchy)
                staleBags.Add(kvp.Key);
        }
        for (int i = 0; i < staleBags.Count; i++)
            claimedBags.Remove(staleBags[i]);

        // Clean enemy targets
        var staleTargets = new List<Transform>();
        foreach (var kvp in targetedEnemies)
        {
            if (kvp.Key == null) staleTargets.Add(kvp.Key);
        }
        for (int i = 0; i < staleTargets.Count; i++)
            targetedEnemies.Remove(staleTargets[i]);
    }

    private float _lastCleanTime;
    private void Update()
    {
        if (Time.time - _lastCleanTime > 3f)
        {
            CleanStaleEntries();
            RefreshCarriers();
            _lastCleanTime = Time.time;
        }
    }

    private void OnDestroy()
    {
        if (_playerTeam == this) _playerTeam = null;
        if (_enemyTeam == this) _enemyTeam = null;
    }
}
