namespace Hims.Infrastructure.Services
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Dapper;
    using Domain.Repositories.UnitOfWork;
    using Domain.Services;
    using Hims.Domain.Entities;
    using Hims.Shared.DataFilters;
    using Hims.Shared.EntityModels;
    using Hims.Shared.UserModels;
    using Hims.Shared.UserModels.ProviderSchedule;
    using Newtonsoft.Json.Linq;

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

        /// <inheritdoc cref="IHealthCardService" />
        public HealthCardService(IUnitOfWork unitOfWork)
        {
            this.unitOfWork = unitOfWork;
        }

        public async Task<int> ActivateOrDeactivateCard(HealthCardModel model)
        {
           var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"select count(""IssueHealthCardId"") from ""IssueHealthCard"" where ""HealthCardId"" = {model.HealthCardId} ");
           
            if (checkIf > 0)
            {
                return -1;
            }
            var query = $@"UPDATE ""HealthCard""
	                           SET ""ModifiedBy""={model.CreatedBy}, ""ModifiedDate""=now(), ""Active""= {model.Active}
	                           WHERE ""HealthCardId""= {model.HealthCardId}";
            return await this.unitOfWork.Current.ExecuteAsync(query);
        }

        public async Task<IEnumerable<HealthCardModel>> FetchAllAsync(HealthCardModel model)
        {
            var where = $@" WHERE 1 = 1 ";
            if (model.Active != null)
            {
                where += $@" and hc.""Active"" is {((bool)model.Active ? "true" : "false")}";
            }
            var query = $@"select  count(hc.""HealthCardId"") over() as ""TotalItems"", hc.""HealthCardId"",hc.""Duration"",hc.""Amount"",hc.""AllowMembers"",hc.""ValidityType"", hc.""HealthCardName"", hc.""Active"", hc.""CreatedBy"", hc.""CreatedDate"",
                               hc.""ModifiedBy"", hc.""ModifiedDate"",
		                       C.""FullName"" as ""CreatedByName"",M.""FullName"" as ""ModifiedByName""
                             FROM ""HealthCard"" hc
                             left join ""Account"" C on C.""AccountId"" = hc.""CreatedBy""
                             left join ""Account"" M on M.""AccountId"" = hc.""ModifiedBy""{where}";
            if (model.PageIndex != null && model.PageSize != null)
            {
                model.PageIndex = model.PageIndex > 0 ? model.PageIndex - 1 : model.PageIndex;
                query += $@" limit {model.PageSize} offset {model.PageSize * model.PageIndex}";
            }

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

        public async Task<int> InsertAsync(HealthCardModel model)
        {
            var checkIf = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>($@"SELECT count(*) from ""HealthCard"" where  lower(""HealthCardName"") = '{model.HealthCardName.ToLower()}'");
            if (checkIf > 0)
            {
                return -1;
            }
            var healthCard = new HealthCard
            {
                HealthCardName = model.HealthCardName,
                Amount = model.Amount,
                AllowMembers = model.AllowMembers,
                ValidityType = model.ValidityType,
                Duration = model.Duration,
                Active = true,
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now
            };
            return await this.unitOfWork.HealthCards.InsertAsync(healthCard);
        }

        public async Task<int> UpdateAsync(HealthCardModel model)
        {
            var checkIf = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>($@"select count(*) from ""HealthCard"" where  lower (""HealthCardName"") = '{model.HealthCardName.ToLower()}' and ""HealthCardId"" <> {model.HealthCardId}");
            if (checkIf > 0)
            {
                return -1;
            }
            var record = await this.unitOfWork.HealthCards.FindAsync(m => m.HealthCardId == model.HealthCardId);
            if (record == null)
            {
                return -2;
            }
            record.ModifiedBy = model.CreatedBy;
            record.ModifiedDate = DateTime.Now;
            record.HealthCardName = model.HealthCardName;
            record.Amount = model.Amount;
            record.AllowMembers = model.AllowMembers;
            record.ValidityType = model.ValidityType;
            record.Duration = model.Duration;
            return await this.unitOfWork.HealthCards.UpdateAsync(record);
        }

        public async Task<IEnumerable<HealthCardHolderModel>> FetchAllIssuedCard(HealthCardHolderModel model)
        {
            var where = $@" where 1=1";
            if (model.PatientId > 0)
            {
                where += $@" and IHC.""PatientId"" = {model.PatientId}";
            }
            if (model.CardNumber != null)
            {
                where += $@" and IHC.""CardNumber"" ilike '%{model.CardNumber}%'";
            }
            if (model.HealthCardId > 0)
            {
                where += $@" and IHC.""HealthCardId"" = {model.HealthCardId}";
            }
            var query = $@"Select Pat.""FullName"" as ""PatientName"",Pat.""PatientId"",Pat.""Mobile"", HC.""HealthCardName"", P.""PayTypeName"",
                            IHC.""CreatedDate"",IHC.""ModifiedDate"",AC.""FullName"" as ""CreatedByName"", AM.""FullName"" as ""ModifiedByName"",
                        HC.""HealthCardId"",IHC.""PayTypeId"",IHC.""PaymentNumber"",IHC.""CardNumber"",
                                IHC.""IssuedDate""    ,HC.""Duration"" , HC.""ValidityType"",IHC.""IssueHealthCardId"",IHC.""ValidTill"",IHC.""CardNumber""
                            from ""IssueHealthCard"" IHC
                            join ""Patient"" Pat on Pat.""PatientId"" = IHC.""PatientId""
                            join ""HealthCard"" HC on HC.""HealthCardId""= IHC.""HealthCardId""
                            left join ""Account"" AC on AC.""AccountId"" = IHC.""CreatedBy""
                            left Join ""Account"" AM on AM.""AccountId"" = IHC.""ModifiedBy"" 
                            join ""PayType"" P on P.""PayTypeId""=IHC.""PayTypeId""{where}
                            order by IHC.""CreatedDate"" desc";

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

        /// <inheritdoc/>
        public async Task<int> AddNewCardAsync(HealthCardHolderModel model)
        {
            int issueHealthCardId = 0;
            var checkIssueCard = await this.unitOfWork.IssueHealthCards.FindAsync(x => x.IssueHealthCardId == model.IssueHealthCardId);
            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"select count(""IssueHealthCardId"") from ""IssueHealthCard"" where ""PatientId"" = {model.PatientId} and ""HealthCardId"" = {model.HealthCardId}");

            var checkCard = await this.unitOfWork.HealthCards.FindAsync(x => x.HealthCardId == model.HealthCardId);
            if (checkCard == null)
            {
                return 0;
            }
            var validityDate = DateTime.Now;

            switch (checkCard.ValidityType)
            {
                case "D":
                    validityDate = DateTime.Now.AddDays(checkCard.Duration);
                    break;
                case "W":
                    var daysToAdd = 7 * checkCard.Duration;
                    validityDate = DateTime.Now.AddDays(daysToAdd);
                    break;
                case "M":
                    validityDate = DateTime.Now.AddMonths(checkCard.Duration);
                    break;
                case "Y":
                    validityDate = DateTime.Now.AddYears(checkCard.Duration);
                    break;
            }
            var transaction = this.unitOfWork.BeginTransaction();
            if (model.IssueHealthCardId > 0)
            {
                checkIssueCard.IssueHealthCardId = model.IssueHealthCardId;
                checkIssueCard.CardNumber = await this.GetCardNumber();
                checkIssueCard.PatientId = model.PatientId;
                checkIssueCard.ModifiedBy = model.CreatedBy;
                checkIssueCard.ModifiedDate = DateTime.Now;
                checkIssueCard.IssuedDate = DateTime.Now;
                checkIssueCard.Active = true;
                checkIssueCard.PayTypeId = model.PayTypeId;
                checkIssueCard.PaymentNumber = model.PaymentNumber;
                checkIssueCard.NetAmount = model.NetAmount;
                checkIssueCard.ValidTill = validityDate;
                checkIssueCard.HealthCardId = model.HealthCardId;
                issueHealthCardId = await this.unitOfWork.IssueHealthCards.UpdateAsync(checkIssueCard, transaction);
            }
            else
            {
                if (checkIf > 0)
                {
                    return -1; //alrealy alloted card
                }
                var newCard = new IssueHealthCard
                {
                    //  IssueHealthCardId
                    HealthCardId = model.HealthCardId,
                    CardNumber = await this.GetCardNumber(),
                    PatientId = model.PatientId,
                    CreatedBy = model.CreatedBy,
                    CreatedDate = DateTime.Now,
                    IssuedDate = DateTime.Now,
                    Active = true,
                    PayTypeId = model.PayTypeId,
                    PaymentNumber = model.PaymentNumber,
                    NetAmount = model.NetAmount,
                    ValidTill = validityDate
                    //remaining days 
                };
                 issueHealthCardId = await this.unitOfWork.IssueHealthCards.InsertAsync(newCard, transaction);
                if (issueHealthCardId < 0)
                {
                    transaction.Rollback();
                    return -2;
                }
            }           
            try
            {
                if (!string.IsNullOrEmpty(model.DependentPatientIds) && issueHealthCardId > 0)
                {
                    var ids = model.DependentPatientIds.Split(',').ToList();
                    int issueCard = 0;
                    if(model.IssueHealthCardId>0)
                    {
                        issueCard = model.IssueHealthCardId;
                    }
                    else
                    {
                        issueCard = issueHealthCardId;                        
                    }
                    var members = ids.Select(x => new HealthCardMember
                    {
                        DependentPatientId = Convert.ToInt32(x),
                        IssueHealthCardId = issueCard
                        // IssueHealthCardId = issueHealthCardId
                    }) ;
                    if (model.IssueHealthCardId > 0)
                    {
                        int a = await this.DeleteAsync(model.IssueHealthCardId);
                    }
                    //await this.unitOfWork.HealthCardMembers.DeleteAsync()
                    var memberResponse = await this.unitOfWork.HealthCardMembers.BulkInsertAsync(members, transaction);
                    if (memberResponse <= 0)
                    {
                        transaction.Rollback();
                        return -2;
                    }
                }
            }
            catch (Exception ex)
            {
                throw;
            }
            transaction.Commit();
            return issueHealthCardId;
        }
        public  Task<int> DeleteAsync(int IssueHealthCardId)
        {
            var qry = $@"delete from ""HealthCardMember""  where ""IssueHealthCardId""={IssueHealthCardId}";
            return this.unitOfWork.Current.ExecuteAsync(qry);
        }

            /// <inheritdoc/>
            public async Task<IEnumerable<PatientModel>> FetchCardBeneficiariesAsync(HealthCardHolderModel model)
        {
            var query = $@"select * from ""Patient"" where ""PatientId"" in (select ""DependentPatientId"" from ""HealthCardMember"" where ""IssueHealthCardId"" = {model.IssueHealthCardId})";
            return await this.unitOfWork.Current.QueryAsync<PatientModel>(query);
        }

        /// <inheritdoc/>
        public async Task<IEnumerable<HealthCardHolderModel>> CheckHealthCardForPatient(HealthCardHolderModel model)
        {
            var where = " where 1=1";

            if (model.PatientId > 0)
            {
                where += $@" and (IHC.""PatientId"" = {model.PatientId} or IHC.""IssueHealthCardId"" in (select distinct ""IssueHealthCardId"" from ""HealthCardMember"" where ""DependentPatientId"" = {model.PatientId}))";
            }
            if (model.HealthCardId > 0)
            {
                where += $@" and IHC.""HealthCardId"" = {model.HealthCardId}";
            }
            if (model.CardNumber != null)
            {
                where += $@" and IHC.""CardNumber"" ilike '%{model.CardNumber}%'";
            }
            if (model.PayTypeId > 0)
            {
                where += $@" and IHC.""PayTypeId"" ={model.PayTypeId}";
            }
            if (!string.IsNullOrEmpty(model.FromDate))
            {
                where += $@" and IHC.""CreatedDate""::date >= '{Convert.ToDateTime(model.FromDate):yyyy-MM-dd}'::date";
            }

            if (!string.IsNullOrEmpty(model.ToDate))
            {
                where += $@" and IHC.""CreatedDate""::date <= '{Convert.ToDateTime(model.ToDate):yyyy-MM-dd}'::date";
            }
            var query = $@"Select distinct IHC.""IssueHealthCardId"",Pat.""FullName"" as ""PatientName"",Pat.""Mobile"", HC.""HealthCardName"", P.""PayTypeName"",
                            IHC.""CreatedDate"",IHC.""ModifiedDate"",AC.""FullName"" as ""CreatedByName"", AM.""FullName"" as ""ModifiedByName""
                                ,IHC.""IssuedDate"",HC.""Duration"" , HC.""ValidityType"",IHC.""IssueHealthCardId"",IHC.""ValidTill"",IHC.""CardNumber"",
                                IHC.""PatientId""
                            from ""IssueHealthCard"" IHC
                            join ""Patient"" Pat on Pat.""PatientId"" = IHC.""PatientId""
                            join ""HealthCard"" HC on HC.""HealthCardId""= IHC.""HealthCardId""
                            left join ""Account"" AC on AC.""AccountId"" = IHC.""CreatedBy""
                            left Join ""Account"" AM on AM.""AccountId"" = IHC.""ModifiedBy"" 
                            join ""PayType"" P on P.""PayTypeId""=IHC.""PayTypeId""
                            {where}
                            and IHC.""ValidTill""::date >= '{DateTime.Now.ToString("yyyy-MM-dd")}'::date                            
                            order by IHC.""CreatedDate"" desc";

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

        /// <summary>
        /// The get bill number.
        /// </summary>
        /// <returns>
        /// The <see cref="Task"/>.
        /// </returns>
        private async Task<string> GetCardNumber()
        {
            var cardQuery = $@"SELECT ""CardNumber"" FROM ""IssueHealthCard""
                                        ORDER BY ""IssueHealthCardId"" DESC LIMIT 1";
            var oldNumber = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<string>(cardQuery);
            if (string.IsNullOrEmpty(oldNumber))
            {
                // LAAB2021040101
                return "HC" + CoreFilter.Random(3) + DateTime.Now.ToString("yyyyMMdd") + "1";
            }
            else
            {
                var stringLiterals = oldNumber.Substring(0, 4);
                var previousBillDateWithCount = oldNumber.Substring(4);
                var counter = previousBillDateWithCount.Substring(8);
                var date = previousBillDateWithCount.Substring(0, 8);
                if (date != DateTime.Now.ToString("yyyyMMdd"))
                {
                    return "HC" + CoreFilter.Random(3) + DateTime.Now.ToString("yyyyMMdd") + "1";
                }

                counter = (int.Parse(counter) + 1).ToString();
                return stringLiterals + date + counter;
            }
        }

        public async Task<IEnumerable<HealthCardHolderModel>> FetchPatientIssuedCard(int PatientId)
        {
            var where = $@" where 1=1";
            if (PatientId > 0)
            {
                where += $@" and t1.""PatientId"" = {PatientId}";
            }
            //var qry = $@"select *  from (select ""PatientId"",""IssueHealthCardId"" from ""IssueHealthCard"" 
            //Union
            //select ""DependentPatientId"",""IssueHealthCardId"" from ""HealthCardMember"") as t1 {where}";
            var qry = $@"select 
                IH.""IssueHealthCardId"", 
                *  from (select ""PatientId"",""IssueHealthCardId"" from ""IssueHealthCard"" 
                Union
                select ""DependentPatientId"",""IssueHealthCardId"" from ""HealthCardMember"") as t1
                 join ""IssueHealthCard"" IH on IH.""IssueHealthCardId""=t1.""IssueHealthCardId"" { where}";
            //var qry = $@"select * from ""IssueHealthCard"" {where}";
            return await this.unitOfWork.Current.QueryAsync<HealthCardHolderModel>(qry);
        }
    }
}