namespace Hims.Infrastructure.Services
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using Dapper;
    using Domain.Entities;
    using Domain.Repositories.UnitOfWork;
    using Domain.Services;
    using Hims.Shared.Library;
    using Hims.Shared.UserModels.ProgressReport.Vitals;
    using Labs = Hims.Shared.UserModels.ProgressReport.Labs;
    using MedicationInfo = Hims.Shared.UserModels.ProgressReport.MedicationInfo;
    using Resource = Hims.Shared.UserModels.VitalType;

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

        /// <inheritdoc cref="IBedService" />
        public ProgressReportVitalsService(IUnitOfWork unitOfWork) => this.unitOfWork = unitOfWork;

        public async Task<int> DeleteAsync(DeleteModel model)
        {
            var responseId = await this.unitOfWork.ProgressReportVitals.DeleteAsync(x => x.ProgressReportVitalsId == model.ProgressReportVitalsId);
            return responseId ? 1 : -1;
        }

        public async Task<IEnumerable<ViewModel>> FetchAsync(FilterModel model)
        {
            var dateWhere = model.Date != null
                ? $@" AND to_char(p.""Date"", 'YYYY-MM-DD' )::DATE = '{model.Date:yyyy-MM-dd}'::DATE "
                : string.Empty;

            var query = $@"SELECT
	                            p.""ProgressReportVitalsId"",
	                            p.""ProgressReportId"",
	                            p.""Date""::DATE,
	                            p.""VitalTypeId"",
                                p.""Vitals"",
                                p.""VitalTypeValue"",
                                p.""UnitName"",
	                            p.""CreatedDate"",
	                            c.""FullName"" ""CreatedByName"",
	                            p.""ModifiedDate"",
	                            m.""FullName"" ""ModifiedByName"",
                                CR.""RoleName""  ""CreatedByRole"",
                                MR.""RoleName""  ""ModifiedByRole""
                            FROM
	                            ""ProgressReportVitals""
	                            P JOIN ""ProgressReport"" r ON r.""ProgressReportId"" = P.""ProgressReportId""
	                            JOIN ""Account"" c on c.""AccountId"" = p.""CreatedBy""
	                            LEFT JOIN ""Account"" m on m.""AccountId"" = p.""ModifiedBy""
                                left JOIN ""Role"" CR on CR.""RoleId"" = c.""RoleId""
                                left JOIN ""Role"" MR on MR.""RoleId"" = m.""RoleId""

	                            WHERE r.""AdmissionId"" = {model.AdmissionId} {dateWhere}";
            var response = await this.unitOfWork.Current.QueryAsync<ViewModel>(query);
            return response;
        }

        public async Task<IEnumerable<MedicationInfo.LabFetchModel>> FetchVitalsInfoAsync(Labs.FilterModel model)
        {
            var dateWhere = model.Date != null
                ? $@" AND to_char(p.""Date"", 'YYYY-MM-DD' )::DATE = '{model.Date:yyyy-MM-dd}'::DATE "
                : string.Empty;

            var query = $@"SELECT
	                            r.""AdmissionId"",
	                            COUNT(*) OVER() AS ""Count""
                            FROM
	                            ""ProgressReportVitals""
	                            P JOIN ""ProgressReport"" r ON r.""ProgressReportId"" = P.""ProgressReportId""
	                            WHERE r.""AdmissionId"" IN ({string.Join(",", model.AdmissionIds.Select(x => x).ToArray())}) {dateWhere}";
            var response = await this.unitOfWork.Current.QueryAsync<MedicationInfo.LabFetchModel>(query);
            return response;
        }

        public async Task<int> InsertAsync(InsertModel model)
        {
            var transaction = this.unitOfWork.BeginTransaction();
            try
            {
                var progressReport = await this.unitOfWork.ProgressReport.FindAsync(x => x.AdmissionId == model.AdmissionId, transaction);
                var progressReportId = progressReport?.ProgressReportId ?? await this.unitOfWork.ProgressReport.InsertAsync(new ProgressReport
                {
                    AdmissionId = model.AdmissionId,
                    CreatedBy = model.CreatedBy,
                    CreatedDate = DateTime.Now
                }, transaction);

                var record = new ProgressReportVitals
                {
                    Active = true,
                    CreatedBy = model.CreatedBy,
                    CreatedDate = DateTime.Now,
                    Date = model.Date,
                    VitalTypeId = model.VitalTypeId,
                    VitalTypeValue = model.VitalTypeValue,
                    Vitals = model.Vitals,
                    UnitName = model.UnitName,
                    ProgressReportId = progressReportId
                };

                var responseId = await this.unitOfWork.ProgressReportVitals.InsertAsync(record, transaction);
                if (responseId <= 0)
                {
                    transaction.Rollback();
                    return -1;
                }

                transaction.Commit();
                return responseId;
            }
            catch (Exception)
            {
                transaction.Rollback();
                return -1;
            }
        }

        public async Task<int> UpdateAsync(UpdateModel model)
        {
            var record = await this.unitOfWork.ProgressReportVitals.FindAsync(x => x.ProgressReportVitalsId == model.ProgressReportVitalsId);
            record.ModifiedBy = model.ModifiedBy;
            record.ModifiedDate = DateTime.Now;
            record.VitalTypeId = model.VitalTypeId;
            record.VitalTypeValue = model.VitalTypeValue;
            record.Vitals = model.Vitals;
            record.UnitName = model.UnitName;
            record.Date = model.Date;

            var responseId = await this.unitOfWork.ProgressReportVitals.UpdateAsync(record);
            return responseId;
        }

        public async Task<IEnumerable<Resource.ViewModel>> FetchVitalTypeAsync()
        {
            var query = $@"SELECT
	                        ""VitalTypeId"" AS ""Id"",
	                        ""Name"" AS ""Name"" 
                        FROM
	                        ""VitalType"" ";
            var records = await this.unitOfWork.Current.QueryAsync<Resource.ViewModel>(query);
            return records;
        }
        public async Task<IEnumerable<Resource.ViewModel>> FetchUnitTypeAsync()
        {
            var query = $@"SELECT
	                        ""UnitTypeId"" AS ""Id"",
	                        ""Name"" AS ""Name"" 
                        FROM
	                        ""UnitType"" ";
            var records = await this.unitOfWork.Current.QueryAsync<Resource.ViewModel>(query);
            return records;
        }
    }
}