using System;
using System.Collections.Generic;
using System.Linq;

namespace Nox.Game {
    public interface IPartyFactory {
        PartyDefinition Create(CharacterDefinition protagonist, IEnumerable<CharacterDefinition> companions = null);
    }

    public sealed class PartyFactory : IPartyFactory {
        private readonly StarterCharacterSettings starterCharacterSettings;

        public PartyFactory(StarterCharacterSettings starterCharacterSettings) {
            this.starterCharacterSettings = starterCharacterSettings;
        }

        public PartyDefinition Create(CharacterDefinition protagonist, IEnumerable<CharacterDefinition> companions = null) {
            if(protagonist == null) {
                throw new ArgumentNullException(nameof(protagonist));
            }

            var party = new PartyDefinition {
                maxPartySize = starterCharacterSettings.maxPartySize <= 0 ? int.MaxValue : starterCharacterSettings.maxPartySize
            };

            var protagonistClone = protagonist.Clone();
            protagonistClone.Role = CharacterRole.Protagonist;
            party.members.Add(protagonistClone);

            if(companions != null) {
                foreach(var companion in companions.Where(c => c != null)) {
                    var companionClone = companion.Clone();
                    companionClone.Role = CharacterRole.Companion;
                    party.members.Add(companionClone);
                }
            }

            ValidateParty(party);
            return party;
        }

        private void ValidateParty(PartyDefinition party) {
            if(party.members.Count > party.maxPartySize) {
                throw new ArgumentException($"Party size {party.members.Count} exceeds max {party.maxPartySize}.");
            }

            var protagonistCount = party.members.Count(m => m.Role == CharacterRole.Protagonist);
            if(protagonistCount != 1) {
                throw new ArgumentException($"Party must contain exactly one protagonist, found {protagonistCount}.");
            }

            var uniqueIds = party.members
                .Where(m => m.Id != Guid.Empty)
                .Select(m => m.Id)
                .Distinct()
                .Count();
            if(uniqueIds != party.members.Count) {
                throw new ArgumentException("Party contains duplicate or missing character ids.");
            }
        }
    }
}
