using Dapper;

namespace Hims.Infrastructure.Services
{
    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Hims.Domain.Entities;
    using Hims.Domain.Repositories.UnitOfWork;
    using Hims.Domain.Services;
    using Hims.Shared.EntityModels;
    using Hims.Shared.UserModels.Discharge;
    using Hims.Shared.UserModels.Slots;
    using Resource = Hims.Shared.UserModels.Discharge.Resource;

    /// <summary> The chat service.</summary>
    public class DischargeService : IDischargeService
    {
        /// <summary>
        /// The unit of work.
        /// </summary>
        private readonly IUnitOfWork unitOfWork;

        /// <inheritdoc cref="IChatService" />
        public DischargeService(IUnitOfWork unitOfWork) => this.unitOfWork = unitOfWork;


        public async Task<int> InsertAsync(InsertModel model)
        {
            var record = new Discharge
            {
                Active = model.Active,
                AdmissionId = model.AdmissionId,
                CreatedBy = model.CreatedBy,
                CreatedDate = model.CreatedDate,
                ConditionOnDischarge = model.ConditionOnDischarge,
                Diet = model.Diet,
                DischargeDate = model.DischargeDate,
                DischargeFollowUpDate = model.DischargeFollowUpDate,
                FollowUpSummary = model.FollowUpSummary,
                DischargeFollowUpDays = model.DischargeFollowUpDays,
                DischargeInstructionId = model.DischargeInstructionId,
                DischargeStatusId = model.DischargeStatusId,
                DischargeSummary = model.DischargeSummary,
                DischargeTime = model.DischargeDate.TimeOfDay,
                FinalDiagnosis = model.FinalDiagnosis,
                HistoryOfIllness = model.HistoryOfIllness,
                IsDeath = model.IsDeath,
                //DischargeInstructionName = model.DischargeInstructionName,
            };

            if (record.IsDeath == true)
            {
                record.DeathDate = model.DeathDate;
                record.DeathTime = model.DeathDate?.TimeOfDay;
                record.CauseOfDeath = model.CauseOfDeath;
            }

            var response = await this.unitOfWork.Discharge.InsertAsync(record);
            return response;
        }

        public async Task<int> UpdateAsync(UpdateModel model)
        {
            var record = await this.unitOfWork.Discharge.FindAsync(x => x.DischargeId == model.DischargeId);

            record.ModifiedBy = model.ModifiedBy;
            record.ModifiedDate = model.ModifiedDate;
            record.ConditionOnDischarge = model.ConditionOnDischarge;
            record.Diet = model.Diet;
            record.DischargeDate = model.DischargeDate;
            record.DischargeFollowUpDate = model.DischargeFollowUpDate;
            record.FollowUpSummary = model.FollowUpSummary;
            record.DischargeFollowUpDays = model.DischargeFollowUpDays;
            record.DischargeInstructionId = model.DischargeInstructionId;
            record.DischargeStatusId = model.DischargeStatusId;
            record.DischargeSummary = model.DischargeSummary;
            record.DischargeTime = model.DischargeDate.TimeOfDay;
            record.FinalDiagnosis = model.FinalDiagnosis;
            record.HistoryOfIllness = model.HistoryOfIllness;
            record.IsDeath = model.IsDeath;
            //record.DischargeInstructionName = model.DischargeInstructionName;

            if (record.IsDeath == true)
            {
                record.DeathDate = model.DeathDate;
                record.DeathTime = model.DeathDate?.TimeOfDay;
                record.CauseOfDeath = model.CauseOfDeath;
            }
            else
            {
                record.DeathDate = null;
                record.DeathTime = null;
                record.CauseOfDeath = null;
            }

            var response = await this.unitOfWork.Discharge.UpdateAsync(record);
            return response;
        }

