using Hims.Shared.UserModels.Receipt;

namespace Hims.Infrastructure.Services
{
    using System;
    using System.Collections.Generic;
    using System.Threading.Tasks;
    using Dapper;
    using Hims.Domain.Entities;
    using Hims.Domain.Entities.Enums;
    using Hims.Domain.Repositories.UnitOfWork;
    using Hims.Domain.Services;
    using ReceiptTypes = Hims.Shared.UserModels.Receipt.ReceiptTypePayload;

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

        /// <summary>
        /// The appointment transaction services.
        /// </summary>
        private readonly IAppointmentTransactionService appointmentTransactionsServices;

        /// <inheritdoc cref="IChatService" />
        public ReceiptService(IUnitOfWork unitOfWork, IAppointmentTransactionService appointmentTransactionsServices)
        {
            this.unitOfWork = unitOfWork;
            this.appointmentTransactionsServices = appointmentTransactionsServices;
        }

        /// <summary>
        /// Fetches the charges asynchronous.
        /// </summary>
        /// <returns></returns>
        public async Task<IEnumerable<ReceiptTypes.ViewModel>> FetchReceiptTypesAsync()
        {
            var query = $@"SELECT
	                    ""ReceiptTypeId"",
	                    ""ReceiptTypeName""
                    FROM
	                    ""ReceiptType""";

            var records = await this.unitOfWork.Current.QueryAsync<ReceiptTypes.ViewModel>(query);
            return records;
        }

        public async Task<int> InsertAsync(InsertModel model)
        {
            var receipt = new Receipt();
            var existingFinalBill = new FinalBill();
            if (model.IsAdmission)
            {
                existingFinalBill = await this.unitOfWork.FinalBill.FindAsync(x => x.Active == true && x.AdmissionId == model.AdmissionId);

                var existingCount = await this.unitOfWork.Receipt.CountAsync(x => x.AdmissionId == model.AdmissionId && x.Active);
                if (existingCount <= 0 && existingFinalBill == null && model.ReceiptAreaTypeId == null)
                {
                    model.IsAdvance = true;
                }
            }
            var billId = 0;
            var getbillrecord = await this.unitOfWork.MasterBill.FindAsync(x => x.PatientId == model.PatientId && x.BillStatusTypeId == 2 && x.ReceiptAreaTypeId == (int)ReceiptAreaType.Services && x.ModuleId == existingFinalBill.AdmissionId); //need to check more

            if (model.ReceiptId > 0)
            {
                receipt = await this.unitOfWork.Receipt.FindAsync(x => x.ReceiptId == model.ReceiptId);
                receipt.ModifiedBy = model.CreatedBy;
                receipt.ModifiedDate = System.DateTime.Now;
                receipt.Cost += model.Cost;

                var response = await this.unitOfWork.Receipt.UpdateAsync(receipt);
                return response;
            }
            else
            {
                var record = new Receipt
                {
                    Active = model.Active,
                    AdmissionId = model.IsAdmission ? (int?)model.AdmissionId : null,
                    AppointmentId = !model.IsAdmission ? (int?)model.AdmissionId : null,
                    Cost = model.Cost,
                    CreatedBy = model.CreatedBy,
                    CreatedDate = model.CreatedDate,
                    IsAdvance = model.IsAdvance,
                    ReceiptTypeId = model.ReceiptTypeId,
                    PayTypeId = model.PayTypeId,
                    PaymentDetails = model.PaymentDetails,
                    IsRefunded = model.IsRefunded,
                    IsAppointmentReceipt = false,
                    ReceiptAreaTypeId = model.IsRefunded == false ? ReceiptAreaType.AdmissionReceiptRefund : ReceiptAreaType.AdmissionReceipt,
                    ReceiptAreaMainId = model.ReceiptAreaMainId,
                    RespectiveId = model.AdmissionId,
                    IsSalucroAdmissionReceipt = model.IsSalucroAdmissionReceipt,
                    MasterBillId = getbillrecord != null ? getbillrecord.MasterBillId : (int?)null,
                };

                var response = await this.unitOfWork.Receipt.InsertAsync(record);
                var txnId = 0;

                if (response > 0)
                {
                    //transaction for the receipt
                    var commonTransaction = new AppointmentTransaction
                    {
                        AppointmentId = model.AdmissionId,
                        Transaction = model.Transaction ?? "",
                        TransactionDate = DateTime.Now,
                        TransactionId = model.TransactionId ?? await this.appointmentTransactionsServices.GetATransactionId(),
                        VoucherNumber = model.SalucroStatusCode == 1201 ? null : await this.appointmentTransactionsServices.GetVoucherNumber(),
                        BankReference = "",
                        BankCode = "",
                        Active = true,
                        PaymentId = 0,
                        PaymentModeId = 1,
                        SettledAmount = (decimal)model.Cost,
                        CreatedBy = (int)model.CreatedBy,
                        CreatedDate = DateTime.Now,
                        LocationId = (int)model.LocationId,
                        ReceiptTypeId = model.IsRefunded == false ? (int)ReceiptType.Refund : (int)ReceiptType.Cash,
                        //ReceiptAreaTypeId = model.IsRefunded == false ? (int)ReceiptAreaType.AdmissionReceiptRefund : (int)ReceiptAreaType.AdmissionReceipt,
                        SalucroStatusCode = model.SalucroStatusCode,
                        SalucroTransactionId = model.SalucroTransactionId,
                        ReceiptAreaTypeId = (int)ReceiptAreaType.Services,
                        PatientId = model.PatientId
                        //PayStatus wrong impl
                    };
                    commonTransaction.AppointmentTransactionId = await this.unitOfWork.AppointmentTransactions.InsertAsync(commonTransaction);
                    txnId = commonTransaction.AppointmentTransactionId;
                }
                if (txnId > 0)
                {
                    receipt = await this.unitOfWork.Receipt.FindAsync(x => x.ReceiptId == response);
                    receipt.TransactionId = txnId;
                    receipt.MasterBillId = getbillrecord != null ? getbillrecord.MasterBillId : (int?)null;
                    await this.unitOfWork.Receipt.UpdateAsync(receipt);
                }
                var allreceiptforbill = await this.unitOfWork.Receipt.FindAllAsync(x => x.MasterBillId == receipt.MasterBillId);
                //allreceiptforbill.ToList();
                var receiptcostsum = 0.0;
                foreach (var item in allreceiptforbill)
                {
                    receiptcostsum += item.Cost;
                }
                var getbill = await this.unitOfWork.MasterBill.FindAsync(x => x.MasterBillId == receipt.MasterBillId);
                if (getbill != null && receiptcostsum == getbill.NetTotal)
                {
                    getbill.BillStatusTypeId = (int)BillStatusType.Generated;
                    getbill.ModifiedBy = (int)model.CreatedBy;
                    getbill.ModifiedDate = DateTime.Now;
                    await this.unitOfWork.MasterBill.UpdateAsync(getbill);
                }
                return response;
            }

        }

        /// <inheritdoc/>
        public async Task<ViewModel> FetchReceiptDetailsAsync(InsertModel model)
        {
            var where = "where 1=1";
            var condition = model.IsAdmission ? $@" join ""Admission"" A on A.""AdmissionId"" = R.""AdmissionId""" : $@" join ""Appointment"" A on A.""AppointmentId"" = R.""AppointmentId""";

            where += model.IsAdmission ? $@" and  A.""AdmissionId"" = {model.Id} and R.""ReceiptId"" = {model.ReceiptId}" : $@" and A.""AppointmentId"" = {model.Id} and R.""ReceiptId"" = {model.ReceiptId}";

            var query = $@"select R.""ReceiptId"", P.""FullName""
                             from ""Receipt"" R
                            {condition}
                            join ""Patient"" P on P.""PatientId"" = A.""PatientId""
                            {where}";
            return await this.unitOfWork.Current.QueryFirstOrDefaultAsync<ViewModel>(query);
        }

