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

namespace Nox.Game {
    public interface IEntityDefinition {
        Guid Id { get; }
        string Name { 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,
        Stamina,
        Level,
        Experience
    }

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

    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
                    .Select(attr => {
                        var match = b.attributes?.FirstOrDefault(attr2 => attr2.attribute == attr.attribute);
                        return new Attribute(attr.attribute, attr.value + (match?.value ?? 0));
                    })
                    .ToArray()
            };
        }

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

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

        public int GetValue(StatType statType) {
            return stats.First(stat => stat.stat == statType).value;
        }
    }

    [Serializable]
    public class CharacterDefinition : IEntityDefinition {
        public Guid Id { get; set; }
        [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,
                Attributes = new EntityAttributes {
                    attributes = Attributes?.attributes?.Select(a => new Attribute(a.attribute, a.value)).ToArray()
                },
                Stats = new EntityStats {
                    stats = Stats?.stats?.Select(s => new Stat(s.stat, s.value)).ToArray()
                },
                Perks = new PerksData {
                    perks = Perks?.perks?.Select(p => new PerkDefinition {
                        Id = p.Id,
                        Name = p.Name,
                        Modifiers = p.Modifiers
                    }).ToList() ?? new()
                },
                Modifiers = new ModifiersData {
                    modifiers = Modifiers?.modifiers?.Select(m => new ModifierDefinition {
                        Id = m.Id,
                        Name = m.Name,
                        StatType = m.StatType,
                        AttributeType = m.AttributeType,
                        Operation = m.Operation,
                        Value = m.Value
                    }).ToList() ?? new()
                }
            };
        }


    }

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

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

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