        public async Task<IEnumerable<Resource.ViewModel>> FetchDischargeInstructionsAsync()
        {
            var query = $@"SELECT
	                        ""DischargeInstructionId"" AS ""Id"",
	                        ""DischargeInstructionName"" AS ""Name"" 
                        FROM
	                        ""DischargeInstruction"" where ""Active"" = true";
            var records = await this.unitOfWork.Current.QueryAsync<Resource.ViewModel>(query);
            return records;
        }

        public async Task<int> UpdateBedStatusAsync(int admissionId)
        {
            var admission = await this.unitOfWork.Admission.FindAsync(x => x.AdmissionId == admissionId);
            if (admission?.BedId == null) return 1;

            var query1 = $@"update ""Bed"" set ""BedStatusId"" = 1 where ""BedId""= {admission.BedId} ";
            await this.unitOfWork.Current.ExecuteAsync(query1);

            return 1;
        }

        public async Task<IEnumerable<Resource.ViewModel>> FetchDischargeStatusAsync()
        {
            var query = $@"SELECT
	                        ""DischargeStatusId"" AS ""Id"",
	                        ""DischargeStatus"" AS ""Name"" 
                        FROM
	                        ""DischargeStatus""";
            var records = await this.unitOfWork.Current.QueryAsync<Resource.ViewModel>(query);
            return records;
        }

        public async Task<Shared.UserModels.Discharge.ViewModel> FetchAsync(FilterModel model)
        {
            var query = $@"SELECT
	                            d.""DischargeId"",
	                            d.""DischargeDate"",
	                            d.""DischargeTime"",
	                            d.""DischargeStatusId"",
	                            s.""DischargeStatus"",
                                d.""DischargeInstructionId"",
                                (select string_agg(""DischargeInstructionName"",',') ""DischargeInstructionName""  from ""DischargeInstruction"" where ""DischargeInstructionId"" in(
                                select UNNEST(string_to_array(dd.""DischargeInstructionId"",','))::int from ""Discharge"" dd where dd.""AdmissionId"" = d.""AdmissionId"")),
	                            d.""DischargeSummary"",
	                            d.""ConditionOnDischarge"",
	                            d.""FinalDiagnosis"",
	                            d.""HistoryOfIllness"",
	                            d.""Diet"",
	                            d.""DischargeFollowUpDate"",
	                            d.""DischargeFollowUpDays"",
	                            d.""FollowUpSummary"",
	                            d.""IsDeath"",
	                            d.""CauseOfDeath"",
	                            d.""DeathDate"",
	                            d.""DeathTime"",
	                            C.""FullName"" AS ""CreatedByName"",
	                            d.""CreatedDate"",
	                            M.""FullName"" AS ""ModifiedByName"",
	                            d.""ModifiedDate"" 
                            FROM
	                            ""Discharge"" d
	                            JOIN ""DischargeStatus"" s ON s.""DischargeStatusId"" = d.""DischargeStatusId""                            
	                            JOIN ""Account"" C ON C.""AccountId"" = d.""CreatedBy""
	                            LEFT JOIN ""Account"" M ON M.""AccountId"" = d.""ModifiedBy""
	                            WHERE d.""Active"" IS TRUE AND d.""AdmissionId"" = {model.AdmissionId}";
            var record = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<Shared.UserModels.Discharge.ViewModel>(query);
            return record;
        }


        //new_Cur(d) methods
        public async Task<int> InsertDischargeAsync(InsertModel model)
        {
            //checking before Insertion is patient already Discharged.
            var Find = await this.unitOfWork.Discharge.FindAsync(m => m.AdmissionId == model.AdmissionId);
            if (Find != null)
            {
                return -1;
            }

            using (var transaction = this.unitOfWork.BeginTransaction())
            {
                var record = new Discharge
                {
                    Active = model.Active,
                    AdmissionId = model.AdmissionId,
                    CreatedBy = model.CreatedBy,
                    CreatedDate = model.CreatedDate,
                    ConditionOnDischarge = model.ConditionOnDischarge,
                    Diet = model.Diet,
                    DischargeDate = model.DischargeDate,
                    DischargeFollowUpDate = model.DischargeFollowUpDate,
                    FollowUpSummary = model.FollowUpSummary,
                    DischargeFollowUpDays = model.DischargeFollowUpDays,
                    DischargeInstructionId = model.DischargeInstructionId,
                    DischargeStatusId = model.DischargeStatusId,
                    DischargeSummary = model.DischargeSummary,
                    DischargeTime = model.DischargeDate.TimeOfDay,
                    FinalDiagnosis = model.FinalDiagnosis,
                    HistoryOfIllness = model.HistoryOfIllness,
                    IsDeath = model.IsDeath,
                    //DischargeInstructionName = model.DischargeInstructionName,
                    PastHistory = model.PastHistory,
                    DischargeAdvise = model.DischargeAdvise,
                    Surgery = model.Surgery,
                    // SurgeonName = model.SurgeonName.ToCharArray(),
                    GeneralExamination = model.GeneralExamination,
                    LocalExamination = model.LocalExamination,
                    HospitalCourse = model.HospitalCourse,
                    OperativeProcedure = model.OperativeProcedure,
                    OperativeNotes = model.OperativeNotes,
                    ConsultantSignature = model.ConsultantSignature
                };

                if (record.IsDeath == true)
                {
                    record.DeathDate = model.DeathDate;
                    record.DeathTime = model.DeathDate?.TimeOfDay;
                    record.CauseOfDeath = model.CauseOfDeath;
                };

                var dischargeId = await this.unitOfWork.Discharge.InsertAsync(record, transaction);
                var data = await this.unitOfWork.Admission.FindAsync(m => m.AdmissionId == model.AdmissionId);
                if (dischargeId > 0)
                {
                    var admission = new Admission
                    {
                        AdmissionNo = data.AdmissionNo,
                        LocationId = data.LocationId,
                        AdmissionId = model.AdmissionId,
                        DischargedBy = model.CreatedBy,
                        IsDischarged = true,
                        PatientId = data.PatientId,
                        Active = true,
                        AdmissionDate = data.AdmissionDate,
                        AdmissionTime = data.AdmissionTime,
                        ProviderId = data.ProviderId,
                        PatientType = data.PatientType,
                        IsMaternity = data.IsMaternity,
                        BabysBirthDate = data.BabysBirthDate,
                        BabysSurgeryType = data.BabysSurgeryType,
                        BabysGender = data.BabysGender,
                        BabysFathersName = data.BabysFathersName,
                        BabysMothersAdmissionNo = data.BabysMothersAdmissionNo,
                        EncounterId = data.EncounterId,
                        ReadyforDischarge = data.ReadyforDischarge,
                        AdmissionNotes = data.AdmissionNotes,
                        AttendantName = data.AttendantName,
                        AttendantContactNo = data.AttendantContactNo,
                        AttendantRelationWithPatient = data.AttendantRelationWithPatient,
                        ExpectedDischargeDate = data.ExpectedDischargeDate,
                        PatientPriorityId = data.PatientPriorityId,
                        DepartmentId = data.DepartmentId,
                        BedId = data.BedId,
                        PatientFamilyId = data.PatientFamilyId,
                        CreatedBy = data.CreatedBy,
                        VisitTypeId = data.VisitTypeId,
                        SurgeryTypeId = data.SurgeryTypeId,
                        IsConvertedFromOPtoIp = data.IsConvertedFromOPtoIp ?? false,
                        TpaId = data.TpaId,
                        CaseTypeId = data.CaseTypeId
                    };
                    await this.unitOfWork.Admission.UpdateAsync(admission, transaction);
                }
                if (model.SurgeonNamesId != null)
                {
                    var ids = model.SurgeonNamesId.Split(',');

                    if (dischargeId > 0)
                    {

                        foreach (var id in ids)
                        {
                            var surgeons = new Surgeons
                            {
                                //AdmissionId = model.AdmissionId,
                                Active = true,
                                CreatedDate = DateTime.Now,
                                CreatedBy = model.CreatedBy,
                                ProviderId = Convert.ToInt32(id),
                                DischargeId = dischargeId
                            };
                            await this.unitOfWork.Surgeons.InsertAsync(surgeons, transaction);
                        }
                    }
                }
                if (dischargeId <= 0)
                {
                    transaction.Rollback();
                    return -2;
                }
                transaction.Commit();

                return dischargeId;
            }
        }
        public async Task<Shared.UserModels.Discharge.ViewModel> FetchDischargeAsync(FilterModel model)
        {
            var query = $@"SELECT 
                                DISTINCT ON(d.""DischargeId"")
                                string_agg(Sg.""ProviderId""::text,',') OVER()  AS ""SurgeonNamesId"",
                                d.""DischargeId"",
	                            d.""DischargeDate"",
	                            d.""DischargeTime"",
	                            d.""DischargeStatusId"",
	                            s.""DischargeStatus"",
                                d.""DischargeInstructionId"",
                                (select string_agg(""DischargeInstructionName"",',') ""DischargeInstructionName""  from ""DischargeInstruction"" where ""DischargeInstructionId"" in(
                                select UNNEST(string_to_array(dd.""DischargeInstructionId"",','))::int from ""Discharge"" dd where dd.""AdmissionId"" = d.""AdmissionId"")),
	                            d.""DischargeSummary"",
	                            d.""ConditionOnDischarge"",
                                (select string_agg(P.""FullName""::text,',') ""SurgeonNames"" from  ""Provider"" P
                                 left join ""Surgeons"" Sg ON P.""ProviderId"" = Sg.""ProviderId""
                                left join ""Discharge"" dd ON dd.""DischargeId"" = Sg.""DischargeId""
                                WHERE Sg.""ProviderId"" in (select  S.""ProviderId"" from ""Surgeons"" S
                                                where S.""DischargeId"" = dd.""DischargeId"" AND dd.""AdmissionId"" = d.""AdmissionId"")),
	                            d.""FinalDiagnosis"", d.""DischargeAdvise"", d.""Surgery"", d.""ConsultantSignature"",
                                d.""HistoryOfIllness"", d.""PastHistory"", d.""GeneralExamination"", d.""LocalExamination"",
                                d.""Diet"", d.""HospitalCourse"",d.""OperativeProcedure"", d.""OperativeNotes"",
                                d.""DischargeFollowUpDate"",
	                            d.""DischargeFollowUpDays"",
	                            d.""FollowUpSummary"",
	                            d.""IsDeath"",
	                            d.""CauseOfDeath"",
	                            d.""DeathDate"",
	                            d.""DeathTime"",
	                            C.""FullName"" AS ""CreatedByName"",
	                            d.""CreatedDate"",
	                            M.""FullName"" AS ""ModifiedByName"",
	                            d.""ModifiedDate"",
                                A.""FullName"" AS ""ConsultantSignatureName""
                            FROM
	                            ""Discharge"" d
	                            JOIN ""DischargeStatus"" s ON s.""DischargeStatusId"" = d.""DischargeStatusId""                            
	                            JOIN ""Account"" C ON C.""AccountId"" = d.""CreatedBy""
                       left JOIN ""Surgeons"" Sg on d.""DischargeId""=Sg.""DischargeId""
                                LEFT JOIN ""Account"" M ON M.""AccountId"" = d.""ModifiedBy""
                                LEFT JOIN ""Provider"" P ON P.""ProviderId"" = d.""ConsultantSignature""
                                LEFT JOIN ""Account"" A ON A.""ReferenceId"" = P.""ProviderId"" and A.""RoleId"" = 3

                                WHERE d.""Active"" IS TRUE AND d.""AdmissionId"" = {model.AdmissionId}";
            var record = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<Shared.UserModels.Discharge.ViewModel>(query);
            return record;
        }

        public async Task<int> UpdateDischargeAsync(UpdateModel model)
        {
            using (var transaction = this.unitOfWork.BeginTransaction())
            {
                var record = await this.unitOfWork.Discharge.FindAsync(x => x.DischargeId == model.DischargeId);

                record.ModifiedBy = model.ModifiedBy;
                record.ModifiedDate = model.ModifiedDate;
                record.ConditionOnDischarge = model.ConditionOnDischarge;
                record.Diet = model.Diet;
                record.DischargeDate = model.DischargeDate;
                record.DischargeFollowUpDate = model.DischargeFollowUpDate;
                record.FollowUpSummary = model.FollowUpSummary;
                record.DischargeFollowUpDays = model.DischargeFollowUpDays;
                record.DischargeInstructionId = model.DischargeInstructionId;
                record.DischargeStatusId = model.DischargeStatusId;
                record.DischargeSummary = model.DischargeSummary;
                record.DischargeTime = model.DischargeDate.TimeOfDay;
                record.FinalDiagnosis = model.FinalDiagnosis;
                record.HistoryOfIllness = model.HistoryOfIllness;
                record.IsDeath = model.IsDeath;
                //record.DischargeInstructionName = model.DischargeInstructionName;
                record.PastHistory = model.PastHistory;
                record.DischargeAdvise = model.DischargeAdvise;
                record.Surgery = model.Surgery;
                // SurgeonName = model.SurgeonName.ToCharArray(),
                record.GeneralExamination = model.GeneralExamination;
                record.LocalExamination = model.LocalExamination;
                record.HospitalCourse = model.HospitalCourse;
                record.OperativeProcedure = model.OperativeProcedure;
                record.OperativeNotes = model.OperativeNotes;
                record.ConsultantSignature = model.ConsultantSignature;

                if (record.IsDeath == true)
                {
                    record.DeathDate = model.DeathDate;
                    record.DeathTime = model.DeathDate?.TimeOfDay;
                    record.CauseOfDeath = model.CauseOfDeath;
                }
                else
                {
                    record.DeathDate = null;
                    record.DeathTime = null;
                    record.CauseOfDeath = null;
                }

                var response = await this.unitOfWork.Discharge.UpdateAsync(record, transaction);

                if (model.SurgeonNamesId != null)
                {
                    var receivedProviderIds = model.SurgeonNamesId.Split(',');

                    var query = $@"DELETE from ""Surgeons"" where ""DischargeId""= {model.DischargeId} ";

                    var res = await this.unitOfWork.Current.QuerySingleOrDefaultAsync(query, transaction);
                    if (!String.IsNullOrEmpty(receivedProviderIds[0]))
                    {
                        if (model.DischargeId > 0)
                        {
                            foreach (var id in receivedProviderIds)
                            {
                                var surgeons = new Surgeons
                                {
                                    //AdmissionId = model.AdmissionId,
                                    Active = true,
                                    CreatedDate = DateTime.Now,
                                    CreatedBy = model.ModifiedBy,
                                    ProviderId = Convert.ToInt32(id),
                                    DischargeId = model.DischargeId
                                };
                                await this.unitOfWork.Surgeons.InsertAsync(surgeons, transaction);
                            }
                        }
                    }
                }

                //var providerIds = $@"SELECT string_agg(""ProviderId""::text,',')  ""SurgeonNamesId""  
                //                    from ""Surgeons""  where ""DischargeId"" = {model.DischargeId} ";

                //var surgeonResponse = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<string>(providerIds);

                //if (surgeonResponse != null)
                //{
                //    string[] existingSurgionIds = surgeonResponse.Split(',');

                //    var unique = new List<string>();

                //    foreach (var existing in existingSurgionIds)
                //    {
                //        var test = receivedProviderIds.AsList().Find(m => m == existing);
                //        if (!string.IsNullOrEmpty(test))
                //        {
                //            unique.Add(test);
                //        }
                //    }

                //}

                transaction.Commit();
                return response;
            }
        }

    }
}