        public async Task<int> CancelAsync(int receiptId)
        {
            var receipt = await this.unitOfWork.Receipt.FindAsync(x => x.Active == true && x.ReceiptId == receiptId);
            receipt.Active = false;
            return await this.unitOfWork.Receipt.UpdateAsync(receipt);
        }

        public async Task<IEnumerable<ViewModel>> FetchAsync(FilterModel model)
        {
            var activeCondition = model.ActiveOnly ? @" AND r.""Active"" IS TRUE " : string.Empty;
            var typeCondition = model.IsAdmission ? @"""AdmissionId""" : @"""AppointmentId""";
            var addColumn = model.IsAdmission ? @",a.""IsDischarged""" : "";
            var idCondition = model.ReceiptId != null ? @$" AND r.""ReceiptId"" = {model.ReceiptId} " : string.Empty;
            var typeJoin = model.IsAdmission
                ? $@"JOIN ""Admission"" a on a.""AdmissionId"" = r.""AdmissionId"""
                : $@"JOIN ""Appointment"" a on a.""AppointmentId"" = r.""AppointmentId""";

            var query = $@"
                            SELECT
                                r.""IsSalucroAdmissionReceipt"",
	                            r.""Cost"",
	                            r.""PayTypeId"",
	                            r.""PaymentDetails"",
	                            t.""ReceiptTypeId"",
                                PT.""PayTypeName"",
                                t.""ReceiptTypeName"",
	                            r.""Active"",
	                            r.""IsAdvance"",
	                            r.""IsRefunded"",
	                            r.""ReceiptId"",
	                            ca.""FullName"" AS ""CreatedByName"",
	                            cr.""RoleName"" AS ""CreatedByRole"",
	                            r.""CreatedDate"",
	                            ma.""FullName"" AS ""ModifiedName"",
                                mr.""RoleName"" AS ""ModifiedByRole"",
	                            r.""ModifiedDate"",
	                            p.""FullName"",
	                            r.""IsAppointmentReceipt"",
	                            r.""ReceiptAreaTypeId"",
	                            r.""ReceiptAreaMainId""
                                {addColumn}
                               -- a.""IsDischarged""
                            FROM
	                            ""Receipt"" r
	                            JOIN ""ReceiptType"" t on t.""ReceiptTypeId"" = r.""ReceiptTypeId""
	                            {typeJoin}
                                JOIN  ""PayType"" PT on PT.""PayTypeId"" = r.""PayTypeId""

                                JOIN ""Patient"" p on p.""PatientId"" = a.""PatientId""
	                            JOIN ""Account"" ca on ca.""AccountId"" = r.""CreatedBy""
                                JOIN ""Role"" cr on cr.""RoleId"" = ca.""RoleId""
	                            LEFT JOIN ""Account"" ma on ma.""AccountId"" = r.""ModifiedBy""
                                LEFT JOIN ""Role"" mr on mr.""RoleId"" = ma.""RoleId""	                            
                                WHERE r.{typeCondition} = {model.AdmissionId} {activeCondition} {idCondition}
	                            ORDER BY r.""ReceiptId"" DESC";
            var records = await this.unitOfWork.Current.QueryAsync<ViewModel>(query);
            return records;
        }
    }
}
