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

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

        /// <inheritdoc cref="IPharmacyDepartmentService" />
        public PharmacyDepartmentServices(IUnitOfWork unitOfWork) => this.unitOfWork = unitOfWork;

        /// <inheritdoc />
        public async Task<int> ModifyDepartment(PharmacyDepartmentModel model)
        {
            var checkIfQuery = $@"Select count(*) from ""PharmacyDepartment"" where lower(""Name"") = '{model.Name.ToLower()}' and ""LocationId"" = {model.LocationId}";

            var transaction = this.unitOfWork.BeginTransaction();

            var pharmacyDepartment = new PharmacyDepartment
            {
                Active = true,
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                LocationId = (int)model.LocationId,
                Name = model.Name
            };

            if (model.PharmacyDepartmentId == 0)
            {
                var checkIf = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>(checkIfQuery);
                if (checkIf > 0)
                {
                    transaction.Rollback();
                    return -1;
                }

                pharmacyDepartment.PharmacyDepartmentId = await this.unitOfWork.PharmacyDepartments.InsertAsync(pharmacyDepartment, transaction);
                if (pharmacyDepartment.PharmacyDepartmentId == 0)
                {
                    transaction.Rollback();
                    return -2;
                }
            }
            else
            {
                checkIfQuery += $@"  and ""PharmacyDepartmentId"" <> {model.PharmacyDepartmentId}";
                var checkIf = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>(checkIfQuery);
                if (checkIf > 0)
                {
                    transaction.Rollback();
                    return -1;
                }

                var previousRecord = await this.unitOfWork.PharmacyDepartments.FindAsync(x => x.PharmacyDepartmentId == model.PharmacyDepartmentId);
                if (previousRecord == null)
                {
                    transaction.Rollback();
                    return -2;
                }

#pragma warning disable S2259 // Null pointers should not be dereferenced
                previousRecord.Name = pharmacyDepartment.Name;
#pragma warning restore S2259 // Null pointers should not be dereferenced
                previousRecord.ModifiedBy = model.CreatedBy;
                previousRecord.ModifiedDate = pharmacyDepartment.CreatedDate;
                pharmacyDepartment.PharmacyDepartmentId = previousRecord.PharmacyDepartmentId;
                var updateDepartment = await this.unitOfWork.PharmacyDepartments.UpdateAsync(previousRecord, transaction);
                if (updateDepartment == 0)
                {
                    transaction.Rollback();
                    return -2;
                }

                var userDeleteQuery = $@"Delete from ""PharmacyDepartmentUser"" where ""PharmacyDepartmentId"" = {pharmacyDepartment.PharmacyDepartmentId}";
                var userDeleteResponse = await this.unitOfWork.Current.ExecuteAsync(userDeleteQuery, transaction);
                //if (userDeleteResponse == 0)
                //{
                //    transaction.Rollback();
                //    return -2;
                //}

            }

            if (!string.IsNullOrEmpty(model.UserIds))
            {
                var departmentUsers = model.UserIds.Split(",").Select(id => new PharmacyDepartmentUser
                {
                    AccountId = int.Parse(id),
                    PharmacyDepartmentId = pharmacyDepartment.PharmacyDepartmentId
                });

                var departmentUserResponse = await this.unitOfWork.PharmacyDepartmentUsers.BulkInsertAsync(departmentUsers, transaction);
                if (departmentUserResponse == 0)
                {
                    transaction.Rollback();
                    return -2;
                }
            }

            transaction.Commit();
            return pharmacyDepartment.PharmacyDepartmentId;
        }

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

            if (model.LocationId != null)
            {
                where += $@"  and PD.""LocationId"" = {model.LocationId}";
            }

            if (model.Active != null)
            {
                if ((bool)model.Active)
                {
                    where += $@" and PD.""Active"" is true";
                }
                else
                {
                    where += $@" and PD.""Active"" is false";
                }
            }

            if (model.AccountBasedAccessId != null)
            {
                var getId = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<string>($@"Select string_agg(""PharmacyDepartmentId""::text,',')  from ""PharmacyDepartmentUser"" where ""AccountId"" = {model.AccountBasedAccessId}");
                if (!string.IsNullOrEmpty(getId))
                {
                    where += $@" and PD.""PharmacyDepartmentId"" in ({getId})";
                }
            }

            var query = $@"SELECT PD.""PharmacyDepartmentId"", PD.""Name"", PD.""Active"", PD.""CreatedBy"", PD.""CreatedDate"", PD.""ModifiedBy"", 
                                   PD.""ModifiedDate"", PD.""LocationId"", CR.""FullName"" as ""CreatedByName"", MR.""FullName"" as ""ModifiedByName""
	                            FROM ""PharmacyDepartment"" PD
	                            join ""Account"" CR on CR.""AccountId"" = PD.""CreatedBy""
	                            left join ""Account"" MR on MR.""AccountId"" = PD.""ModifiedBy""
                                {where}
	                            order by PD.""CreatedDate"" desc";
            var pharmacyDepartments = await this.unitOfWork.Current.QueryAsync<PharmacyDepartmentModel>(query);
            foreach (var item in pharmacyDepartments)
            {
                item.Users = new List<PharmacyDepartmentUserModel>();
                var userQuery = $@"SELECT PDU.""PharmacyDepartmentUserId"", PDU.""PharmacyDepartmentId"", PDU.""AccountId"",
		                                    A.""FullName"", R.""RoleName""
	                                    FROM ""PharmacyDepartmentUser"" PDU
	                                    join ""Account"" A on A.""AccountId"" = PDU.""AccountId""
	                                    join ""Role"" R on R.""RoleId""= A.""RoleId""
	                                    where  PDU.""PharmacyDepartmentId"" = {item.PharmacyDepartmentId}
	                                    order by PDU.""PharmacyDepartmentUserId"" desc";
                item.Users = (await this.unitOfWork.Current.QueryAsync<PharmacyDepartmentUserModel>(userQuery)).ToList();
            }

            return pharmacyDepartments;
        }

        /// <inheritdoc />
        public async Task<IEnumerable<PharmacyDepartmentalStockModel>> FetchDepartmentalStocks(PharmacyDepartmentalStockModel model)
        {
            var where = $@"where 1=1 and (PDS.""QuantityIn"" - PDS.""QuantityOut"") > 0";

            if (!string.IsNullOrEmpty(model.UnavailableStock))
            {
                if (model.UnavailableStock == "yes")
                {
                    where = $@"where 1=1 and (PDS.""QuantityIn"" - PDS.""QuantityOut"") >= 0";
                }
                if (model.UnavailableStock == "no")
                {
                    where = $@"where 1=1 and (PDS.""QuantityIn"" - PDS.""QuantityOut"") > 0";
                }
            }

            if (model.LocationId != null)
            {
                where += $@" and PD.""LocationId"" = {model.LocationId}";
            }

            if (model.PharmacyDepartmentId != null)
            {
                where += $@"  and PDS.""PharmacyDepartmentId"" = {model.PharmacyDepartmentId}";
            }


            if (!string.IsNullOrEmpty(model.CategoryName))
            {
                where += $@" and Cat.""Name"" ilike '%{model.CategoryName}%'";
            }

            if (!string.IsNullOrEmpty(model.ProductName))
            {
                where += $@" and  PP.""ProductName"" ilike '%{model.ProductName}%'";
            }

            if (!string.IsNullOrEmpty(model.BatchNumber))
            {
                where += $@" and   PDS.""BatchNumber"" ilike '%{model.BatchNumber}%'";
            }

            if (!string.IsNullOrEmpty(model.GenericName))
            {
                where += $@" and  PP.""GenericName"" ilike '%{model.GenericName}%'";
            }

            if (!string.IsNullOrEmpty(model.SearchParam))
            {
                where += $@" and (PP.""ProductName"" ilike '%{model.SearchParam}%' or PP.""GenericName"" ilike '%{model.SearchParam}%')";
            }


            var query = $@"SELECT count(PDS.""PharmacyDepartmentalStockId"") over() as ""TotalItems"", PDS.""PharmacyDepartmentalStockId"", PDS.""PharmacyProductId"", PDS.""TaxId"", PDS.""QuantityIn"", PDS.""QuantityOut"", 
		                        PDS.""BatchNumber"", PDS.""ExpiryDate"", PDS.""PurchaseRate"", PDS.""Mrp"", PDS.""Barcode"", PDS.""PharmacyStockId"", 
		                        PDS.""PharmacyRetailStockId"", PDS.""CreatedBy"", PDS.""CreatedDate"", PDS.""ModifiedBy"", PDS.""ModifiedDate"", 
		                        PDS.""PharmacyDepartmentId"",PP.""ProductName"",PP.""GenericName"",Tax.""Name""::int as ""TaxPercentage"",
		                        PPR.""RackName"",PPD.""PharmacyProductDetailId"",PPD.""ROQ"",PPD.""ROL"",(PDS.""QuantityIn"" - PDS.""QuantityOut"") as ""AvailableQuantity"",
		                        CR.""FullName"" as ""CreatedByName"", MR.""FullName"" as ""ModifiedByName"",PD.""Name"" as ""DepartmentName"",Cat.""Name"" as ""CategoryName""
	                        FROM ""PharmacyDepartmentalStock"" PDS
	                        join ""PharmacyDepartment"" PD on PD.""PharmacyDepartmentId"" = PDS.""PharmacyDepartmentId""
	                        join ""PharmacyProduct"" PP on PP.""PharmacyProductId"" = PDS.""PharmacyProductId""
	                        join ""LookupValue"" Tax on Tax.""LookupValueId"" = PDS.""TaxId""
                            join ""LookupValue"" Cat on Cat.""LookupValueId"" = PP.""CategoryId""
	                        join ""Account"" CR on CR.""AccountId"" = PDS.""CreatedBy""
	                        left join ""Account"" MR on MR.""AccountId"" = PDS.""ModifiedBy""
	                        left join ""PharmacyProductDetail"" PPD on PPD.""PharmacyProductId"" = PDS.""PharmacyProductId"" and PPD.""PharmacyDepartmentId"" = PDS.""PharmacyDepartmentId""
	                        left join ""PharmacyProductRack"" PPR on PPR.""PharmacyProductRackId"" = PPD.""PharmacyProductRackId""
	                        {where}
	                        order by PDS.""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<PharmacyDepartmentalStockModel>(query);

        }

        /// <inheritdoc />
        public async Task<int> AddPharmacyProductAdditionalDetailsAsync(PharmacyProductDetailModel model)
        {
            using var transaction = this.unitOfWork.BeginTransaction();

            var getRackNameQuery = $@"SELECT ""PharmacyProductRackId"" FROM ""PharmacyProductRack"" where lower(""RackName"") = '{model.RackName.ToLower()}' ";
            if (model.PharmacyDepartmentId != null)
            {
                getRackNameQuery += $@" and ""PharmacyDepartmentId"" = {model.PharmacyDepartmentId} ";
            }

            if (model.RetailPharmacyId != null)
            {
                getRackNameQuery += $@" and ""RetailPharmacyId"" = {model.RetailPharmacyId} ";
            }

            if (model.PharmacyWareHouseId != null)
            {
                getRackNameQuery += $@" and ""PharmacyWareHouseId"" = {model.PharmacyWareHouseId} ";
            }

            var getRackResponse = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<PharmacyProductRack>(getRackNameQuery);
            if (getRackResponse == null)
            {
                getRackResponse = new PharmacyProductRack();
                var rack = new PharmacyProductRack
                {
                    CreatedBy = model.CreatedBy,
                    CreatedDate = DateTime.Now,
                    RackName = model.RackName,
                    PharmacyDepartmentId = model.PharmacyDepartmentId,
                    RetailPharmacyId = model.RetailPharmacyId,
                    PharmacyWareHouseId = model.PharmacyWareHouseId
                };

                getRackResponse.PharmacyProductRackId = await this.unitOfWork.PharmacyProductRacks.InsertAsync(rack, transaction);
                if (getRackResponse.PharmacyProductRackId == 0)
                {
                    transaction.Rollback();
                    return -1;
                }
            }

            if (model.PharmacyProductDetailId == 0)
            {
                var detail = new PharmacyProductDetail
                {
                    CreatedBy = model.CreatedBy,
                    CreatedDate = DateTime.Now,
                    PharmacyDepartmentId = model.PharmacyDepartmentId,
                    PharmacyProductId = model.PharmacyProductId,
                    PharmacyProductRackId = getRackResponse.PharmacyProductRackId,
                    ROL = model.ROL,
                    ROQ = model.ROQ,
                    RetailPharmacyId = model.RetailPharmacyId,
                    PharmacyWareHouseId = model.PharmacyWareHouseId
                };

                model.PharmacyProductDetailId = await this.unitOfWork.PharmacyProductDetails.InsertAsync(detail, transaction);
                if (model.PharmacyProductDetailId == 0)
                {
                    transaction.Rollback();
                    return -1;
                }
            }
            else
            {
                var findOldOne = await this.unitOfWork.PharmacyProductDetails.FindAsync(x => x.PharmacyProductDetailId == model.PharmacyProductDetailId);
                if (findOldOne != null)
                {
                    findOldOne.ROL = model.ROL;
                    findOldOne.ROQ = model.ROQ;
                    findOldOne.PharmacyProductRackId = getRackResponse.PharmacyProductRackId;
                    findOldOne.ModifiedBy = model.CreatedBy;
                    findOldOne.ModifiedDate = DateTime.Now;
                    var updateResponse = await this.unitOfWork.PharmacyProductDetails.UpdateAsync(findOldOne, transaction);
                    if (updateResponse == 0)
                    {
                        transaction.Rollback();
                        return -1;
                    }
                }
                else
                {
                    transaction.Rollback();
                    return -1;
                }
            }

            transaction.Commit();
            return model.PharmacyProductDetailId;
        }

        /// <inheritdoc />
        public async Task<int> AddDepartmentStockConsumptionAsync(DepartmentConsumptionModel model)
        {
            var transaction = this.unitOfWork.Current.BeginTransaction();

            var consumption = new DepartmentConsumption
            {
                ConsumedBy = (int)model.ConsumedBy,
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                PharmacyDepartmentalStockId = (int)model.PharmacyDepartmentalStockId,
                QuantityConsumed = model.QuantityConsumed,
                ReasonForConsumption = model.ReasonForConsumption
            };

            consumption.DepartmentConsumptionId = await this.unitOfWork.DepartmentConsumptions.InsertAsync(consumption, transaction);
            if (consumption.DepartmentConsumptionId == 0)
            {
                transaction.Rollback();
                return -1;
            }

            var getStock = await this.unitOfWork.PharmacyDepartmentalStocks.FindAsync(d => d.PharmacyDepartmentalStockId == model.PharmacyDepartmentalStockId);
            if (getStock == null)
            {
                transaction.Rollback();
                return -1;
            }

            getStock.QuantityOut += model.QuantityConsumed;
            getStock.ModifiedBy = model.ConsumedBy;
            getStock.ModifiedDate = DateTime.Now;

            var updateResponse = await this.unitOfWork.PharmacyDepartmentalStocks.UpdateAsync(getStock, transaction);
            if (updateResponse == 0)
            {
                transaction.Rollback();
                return -1;
            }

            transaction.Commit();
            return consumption.DepartmentConsumptionId;
        }

        /// <inheritdoc />
        public async Task<IEnumerable<DepartmentConsumptionModel>> FetchDepartmentalConsumptionsAsync(DepartmentConsumptionModel model)
        {
            var where = " where 1=1";
            if (model.PharmacyDepartmentalStockId != null)
            {
                where += $@" and DC.""PharmacyDepartmentalStockId"" = {model.PharmacyDepartmentalStockId}";
            }

            var query = $@"SELECT  count(DC.*) over() as ""TotalItems"", DC.""DepartmentConsumptionId"", DC.""PharmacyDepartmentalStockId"", DC.""QuantityConsumed"", DC.""ConsumedBy"", DC.""CreatedDate"",
		                            DC.""ReasonForConsumption"", DC.""CreatedBy"",
		                            CON.""FullName"" as ""ConsumedByName"",CONR.""RoleName"" as ""ConsumedByRole"",
		                            CR.""FullName"" as ""CreatedByName"",CRR.""RoleName""  as ""CreatedByRole""
                            FROM ""DepartmentConsumption"" DC
                            join ""Account"" CON on CON.""AccountId""  = DC.""ConsumedBy"" 
                            join ""Role"" CONR on CONR.""RoleId"" = CON.""RoleId"" 
                            join ""Account"" CR on CR.""AccountId""  = DC.""CreatedBy""  
                            join ""Role"" CRR on CRR.""RoleId"" = CR.""RoleId"" 
                            {where}
                            order by DC.""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.PageIndex * model.PageSize}";
            }

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

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

            if (model.PharmacyDepartmentalStockId != null)
            {
                where += $@" and DC.""PharmacyDepartmentalStockId"" = {model.PharmacyDepartmentalStockId}";
            }

            if (!string.IsNullOrEmpty(model.FromDate))
            {
                where += $@" and DC.""CreatedDate""::date >= '{Convert.ToDateTime(model.FromDate)::yyyy-MM-dd}'";
            }

            if (!string.IsNullOrEmpty(model.ToDate))
            {
                where += $@" and  DC.""CreatedDate""::date <= '{Convert.ToDateTime(model.ToDate)::yyyy-MM-dd}'";
            }

            if (model.PharmacyDepartmentId != null)
            {
                where += $@"  and PDS.""PharmacyDepartmentId"" = {model.PharmacyDepartmentId}";
            }

            if (!string.IsNullOrEmpty(model.PharmacyProductIds))
            {
                where += $@" and PP.""PharmacyProductId"" in ({model.PharmacyProductIds})";
            }

            if (model.ConsumedBy != null)
            {
                where += $@" and DC.""ConsumedBy"" = {model.ConsumedBy}";
            }

            var query = $@"SELECT DC.""DepartmentConsumptionId"", DC.""PharmacyDepartmentalStockId"", DC.""QuantityConsumed"", DC.""ConsumedBy"", DC.""CreatedDate"", 
	                                DC.""ReasonForConsumption"", DC.""CreatedBy"",PDS.""BatchNumber"",PDS.""ExpiryDate"",PDS.""CreatedDate"" as ""StockCreditedDate"",
	                                PP.""ProductName"",PP.""GenericName"",Tax.""Name""::int as ""TaxPercentage"",PDS.""QuantityIn"",PDS.""QuantityOut"",
	                                Cat.""Name"" as ""CategoryName"",CON.""FullName"" as ""ConsumedByName"",CONR.""RoleName"" as ""ConsumedByRole"",
	                                CR.""FullName"" as ""CreatedByName"",CRR.""RoleName""  as ""CreatedByRole""	
	                                FROM ""DepartmentConsumption"" DC
	                                join ""PharmacyDepartmentalStock"" PDS on PDS.""PharmacyDepartmentalStockId"" = DC.""PharmacyDepartmentalStockId"" 
	                                join ""PharmacyProduct"" PP on PP.""PharmacyProductId"" = PDS.""PharmacyProductId"" 
	                                join ""LookupValue"" Tax on Tax.""LookupValueId"" = PDS.""TaxId""
                                    join ""LookupValue"" Cat on Cat.""LookupValueId"" = PP.""CategoryId""
                                    join ""Account"" CON on CON.""AccountId""  = DC.""ConsumedBy"" 
                                    join ""Role"" CONR on CONR.""RoleId"" = CON.""RoleId"" 
                                    join ""Account"" CR on CR.""AccountId""  = DC.""CreatedBy""  
                                    join ""Role"" CRR on CRR.""RoleId"" = CR.""RoleId"" 
	                                {where}    		                                
                                    order by DC.""CreatedDate"" desc";

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

        /// <inheritdoc />
        public async Task<IEnumerable<PharmacyProductRackModel>> FetchRackDepartmentWiseAsync(PharmacyProductRackModel model)
        {
            var where = "where 1=1";
            if (model.RetailPharmacyId != null)
            {
                where += $@" and ""RetailPharmacyId"" = {model.RetailPharmacyId} ";
            }

            if (model.PharmacyDepartmentId != null)
            {
                where += $@" and ""PharmacyDepartmentId"" = {model.PharmacyDepartmentId} ";
            }

            if (model.PharmacyWareHouseId != null)
            {
                where += $@" and ""PharmacyWareHouseId"" = {model.PharmacyWareHouseId} ";
            }

            var query = $@"Select * from ""PharmacyProductRack"" {where}";

            return await this.unitOfWork.Current.QueryAsync<PharmacyProductRackModel>(query);
        }
        /// <inheritdoc />
        public async Task<int> ActivateOrDeactivateDepartment(PharmacyDepartmentModel model)
        {
            var query = $@"UPDATE ""PharmacyDepartment""
	                           SET ""ModifiedBy""={model.CreatedBy}, ""ModifiedDate""=now()::timestamp, ""Active""= {model.Active}
	                           WHERE ""PharmacyDepartmentId""= {model.PharmacyDepartmentId}";
            return await this.unitOfWork.Current.ExecuteAsync(query);
        }

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

            if (!string.IsNullOrEmpty(model.PharmacyDepartmentIds))
            {
                where += $@" and PD.""PharmacyDepartmentId"" in ({model.PharmacyDepartmentIds}) ";
            }

            var query = $@"SELECT PD.""PharmacyDepartmentId"", PD.""Name"", PD.""Active"", PD.""CreatedBy"", PD.""CreatedDate"", PD.""ModifiedBy"", 
                                   PD.""ModifiedDate"", PD.""LocationId"", CR.""FullName"" as ""CreatedByName"", MR.""FullName"" as ""ModifiedByName"",
                                   l.""Name"" as ""LocationName""
	                            FROM ""PharmacyDepartment"" PD
	                            join ""Account"" CR on CR.""AccountId"" = PD.""CreatedBy""
	                            left join ""Account"" MR on MR.""AccountId"" = PD.""ModifiedBy""
	                            join ""Location"" l on l.""LocationId"" = PD.""LocationId"" 
                                {where}
	                            order by PD.""CreatedDate"" desc";

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