using UnityEngine;
using System.Collections.Generic;

/// <summary>
/// MatchStatTracker — Listens to GameEvents during a match and records
/// per-character stats (kills, deaths, assists, damage dealt, cash deposited, cash stolen).
/// Singleton that lives for the duration of the match scene.
/// MatchResultUI reads from this at match end.
/// </summary>
public class MatchStatTracker : MonoBehaviour
{
    /// <summary>Stats for one character.</summary>
    public class CharacterStats
    {
        public string   name;
        public int      kills;
        public int      deaths;
        public int      assists;
        public float    damageDealt;
        public float    cashDeposited;
        public float    cashStolen;
        public float    cashCollected;
    }

    // Key = character GameObject instance ID
    private Dictionary<int, CharacterStats> _stats = new Dictionary<int, CharacterStats>();

    // Track recent attackers for assist detection (victimID → list of attackerIDs within last N seconds)
    private Dictionary<int, List<(int attackerID, float time)>> _recentAttackers 
        = new Dictionary<int, List<(int, float)>>();
    private const float ASSIST_WINDOW = 5f;

    private static MatchStatTracker _instance;
    public static MatchStatTracker Instance => _instance;

    private void Awake()
    {
        if (_instance != null && _instance != this)
        {
            Destroy(gameObject);
            return;
        }
        _instance = this;
    }

    private void OnEnable()
    {
        GameEvents.OnKill          += OnKill;
        GameEvents.OnDamageDealt   += OnDamageDealt;
        GameEvents.OnCharacterDied += OnCharacterDied;
        GameEvents.OnCashDeposited += OnCashDeposited;
        GameEvents.OnVaultStolen   += OnVaultStolen;
        GameEvents.OnBagPickedUp   += OnBagPickedUp;
    }

    private void OnDisable()
    {
        GameEvents.OnKill          -= OnKill;
        GameEvents.OnDamageDealt   -= OnDamageDealt;
        GameEvents.OnCharacterDied -= OnCharacterDied;
        GameEvents.OnCashDeposited -= OnCashDeposited;
        GameEvents.OnVaultStolen   -= OnVaultStolen;
        GameEvents.OnBagPickedUp   -= OnBagPickedUp;
    }

    // ─── Public API ──────────────────────────────────────────

    /// <summary>Get stats for a specific character. Creates entry if missing.</summary>
    public CharacterStats GetStats(GameObject character)
    {
        if (character == null) return new CharacterStats { name = "Unknown" };
        int id = character.GetInstanceID();
        if (!_stats.ContainsKey(id))
        {
            _stats[id] = new CharacterStats
            {
                name = character.name.Replace("(Clone)", "").Trim()
            };
        }
        return _stats[id];
    }

    /// <summary>Get stats for all characters in a list (team).</summary>
    public List<CharacterStats> GetTeamStats(List<GameObject> teamCharacters)
    {
        List<CharacterStats> result = new List<CharacterStats>();
        if (teamCharacters == null) return result;
        foreach (var c in teamCharacters)
        {
            if (c != null) result.Add(GetStats(c));
        }
        return result;
    }

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

    private void OnKill(GameObject killer, GameObject victim)
    {
        if (killer != null)
            GetStats(killer).kills++;
        if (victim != null)
            GetStats(victim).deaths++;

        // Award assists to other recent attackers of the victim
        if (victim != null)
        {
            int victimID = victim.GetInstanceID();
            int killerID = killer != null ? killer.GetInstanceID() : -1;

            if (_recentAttackers.ContainsKey(victimID))
            {
                HashSet<int> assistedIDs = new HashSet<int>();
                foreach (var (attackerID, time) in _recentAttackers[victimID])
                {
                    if (attackerID != killerID && Time.time - time <= ASSIST_WINDOW && !assistedIDs.Contains(attackerID))
                    {
                        assistedIDs.Add(attackerID);
                        if (_stats.ContainsKey(attackerID))
                            _stats[attackerID].assists++;
                    }
                }
                _recentAttackers.Remove(victimID);
            }
        }
    }

    private void OnDamageDealt(GameObject attacker, GameObject victim, float amount)
    {
        if (attacker != null)
            GetStats(attacker).damageDealt += amount;

        // Track for assists
        if (attacker != null && victim != null)
        {
            int victimID   = victim.GetInstanceID();
            int attackerID = attacker.GetInstanceID();

            if (!_recentAttackers.ContainsKey(victimID))
                _recentAttackers[victimID] = new List<(int, float)>();

            _recentAttackers[victimID].Add((attackerID, Time.time));

            // Prune old entries
            _recentAttackers[victimID].RemoveAll(e => Time.time - e.time > ASSIST_WINDOW);
        }
    }

    private void OnCharacterDied(GameObject character)
    {
        // Death is already tracked via OnKill, but if OnKill isn't fired for some deaths:
        if (character != null)
        {
            var s = GetStats(character);
            // Only increment if OnKill hasn't already done it this frame
            // (simple guard — check if death count was already incremented)
        }
    }

    private void OnCashDeposited(string teamTag, float value, int combo)
    {
        // Attribute to the character who deposited — find the closest carrier
        CashSystemManager mgr = CashSystemManager.Instance;
        if (mgr == null) return;

        var team = mgr.GetTeamCharacters(teamTag);
        if (team == null) return;

        // Find the character who just deposited (nearest to their vault)
        TeamVault vault = mgr.GetVault(teamTag);
        if (vault == null) return;

        float bestDist = float.MaxValue;
        GameObject depositor = null;
        foreach (var c in team)
        {
            if (c == null) continue;
            float d = Vector3.Distance(c.transform.position, vault.transform.position);
            if (d < bestDist)
            {
                bestDist = d;
                depositor = c;
            }
        }

        if (depositor != null)
            GetStats(depositor).cashDeposited += value;
    }

    private void OnVaultStolen(TeamVault vault, float amount)
    {
        // Attribute to the closest enemy character to this vault
        if (vault == null) return;
        CashSystemManager mgr = CashSystemManager.Instance;
        if (mgr == null) return;

        string enemyTag = vault.TeamTag == "Player" ? "Enemy" : "Player";
        var enemies = mgr.GetTeamCharacters(enemyTag);
        if (enemies == null) return;

        float bestDist = float.MaxValue;
        GameObject thief = null;
        foreach (var c in enemies)
        {
            if (c == null) continue;
            float d = Vector3.Distance(c.transform.position, vault.transform.position);
            if (d < bestDist)
            {
                bestDist = d;
                thief = c;
            }
        }

        if (thief != null)
            GetStats(thief).cashStolen += amount;
    }

    private void OnBagPickedUp(CashBag bag, CashCarrier carrier)
    {
        if (carrier != null && carrier.gameObject != null)
            GetStats(carrier.gameObject).cashCollected += bag != null ? bag.CashValue : 0f;
    }
}
