using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using ZLinq;
using UnityEngine;

namespace Nox.Game {
    public interface IEntityDefinition {
        Guid Id { get; }
        string Name { get; }
        int PortraitIndex { get; }
        CharacterRace Race { get; }
        CharacterClass Class { get; }
        CharacterRole Role { get; }
        EntityAttributes Attributes { get; }
        EntityStats Stats { get; }
        PerksData Perks { get; }
        ModifiersData Modifiers { get; }

    }

    public enum StatType {
        None,
        Health,
        Mana,
        Level,
        Experience
    }

    public enum AttributeType {
        None,
        Might,
        Reflex,
        Knowledge,
        Perception
    }

    public enum CombatScoreType {
        None,
        ATK,
        DEF,
        INIT,
        SPD,
        RES
    }

    public enum CharacterRole {
        None,
        Protagonist,
        Companion
    }

    public enum CharacterClass {
        None,
        Warrior,
        Rogue,
        Mage,
        Herald
    }

    public enum CharacterRace {
        None,
        Human,
        DarkElf,
        Tunneler
    }

    [Serializable]
    public sealed record Stat {
        public StatType stat;
        public int value;
        public Stat(StatType stat, int value) {
            this.stat = stat;
            this.value = value;
        }
    }

    [Serializable]
    public sealed record Attribute {
        public AttributeType attribute;
        public int value;
        public Attribute(AttributeType attribute, int value) {
            this.attribute = attribute;
            this.value = value;
        }
    }

    [Serializable]
    public sealed class EntityAttributes {
        public Attribute[] attributes;

        public static EntityAttributes operator +(EntityAttributes a, EntityAttributes b) {
            return new EntityAttributes {
                attributes = a.attributes.AsValueEnumerable()
                    .Select(attr => {
                        var match = b.attributes?.AsValueEnumerable().FirstOrDefault(attr2 => attr2.attribute == attr.attribute);
                        return new Attribute(attr.attribute, attr.value + (match?.value ?? 0));
                    })
                    .ToArray()
            };
        }

        public int GetValue(AttributeType attributeType) {
            return attributes.AsValueEnumerable().First(attr => attr.attribute == attributeType).value;
        }

        public override string ToString() {
            return $"Attributes: {string.Join(", ", attributes.Select(attr => $"{attr.attribute}: {attr.value}"))}";
        }
    }

    [Serializable]
    public sealed class EntityStats {
        public Stat[] stats;

        public int GetValue(StatType statType) {
            if(stats == null) {
                return 0;
            }
            var match = stats.AsValueEnumerable().FirstOrDefault(stat => stat.stat == statType);
            return match?.value ?? 0;
        }

        public override string ToString() {
            return $"Stats: {string.Join(", ", stats.Select(stat => $"{stat.stat}: {stat.value}"))}";
        }
    }

    [Serializable]
    public class CharacterDefinition : IEntityDefinition {
        public Guid Id { get; set; }
        public int PortraitIndex { get; set; } = 0;
        [field: SerializeField] public string Name { get; set; }
        [field: SerializeField] public CharacterRace Race { get; set; }
        [field: SerializeField] public CharacterClass Class { get; set; }
        [field: SerializeField] public CharacterRole Role { get; set; }
        [field: SerializeField] public EntityAttributes Attributes { get; set; }
        [field: SerializeField] public EntityStats Stats { get; set; }
        [field: SerializeField] public PerksData Perks { get; set; }
        [field: SerializeField] public ModifiersData Modifiers { get; set; }

        public CharacterDefinition Clone() {
            return new CharacterDefinition {
                Id = Guid.NewGuid(),
                Name = Name,
                Role = Role,
                Race = Race,
                Class = Class,
                PortraitIndex = PortraitIndex,
                Attributes = new EntityAttributes {
                    attributes = Attributes?.attributes?.AsValueEnumerable().Select(a => new Attribute(a.attribute, a.value)).ToArray()
                },
                Stats = new EntityStats {
                    stats = Stats?.stats?.AsValueEnumerable().Select(s => new Stat(s.stat, s.value)).ToArray()
                },
                Perks = new PerksData {
                    perks = Perks?.perks?.AsValueEnumerable().Select(p => new PerkDefinition {
                        Id = p.Id,
                        Name = p.Name,
                        Modifiers = p.Modifiers
                    }).ToList() ?? new List<PerkDefinition>()
                },
                Modifiers = new ModifiersData {
                    modifiers = Modifiers?.modifiers?.AsValueEnumerable().Select(m => new ModifierDefinition {
                        Id = m.Id,
                        Name = m.Name,
                        Target = m.Target,
                        ScalingSource = m.ScalingSource,
                        Operation = m.Operation,
                        Value = m.Value,
                        Requirements = m.Requirements?.ToList() ?? new List<ModifierRequirement>()
                    }).ToList() ?? new List<ModifierDefinition>()
                }
            };
        }


    }

    [Serializable]
    public sealed class PartyDefinition {
        public int maxPartySize;
        public List<CharacterDefinition> members = new();

        [JsonIgnore]
        public CharacterDefinition Protagonist => members.AsValueEnumerable().FirstOrDefault(m => m.Role == CharacterRole.Protagonist);

        [JsonIgnore]
        public IReadOnlyList<CharacterDefinition> Companions => members.AsValueEnumerable().Where(m => m.Role == CharacterRole.Companion).ToList();
    }
}
