using System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;

namespace MosswartMassacre
{
    [JsonConverter(typeof(StringEnumConverter))]
    public enum DamageElement
    {
        None,
        Unknown,
        Typeless,
        Slash,
        Pierce,
        Bludgeon,
        Fire,
        Cold,
        Acid,
        Electric,
    }

    [JsonConverter(typeof(StringEnumConverter))]
    public enum AttackType
    {
        Unknown,
        MeleeMissile,
        Magic,
    }

    /// <summary>
    /// Per-element damage statistics within a single attack type.
    /// Mirrors Mag-Tools CombatInfo.DamageByAttackType.DamageByElement.
    /// </summary>
    public class DamageStats
    {
        [JsonProperty("total_attacks")]   public int TotalAttacks;
        [JsonProperty("failed_attacks")]  public int FailedAttacks;
        [JsonProperty("crits")]           public int Crits;
        [JsonProperty("total_normal_damage")] public int TotalNormalDamage;
        [JsonProperty("max_normal_damage")]   public int MaxNormalDamage;
        [JsonProperty("total_crit_damage")]   public int TotalCritDamage;
        [JsonProperty("max_crit_damage")]     public int MaxCritDamage;
        [JsonProperty("killing_blows")]       public int KillingBlows;

        [JsonProperty("damage")]
        public int Damage => TotalNormalDamage + TotalCritDamage;
    }

    /// <summary>
    /// Per-monster combat record. Stores offense and defense breakdowns
    /// keyed by AttackType → DamageElement → DamageStats. Mirrors
    /// Mag-Tools CombatInfo but without the source/target pair split
    /// (we always track relative to the local character).
    /// </summary>
    public class MonsterCombatRecord
    {
        [JsonProperty("name")]            public string Name;
        [JsonProperty("kill_count")]      public int KillCount;
        [JsonProperty("damage_given")]    public long DamageGiven;
        [JsonProperty("damage_received")] public long DamageReceived;
        [JsonProperty("aetheria_surges")] public int AetheriaSurges;
        [JsonProperty("cloak_surges")]    public int CloakSurges;

        // offense[AttackType][DamageElement] = stats for damage WE dealt
        [JsonProperty("offense")]
        public Dictionary<AttackType, Dictionary<DamageElement, DamageStats>> Offense
            = new Dictionary<AttackType, Dictionary<DamageElement, DamageStats>>();

        // defense[AttackType][DamageElement] = stats for damage WE received
        [JsonProperty("defense")]
        public Dictionary<AttackType, Dictionary<DamageElement, DamageStats>> Defense
            = new Dictionary<AttackType, Dictionary<DamageElement, DamageStats>>();

        public DamageStats GetOrCreateStats(
            Dictionary<AttackType, Dictionary<DamageElement, DamageStats>> side,
            AttackType at, DamageElement el)
        {
            if (!side.TryGetValue(at, out var byElement))
            {
                byElement = new Dictionary<DamageElement, DamageStats>();
                side[at] = byElement;
            }
            if (!byElement.TryGetValue(el, out var stats))
            {
                stats = new DamageStats();
                byElement[el] = stats;
            }
            return stats;
        }

        // ── Aggregate helpers matching Mag-Tools CombatTrackerGUI calculations ──

        public int TotalOffenseAttacks =>
            Offense.Values.SelectMany(d => d.Values).Sum(s => s.TotalAttacks);
        public int TotalOffenseFailedAttacks =>
            Offense.Values.SelectMany(d => d.Values).Sum(s => s.FailedAttacks);
        public int TotalOffenseCrits =>
            Offense.Values.SelectMany(d => d.Values).Sum(s => s.Crits);
        public long TotalOffenseNormalDamage =>
            Offense.Values.SelectMany(d => d.Values).Sum(s => (long)s.TotalNormalDamage);
        public int MaxOffenseNormalDamage =>
            Offense.Values.SelectMany(d => d.Values).Select(s => s.MaxNormalDamage).DefaultIfEmpty(0).Max();
        public long TotalOffenseCritDamage =>
            Offense.Values.SelectMany(d => d.Values).Sum(s => (long)s.TotalCritDamage);
        public int MaxOffenseCritDamage =>
            Offense.Values.SelectMany(d => d.Values).Select(s => s.MaxCritDamage).DefaultIfEmpty(0).Max();

        public int TotalDefenseAttacks =>
            Defense.Values.SelectMany(d => d.Values).Sum(s => s.TotalAttacks);
        public int TotalDefenseFailedAttacks =>
            Defense.Values.SelectMany(d => d.Values).Sum(s => s.FailedAttacks);

        // Defense damage by element for the breakdown grid
        public int GetDefenseDamage(AttackType at, DamageElement el)
        {
            if (!Defense.TryGetValue(at, out var byEl)) return 0;
            if (!byEl.TryGetValue(el, out var stats)) return 0;
            return stats.Damage;
        }
    }

    /// <summary>
    /// Holds all combat data for a single session (or lifetime accumulation).
    /// Serializes cleanly to JSON for the WebSocket combat_stats envelope.
    /// </summary>
    public class CombatSessionState
    {
        [JsonProperty("total_damage_given")]    public long TotalDamageGiven;
        [JsonProperty("total_damage_received")] public long TotalDamageReceived;
        [JsonProperty("total_kills")]           public int TotalKills;
        [JsonProperty("total_aetheria_surges")] public int TotalAetheriaSurges;
        [JsonProperty("total_cloak_surges")]    public int TotalCloakSurges;
        [JsonProperty("session_start")]         public string SessionStart;

        [JsonProperty("monsters")]
        public Dictionary<string, MonsterCombatRecord> Monsters
            = new Dictionary<string, MonsterCombatRecord>(StringComparer.OrdinalIgnoreCase);

        public MonsterCombatRecord GetOrCreateMonster(string name)
        {
            if (!Monsters.TryGetValue(name, out var rec))
            {
                rec = new MonsterCombatRecord { Name = name };
                Monsters[name] = rec;
            }
            return rec;
        }

        public void Clear()
        {
            TotalDamageGiven = 0;
            TotalDamageReceived = 0;
            TotalKills = 0;
            TotalAetheriaSurges = 0;
            TotalCloakSurges = 0;
            SessionStart = DateTime.UtcNow.ToString("o");
            Monsters.Clear();
        }
    }
}
