using AutoMapper;
using SessionCompanion.Database.Repositories.Base;
using SessionCompanion.Database.Tables;
using SessionCompanion.Services.Base;
using SessionCompanion.Services.Interfaces;
using SessionCompanion.ViewModels.CharacterWeaponViewModels;

namespace SessionCompanion.Services.Services
{
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;

    using Microsoft.EntityFrameworkCore;
    using SessionCompanion.Extensions.EitherType;
    using SessionCompanion.ViewModels.ApiResponses;

    public class CharacterWeaponService : ServiceBase<CharacterWeaponViewModel, CharacterWeapon>, ICharacterWeaponService
    {
        public CharacterWeaponService(IMapper mapper, IRepository<CharacterWeapon> repository) : base(mapper, repository)
        { }

        /// <summary>
        /// Metoda pobiera listę broni konkretnej postaci
        /// </summary>
        /// <param name="characterId"> Id postaci </param>
        /// <returns> Lista broni posiadanych przez postać </returns>
        public async Task<List<CharacterWeaponWithWeaponDetailsViewModel>> GetCharacterWeaponsList(int characterId)
        {
            var characterWeapons = await Repository.Get().Where(w => w.CharacterId.Equals(characterId)).Include(w => w.Weapon).ToListAsync();

            var result = Mapper.Map<List<CharacterWeaponWithWeaponDetailsViewModel>>(characterWeapons);
            return result;
        }

        public async Task<Either<SuccessResponse,ErrorResponse>> ChangeCharacterWeapon(CharacterWeaponViewModel model)
        {
            // Dodaj optional rozbro postac
            var allWeapons = await Repository.Get(c => c.CharacterId.Equals(model.CharacterId)).AsNoTracking().ToListAsync();
            var weaponsInUse = allWeapons.Where(w => w.InUse.Equals(true)).ToList();

            var weapon = Mapper.Map<CharacterWeapon>(model);
            weapon.Id = allWeapons.Where(w => w.WeaponId.Equals(model.WeaponId)).Select(x => x.Id).FirstOrDefault();

            if (weaponsInUse.Count() == 0)
            {
                // no weapon in use
                // just use new one
                await Repository.Update(weapon);
                await Repository.Save();
                return new SuccessResponse("Weapon changed") { SuccessCode = 200 };
            }

            var weaponInBothHands = weaponsInUse.Where(w => w.HoldInLeftHand.Equals(true) && w.HoldInRightHand.Equals(true));

            if ((model.HoldInLeftHand && model.HoldInRightHand) || (weaponInBothHands.Count() > 0))
            {
                // our model weapon uses both hands
                // or there is weapon already in both hands
                foreach (var w in weaponsInUse)
                {
                    w.InUse = false;
                    w.HoldInLeftHand = false;
                    w.HoldInRightHand = false;
                    await Repository.Update(w);
                }

                await Repository.Update(weapon);
                await Repository.Save();
                return new SuccessResponse("Weapon changed") { SuccessCode = 200 };
            }

            var weaponsToChange = weaponsInUse.Where(w => w.HoldInLeftHand.Equals(model.HoldInLeftHand) && w.HoldInRightHand.Equals(model.HoldInRightHand));
            if (weaponsToChange.Count() == 1)
            {
                // there is weapon in the same hand set as our
                // we update it
                var weaponToChange = weaponsToChange.Single();
                weaponToChange.InUse = false;
                weaponToChange.HoldInLeftHand = false;
                weaponToChange.HoldInRightHand = false;
                await Repository.Update(weaponToChange);
                await Repository.Update(weapon);
                await Repository.Save();
                return new SuccessResponse("Weapon changed") { SuccessCode = 200 };
            }

            // weapon is armed in empty hand
            await Repository.Update(weapon);
            await Repository.Save();
            return new SuccessResponse("Weapon changed") { SuccessCode = 200 };
        }
    }
}