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

namespace Nox.Game {
    public interface IEntityDefinition {
        string ID { get; }
        string DisplayName { get; }
        EntityAttributes Attributes { get; }
        EntityStats Stats { 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 class Stat {
        private readonly StatType health;
        public StatType type;
        public int value;
        public Stat(StatType health, int value) {
            this.health = health;
            this.value = value;
        }
    }

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

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

        public static EntityAttributes operator +(EntityAttributes a, EntityAttributes b) {
            return new EntityAttributes {
                attributes = a.attributes
                    .Select(attr => new Attribute(attr.attribute, attr.value + b.attributes
                        .First(attr2 => attr2.attribute == attr.attribute).value))
                    .ToArray()
            };
        }
    }

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

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

    [Serializable]
    public sealed class CharacterDefinition : IEntityDefinition {
        [SerializeField] private string id;
        [SerializeField] private string displayName;
        public CharacterRace race;
        public CharacterClass @class;
        public CharacterRole role;
        [SerializeField] private EntityAttributes attributes;
        [SerializeField] private EntityStats stats;
        public PerksData perksData = new();
        public ModifiersData activeModifiers = new();

        public CharacterDefinition Clone() {
            return new CharacterDefinition {
                id = id,
                displayName = displayName,
                role = role,
                race = race,
                @class = @class,
                attributes = new EntityAttributes(),
                stats = new EntityStats(),
                perksData = new PerksData(),
                activeModifiers = new ModifiersData()
            };
        }

        public string ID {
            get => id;
            set => id = value;
        }

        public string DisplayName {
            get => displayName;
            set => displayName = value;
        }

        public EntityAttributes Attributes {
            get => attributes;
            set => attributes = value;
        }

        public EntityStats Stats {
            get => stats;
            set => stats = value;
        }
    }

    [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();
    }
}
