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

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

        /// <summary>
        /// The amazon s3 configuration.
        /// </summary>
        private readonly IAmazonS3Configuration amazonS3Configuration;

        /// <inheritdoc cref="IInsuranceAdmissionService" />
        public InsuranceAdmissionServices(IUnitOfWork unitOfWork, IAmazonS3Configuration amazonS3Configuration)
        {
            this.unitOfWork = unitOfWork;
            this.amazonS3Configuration = amazonS3Configuration;
        }

        ///<inheritdoc/>
        public async Task<int> AddInsuranceForAdmission(AdmissionInsuranceModel model)
        {
            var checkIf = await this.GetInsuranceForAdmission((int)model.AdmissionId);
            if (checkIf != null)
            {
                return -1;
            }

            var admissonModel = new InsuranceForAdmission
            {
                AdmissionId = (int)model.AdmissionId,
                CreatedBy = (int)model.CreatedBy,
                CreatedDate = DateTime.Now,
                ExpectedAmount = (int)model.ExpectedAmount,
                ExpectedSettlementDate = model.ExpectedSettlementDate,
                Status = "Pending",
                InsuranceCompanyId = (int)model.InsuranceCompanyId
            };
            return await this.unitOfWork.InsuranceForAdmissions.InsertAsync(admissonModel);
        }

        ///<inheritdoc/>
        public async Task<int> UpdateInsuranceForAdmission(AdmissionInsuranceModel model)
        {
            var data = await this.unitOfWork.InsuranceForAdmissions.FindAsync(i => i.InsuranceForAdmissionId == model.InsuranceForAdmissionId);
            if (data == null)
            {
                return -2;
            }

            data.ExpectedAmount = (int)model.ExpectedAmount;
            data.InsuranceCompanyId = (int)model.InsuranceCompanyId;
            data.ModifiedBy = model.CreatedBy;
            data.ModifiedDate = DateTime.Now;
            data.ExpectedSettlementDate = model.ExpectedSettlementDate;

            return await this.unitOfWork.InsuranceForAdmissions.UpdateAsync(data);
        }

        ///<inheritdoc/>
        public async Task<InsuranceForAdmission> GetInsuranceForAdmission(int admissionId)
        {
            return await this.unitOfWork.InsuranceForAdmissions.FindAsync(m => m.AdmissionId == admissionId);
        }

        ///<inheritdoc/>
        public async Task<IEnumerable<AdmissionInsuranceModel>> FetchAll(AdmissionInsuranceModel model)
        {

            var where = " where 1=1";
            var query = $@"SELECT count(IFA.*) over() as ""TotalItems"", IFA.""InsuranceForAdmissionId"", IFA.""InsuranceCompanyId"",IFA.""Status"", 
                            IFA.""AdmissionId"", IFA.""ExpectedAmount"", IFA.""ExpectedSettlementDate"", 
                            IFA.""CreatedBy"", IFA.""CreatedDate"", IFA.""ModifiedBy"", IFA.""ModifiedDate"",
                            IC.""FullName"" as ""InsuranceCompanyName"",A.""AdmissionDate"",A.""AdmissionNo"",
                            P.""FullName"" as ""PatientName"",P.""Gender"",P.""Age"",P.""UMRNo"",
                            (CASE WHEN P.""ThumbnailUrl"" IS NOT NULL THEN CONCAT('{this.amazonS3Configuration.BucketURL}', P.""Guid"", '/', P.""ThumbnailUrl"") ELSE '' END) AS ""ThumbnailUrl"",
                            C.""FullName"" as ""CreatedByName"",CR.""RoleName"" as ""CreatedByRole"",
                            M.""FullName"" as ""ModifiedByName"",MR.""RoleName"" as ""ModifiedByRole""
	                            FROM ""InsuranceForAdmission"" IFA
	                            join ""InsuranceCompany"" IC on IC.""InsuranceCompanyId"" = IFA.""InsuranceCompanyId""
	                            join ""Admission"" A on A.""AdmissionId"" = IFA.""AdmissionId""
	                            join ""Patient"" P on P.""PatientId"" = A.""PatientId""
	                            join ""Account"" C on C.""AccountId"" = IFA.""CreatedBy""
	                            join ""Role"" CR on CR.""RoleId"" = C.""RoleId""
	                            left join ""Account"" M on M.""AccountId"" = IFA.""ModifiedBy""
	                            left join ""Role"" MR on MR.""RoleId"" = M.""RoleId""
	                            {where}
	                            order by IFA.""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.PageSize * model.PageIndex}";
            }

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

        ///<inheritdoc/>
        public async Task<int> InsertInsuranceTimeline(AdmissionInsuranceModel model)
        {
            var timeline = new InsuranceTimeline
            {
                Attachments = model.Attachments,
                Comment = model.Comment,
                CreatedBy = (int)model.CreatedBy,
                InsuranceForAdmissionId = (int)model.InsuranceForAdmissionId,
                CreatedDate = DateTime.Now
            };

            return await this.unitOfWork.InsuranceTimelines.InsertAsync(timeline);
        }

        ///<inheritdoc/>
        public async Task<IEnumerable<AdmissionInsuranceModel>> FetchAllInsuranceTimelineAsync(int insuranceForAdmissionId)
        {
            var query = $@"SELECT IT.""InsuranceTimelineId"", IT.""InsuranceForAdmissionId"", IT.""Comment"", IT.""Attachments"", IT.""CreatedBy"", IT.""CreatedDate"",
                                   A.""FullName"" as ""CreatedByName"",R.""RoleName"" as ""CreatedByRole""
	                                 FROM ""InsuranceTimeline"" IT
	                                 join ""Account"" A on A.""AccountId"" = IT.""CreatedBy""
	                                 join ""Role"" R on R.""RoleId"" = A.""RoleId""
		                             where IT.""InsuranceForAdmissionId"" = {insuranceForAdmissionId}";

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

        ///Inusurance approval services
        ///<inheritdoc/>
        public async Task<InsuranceApproval> GetInsuranceAdmissionApproval(int admissionId, int patientInsuranceId)
        {
            return await this.unitOfWork.InsuranceApproval.FindAsync(m => m.AdmissionId == admissionId && m.PatientInsuranceId == patientInsuranceId);
        }

        ///Inusurance approval services
        ///<inheritdoc/>
        public async Task<InsuranceApproval> GetInsuranceAppointmentApproval(int appointmentId, int patientInsuranceId)
        {
            return await this.unitOfWork.InsuranceApproval.FindAsync(m => m.AppointmentId == appointmentId && m.PatientInsuranceId == patientInsuranceId);
        }

        ///Inusurance approval services
        ///<inheritdoc/>
        public async Task<IEnumerable<AdmissionInsuranceModel>> FetchInsuranceApprovalsByAdmissionIdAysnc(int admissionId)
        {
            var query = $@"Select ic.""FullName"" as ""InsuranceCompanyName"",ip.""ExpectedAmount"" from ""InsuranceApproval"" ip
                            join ""PatientInsurance"" pi on pi.""PatientInsuranceId"" = ip.""PatientInsuranceId""
                            join ""InsuranceCompany"" ic on ic.""InsuranceCompanyId"" = pi.""InsuranceCompanyId""
                            where ip.""AdmissionId"" = {admissionId}";
            return await this.unitOfWork.Current.QueryAsync<AdmissionInsuranceModel>(query);
        }

        ///Inusurance approval services
        ///<inheritdoc/>
        public async Task<IEnumerable<AdmissionInsuranceModel>> FetchInsuranceApprovalsByAppointmentIdAysnc(int appointmentId)
        {
            var query = $@"Select ic.""FullName"" as ""InsuranceCompanyName"",ip.""ExpectedAmount"" from ""InsuranceApproval"" ip
                            join ""PatientInsurance"" pi on pi.""PatientInsuranceId"" = ip.""PatientInsuranceId""
                            join ""InsuranceCompany"" ic on ic.""InsuranceCompanyId"" = pi.""InsuranceCompanyId""
                            where ip.""AppointmentId"" = {appointmentId}";
            return await this.unitOfWork.Current.QueryAsync<AdmissionInsuranceModel>(query);
        }

        ///<inheritdoc/>
        public async Task<int> AddInsuranceApproval(AdmissionInsuranceModel model)
        {
            try
            {
                int insuranceApprovalId = 0;
                if (model.AdmissionId > 0)
                {
                    var checkIf = await this.GetInsuranceAdmissionApproval((int)model.AdmissionId, (int)model.PatientInsuranceId);
                    if (checkIf != null)
                    {
                        return -1;
                    }

                    var approvalModel = new InsuranceApproval
                    {
                        AdmissionId = (int)model.AdmissionId,
                        CreatedBy = (int)model.CreatedBy,
                        CreatedDate = DateTime.Now,
                        ExpectedAmount = (int)model.ExpectedAmount,
                        ExpectedSettlementDate = model.ExpectedSettlementDate,
                        PatientInsuranceId = (int)model.PatientInsuranceId
                    };
                    insuranceApprovalId = await this.unitOfWork.InsuranceApproval.InsertAsync(approvalModel);
                }
                if (model.AppointmentId > 0)
                {
                    var checkIf = await this.GetInsuranceAppointmentApproval((int)model.AppointmentId, (int)model.PatientInsuranceId);
                    if (checkIf != null)
                    {
                        return -1;
                    }

                    var approvalModel = new InsuranceApproval
                    {
                        AppointmentId = (int)model.AppointmentId,
                        CreatedBy = (int)model.CreatedBy,
                        CreatedDate = DateTime.Now,
                        ExpectedAmount = (int)model.ExpectedAmount,
                        ExpectedSettlementDate = model.ExpectedSettlementDate,
                        PatientInsuranceId = (int)model.PatientInsuranceId
                    };
                    insuranceApprovalId = await this.unitOfWork.InsuranceApproval.InsertAsync(approvalModel);
                }
                return insuranceApprovalId;
            }
            catch(Exception e)
            {

            }
            return 0;
        }

        ///<inheritdoc/>
        public async Task<int> UpdateInsuranceApproval(AdmissionInsuranceModel model)
        {
            var data = await this.unitOfWork.InsuranceApproval.FindAsync(i => i.InsuranceApprovalId == model.InsuranceApprovalId);
            if (data == null)
            {
                return -2;
            }

            data.ExpectedAmount = (int)model.ExpectedAmount;
            data.ModifiedBy = model.CreatedBy;
            data.ModifiedDate = DateTime.Now;
            data.ExpectedSettlementDate = model.ExpectedSettlementDate;

            return await this.unitOfWork.InsuranceApproval.UpdateAsync(data);
        }

        ///<inheritdoc/>
        public async Task<IEnumerable<AdmissionInsuranceModel>> FetchAppointmentInsuranceApprovals(AdmissionInsuranceModel model)
        {
            var where = " where 1=1";
            if (model.AppointmentId > 0)
            {
                where += $@" and IFA.""AppointmentId"" = {model.AppointmentId} ";
            }
            var query = $@"SELECT count(IFA.*) over() as ""TotalItems"",IFA.""ExpectedSettlementDate"", IFA.""InsuranceApprovalId"",PI.""PatientInsuranceId"" ,PI.""InsuranceCompanyId"",IFA.""Status"", 
                            IFA.""AdmissionId"", IFA.""ExpectedAmount"", IFA.""ExpectedSettlementDate"", 
                            IFA.""CreatedBy"", IFA.""CreatedDate"", IFA.""ModifiedBy"", IFA.""ModifiedDate"",
                            IC.""FullName"" as ""InsuranceCompanyName"",A.""AppointmentId"",
                            P.""FullName"" as ""PatientName"",P.""Gender"",P.""Age"",P.""UMRNo"",IT.""TypeName"" as ""InsuranceTypeName"",
                            (CASE WHEN P.""ThumbnailUrl"" IS NOT NULL THEN CONCAT('https://hims-devv.s3.amazonaws.com/', P.""Guid"", '/', P.""ThumbnailUrl"") ELSE '' END) AS ""ThumbnailUrl"",
                            C.""FullName"" as ""CreatedByName"",CR.""RoleName"" as ""CreatedByRole"",
                            M.""FullName"" as ""ModifiedByName"",MR.""RoleName"" as ""ModifiedByRole""
	                            FROM ""InsuranceApproval"" IFA
	                            join ""PatientInsurance"" PI on PI.""PatientInsuranceId"" = IFA.""PatientInsuranceId""
								join ""InsuranceCompany"" IC on IC.""InsuranceCompanyId"" = PI.""InsuranceCompanyId""
	                            join ""Appointment"" A on A.""AppointmentId"" = IFA.""AppointmentId""
                                join ""InsuranceType"" IT on IT.""InsuranceTypeId"" = PI.""InsuranceTypeId""
	                            join ""Patient"" P on P.""PatientId"" = A.""PatientId""
	                            join ""Account"" C on C.""AccountId"" = IFA.""CreatedBy""
	                            join ""Role"" CR on CR.""RoleId"" = C.""RoleId""
	                            left join ""Account"" M on M.""AccountId"" = IFA.""ModifiedBy""
	                            left join ""Role"" MR on MR.""RoleId"" = M.""RoleId""
                                 {where}
	                            order by IFA.""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.PageSize * model.PageIndex}";
            }

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

        ///<inheritdoc/>
        public async Task<IEnumerable<AdmissionInsuranceModel>> FetchAdmissionInsuranceApprovals(AdmissionInsuranceModel model)
        {

            var where = " where 1=1";
            if (model.AdmissionId > 0)
            {
                where += $@" and IFA.""AdmissionId"" = {model.AdmissionId} ";
            }
            var query = $@"SELECT count(IFA.*) over() as ""TotalItems"",IFA.""ExpectedSettlementDate"", IFA.""InsuranceApprovalId"",PI.""PatientInsuranceId"" ,PI.""InsuranceCompanyId"",IFA.""Status"", 
                            IFA.""AdmissionId"", IFA.""ExpectedAmount"", IFA.""ExpectedSettlementDate"", 
                            IFA.""CreatedBy"", IFA.""CreatedDate"", IFA.""ModifiedBy"", IFA.""ModifiedDate"",
                            IC.""FullName"" as ""InsuranceCompanyName"",A.""AdmissionDate"",A.""AdmissionNo"",IT.""TypeName"" as ""InsuranceTypeName"",
                            P.""FullName"" as ""PatientName"",P.""Gender"",P.""Age"",P.""UMRNo"",
                            (CASE WHEN P.""ThumbnailUrl"" IS NOT NULL THEN CONCAT('{this.amazonS3Configuration.BucketURL}', P.""Guid"", '/', P.""ThumbnailUrl"") ELSE '' END) AS ""ThumbnailUrl"",
                            C.""FullName"" as ""CreatedByName"",CR.""RoleName"" as ""CreatedByRole"",
                            M.""FullName"" as ""ModifiedByName"",MR.""RoleName"" as ""ModifiedByRole""
	                            FROM ""InsuranceApproval"" IFA
	                            join ""PatientInsurance"" PI on PI.""PatientInsuranceId"" = IFA.""PatientInsuranceId""
								join ""InsuranceCompany"" IC on IC.""InsuranceCompanyId"" = PI.""InsuranceCompanyId""
	                            join ""Admission"" A on A.""AdmissionId"" = IFA.""AdmissionId""
	                            join ""Patient"" P on P.""PatientId"" = A.""PatientId""
                                join ""InsuranceType"" IT on IT.""InsuranceTypeId"" = PI.""InsuranceTypeId""
                                join ""Account"" C on C.""AccountId"" = IFA.""CreatedBy""
	                            join ""Role"" CR on CR.""RoleId"" = C.""RoleId""
	                            left join ""Account"" M on M.""AccountId"" = IFA.""ModifiedBy""
	                            left join ""Role"" MR on MR.""RoleId"" = M.""RoleId""
                                {where}
	                            order by IFA.""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.PageSize * model.PageIndex}";
            }

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

    }
}