using Jovian.InspectorTools;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Nox.Game {

    public interface IPerkFactory {
        IReadOnlyCollection<PerkDefinition> GetAll();
        PerkDefinition GetById(Guid perkId);
        IReadOnlyCollection<PerkDefinition> GetPerksFor(CharacterDefinition character);
        bool TryAddPerk(CharacterDefinition character, Guid perkId);
    }

    [Serializable]
    public sealed class PerkDefinition {
        public string name;
        [ReadOnly]
        public Guid id;
        public ModifiersData modifiers = new ();
    }

    [Serializable]
    public sealed class PerksData {
        public List<PerkDefinition> perks = new ();
    }

    public sealed class PerkFactory : IPerkFactory {
        private readonly Dictionary<Guid, PerkDefinition> perkPool = new ();

        public PerkFactory(PerksRegistry perksRegistry) {
            if(!perksRegistry) {
                throw new ArgumentNullException(nameof(perksRegistry));
            }
            var allAvailablePerks = perksRegistry.perksData;
            foreach(var perk in allAvailablePerks.perks) {
                perkPool.Add(perk.id, perk);
            }
        }

        public IReadOnlyCollection<PerkDefinition> GetAll() {
            return perkPool.Values.ToList();
        }

        public PerkDefinition GetById(Guid perkId) {
            perkPool.TryGetValue(perkId, out var perk);
            return perk;
        }

        public IReadOnlyCollection<PerkDefinition> GetPerksFor(CharacterDefinition character) {
            if(character == null) {
                return perkPool.Values.ToList();
            }

            var ownedPerkIds = character.perksData.perks
                .Select(p => p.id)
                .ToHashSet();

            return perkPool.Values.Where(p => !ownedPerkIds.Contains(p.id)).ToList();
        }

        public bool TryAddPerk(CharacterDefinition character, Guid perkId) {
            if(character == null) {
                return false;
            }

            if(character.perksData.perks.Any(p => p != null && p.id == perkId)) {
                return false;
            }

            if(!perkPool.TryGetValue(perkId, out var perk)) {
                return false;
            }

            character.perksData.perks.Add(new PerkDefinition {
                id = perk.id,
                name = perk.name
            });
            return true;
        }
    }
}
