namespace Hims.Infrastructure.Services
{
    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Dapper;
    using Domain.Entities;
    using Domain.Repositories.UnitOfWork;
    using Hims.Domain.Services;
    using Shared.UserModels.MealType;

    /// <inheritdoc />
    public class MealTypesService : IMealTypesService
    {
        /// <summary>
        /// The unit of work.
        /// </summary>
        private readonly IUnitOfWork unitOfWork;

        /// <inheritdoc cref="IMealTypesService" />
        public MealTypesService(IUnitOfWork unitOfWork) => this.unitOfWork = unitOfWork;

        /// <inheritdoc />
        public async Task<int> AddAsync(InsertModel model)
        {
            var transaction = this.unitOfWork.BeginTransaction();

            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""MealTypeId"") FROM ""MealTypes"" WHERE TRIM(UPPER(""MealType"")) = '{model.MealType.ToUpper().Trim()}'");
            if (checkIf > 0)
            {
                return -2;
            }            
            var mealTypes = new MealTypes
                {
                    Active = true,
                    MealType = model.MealType,
                    MealInstruction = model.MealInstruction,
                    CreatedBy = model.CreatedBy,
                    CreatedDate = DateTime.Now,
                };

            var mealtypeId =await this.unitOfWork.MealTypes.InsertAsync(mealTypes,transaction);           
            if (mealtypeId <= 0)
            {
                transaction.Rollback();
                return -1;
            }
            if (model.LocationIds != null)
            {
                var ids = model.LocationIds.Split(',');
                foreach (var locationId in ids)
                {

                    var location = new LocationMealTypesMap
                    {
                        MealTypeId = mealtypeId,
                        LocationId = Convert.ToInt32(locationId)
                    };
                    var locationInsertId = await this.unitOfWork.LocationMealTypesMap.InsertAsync(location, transaction);
                    if (locationInsertId == 0)
                    {
                        transaction.Rollback();
                        return 0;
                    }
                }
            }
            transaction.Commit();
            return mealtypeId;
        }

        public async Task<IEnumerable<ViewModel>> FetchAsync(ViewModel model)
        {           
            var query = $@"SELECT MT.""MealTypeId"",MT.""MealType"",A.""FullName"" as ""CreatedByName"",M.""FullName"" as ""ModifiedByName"",MT.""MealInstruction"",MT.""CreatedBy"",MT.""CreatedDate"",MT.""ModifiedBy"",MT.""ModifiedDate"",MT.""Active""
                           , string_agg(L.""Name"", ', ') as ""LocationNames"",string_agg(L.""LocationId""::text, ', ') as ""LocationIds""

                            FROM ""MealTypes"" MT
                            LEFT JOIN ""Account"" A on A.""AccountId"" = MT.""CreatedBy""
                            LEFT JOIN ""Account"" M on M.""AccountId"" = MT.""ModifiedBy"" 
                             left join ""LocationMealTypesMap"" LMT ON LMT.""MealTypeId"" = MT.""MealTypeId""
                             left join ""Location"" L ON L.""LocationId"" = LMT.""LocationId""
                    group by MT.""MealTypeId"",A.""FullName"",M.""FullName""
                            order by MT.""CreatedDate"" desc";
            if (model.PageIndex != null && model.PageSize != null)
            {
                model.PageIndex = model.PageIndex > 0 ? model.PageIndex - 1 : model.PageIndex;
                query += $@" limit {model.PageSize} offset {model.PageIndex * model.PageSize}";
            }

            return await this.unitOfWork.Current.QueryAsync<ViewModel>(query);
        }

        public async Task<IEnumerable<ViewModel>> FetchActiveMealTypes(ViewModel model)
        {
            var where = "where 1=1";
            var query = $@"SELECT MT.""MealTypeId"",MT.""MealType"",A.""FullName"" as ""CreatedByName"",M.""FullName"" as ""ModifiedByName"",MT.""MealInstruction"",MT.""CreatedBy"",MT.""CreatedDate"",MT.""ModifiedBy"",MT.""ModifiedDate"",MT.""Active"" FROM ""MealTypes"" MT
                            LEFT JOIN ""Account"" A on A.""AccountId"" = MT.""CreatedBy""
                            LEFT JOIN ""Account"" M on M.""AccountId"" = MT.""ModifiedBy"" 
                               {where} AND  MT.""Active"" is True  order by MT.""CreatedDate"" desc ";
            return await this.unitOfWork.Current.QueryAsync<ViewModel>(query);
        }

        public async Task<int> UpdateAsync(UpdateModel model)
        {
            var transaction = this.unitOfWork.BeginTransaction();
            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""MealTypeId"") FROM ""MealTypes"" WHERE TRIM(UPPER(""MealType"")) = '{model.MealType.ToUpper().Trim()}' AND ""MealTypeId"" <> {model.MealTypeId}");
            if (checkIf > 0)
            {
                return -2;
            }

            var mealTypes = await this.unitOfWork.MealTypes.FindAsync(m => m.MealTypeId == model.MealTypeId);
            mealTypes.MealType = model.MealType;
            mealTypes.MealInstruction = model.MealInstruction;
            mealTypes.Active = true;
            mealTypes.ModifiedBy = model.ModifiedBy;
            mealTypes.ModifiedDate = DateTime.Now;

            var updateResponse = await this.unitOfWork.MealTypes.UpdateAsync(mealTypes,transaction);        
            if (updateResponse <= 0)
            {
                transaction.Rollback();
                return -1;
            }
        
            var query = $@"DELETE from ""LocationMealTypesMap"" where ""MealTypeId""= {mealTypes.MealTypeId} ";
            var res = await this.unitOfWork.Current.QuerySingleOrDefaultAsync(query, transaction);
            if (res == 0)
            {
                transaction.Rollback();
                return -1;
            }

            if (model.LocationIds != null)
            {
                var ids = model.LocationIds.Split(',');
                foreach (var locationId in ids)
                {

                    var location = new LocationMealTypesMap
                    {
                        MealTypeId = model.MealTypeId,
                        LocationId = Convert.ToInt32(locationId)
                    };
                    var locationInsertId = await this.unitOfWork.LocationMealTypesMap.InsertAsync(location, transaction);
                    if (locationInsertId == 0)
                    {
                        transaction.Rollback();
                        return -1;
                    }
                }
            }
            transaction.Commit();
            return updateResponse;
        }

        public async Task<int> ModifyMealStatusAsync(UpdateModel model)
        {
            var query = $@"UPDATE ""MealTypes""
	                           SET ""ModifiedBy""={model.ModifiedBy}, ""ModifiedDate""=now(), ""Active""= {model.Active}
	                           WHERE ""MealTypeId""= {model.MealTypeId}";

            return await this.unitOfWork.Current.ExecuteAsync(query);
        }
    }
}