namespace Hims.Infrastructure.Services
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Data.Common;
    using System.Linq;
    using System.Threading.Tasks;
    using System.Transactions;
    using Dapper;
    using Domain.Repositories.UnitOfWork;
    using Domain.Services;
    using Hims.Domain.Entities;
    using Hims.Shared.EntityModels;
    using Hims.Shared.UserModels;
    using Hims.Shared.UserModels.Laboratory;
    using Hims.Shared.UserModels.Scan.ScanTest;
    using Hims.Shared.UserModels.Slots;
    using IdentityModel.Client;


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

        /// <inheritdoc cref="IScanTestService" />
        public ScanTestService(IUnitOfWork unitOfWork) => this.unitOfWork = unitOfWork;

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

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

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

            }

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

            }

            var query = $@"SELECT count(LH.*) over() as ""TotalItems"",
                                LHM.""ChargeCategoryId"",
                                LHM.""Amount"",
                                cc.""ChargeCategoryName"",
                                LH.""ScanTestMasterId"", 
                                LH.""ScanTestCode"", 
                                LH.""ScanTestName"", 
                                LH.""Duration"",
                                LH.""Description"",
                                LH.""Active"", 
                                LH.""CreatedBy"", 
                                LH.""CreatedDate"", 
                                LH.""ModifiedBy"",
                                LH.""ModifiedDate"",
								LH.""ScanClassificationId"",
								sc.""ScanClassificationName"",
                             A.""FullName"" as ""CreatedByName"",
							M.""FullName"" as ""ModifiedByName"",							
LHM.""LocationScanTestMasterMapId""

                            FROM ""ScanTestMaster"" LH
                            join ""Account"" A on A.""AccountId"" = LH.""CreatedBy""
                            left join ""Account"" M on M.""AccountId"" = LH.""ModifiedBy""
                            join ""LocationScanTestMasterMap"" LHM on LHM.""ScanTestMasterId"" = LH.""ScanTestMasterId""
                        left join ""ChargeCategory"" cc on cc.""ChargeCategoryId"" = LHM.""ChargeCategoryId""
                    left join ""Location"" l on l.""LocationId"" = LHM.""LocationId""
                        left join ""ScanClassification"" sc on sc.""ScanClassificationId"" = LH.""ScanClassificationId""


                            {where}
            group by cc.""ChargeCategoryName"",LH.""ScanTestMasterId"",LHM.""ChargeCategoryId"",LHM.""Amount"",A.""FullName"",M.""FullName"",sc.""ScanClassificationName"",LHM.""LocationScanTestMasterMapId"" order by LH.""CreatedDate"" desc ";


            var response = await this.unitOfWork.Current.QueryAsync<ScanTestMasterModel>(query);

            return response;
        }


        public async Task<int> AddAsync(ScanTestInsertModel model)
        {
            using (var transaction = this.unitOfWork.BeginTransaction())
                try
                {
                    var checkIf = this.unitOfWork.Current.QueryFirstOrDefault<int>($@"SELECT COUNT(""ScanTestMasterId"") FROM ""ScanTestMaster"" WHERE TRIM(UPPER(""ScanTestName"")) = '{model.ScanTestName.ToUpper().Trim()}'");
                    if (checkIf > 0)
                    {
                        return -1;
                    }
                    var scanTestModuleMaster = await this.unitOfWork.ModulesMasters.FindAsync(m => m.ModuleName == "Scan");
                    if (scanTestModuleMaster == null)
                    {
                        return -2;
                    }
                    var scanTest = new ScanTestMaster
                    {
                        ScanTestName = model.ScanTestName,
                        ScanTestCode = model.ScanTestCode,
                        Duration = model.Duration,
                        Description = model.Description,
                        CreatedBy = model.CreatedBy,
                        Active = true,
                        CreatedDate = DateTime.Now,
                        ScanClassificationId = model.ScanClassificationId,
                        ScanSubClassificationId = model.ScanSubClassificationId,
                        ModulesMasterId = scanTestModuleMaster.ModulesMasterId
                    };
                    scanTest.ScanTestMasterId = await this.unitOfWork.ScanTestMasters.InsertAsync(scanTest, transaction);
                    if (scanTest.ScanTestMasterId == 0)
                    {
                        transaction.Rollback();
                        return 0;
                    }

                    var locationScanTestMasterMap = new LocationScanTestMasterMap
                    {
                        ScanTestMasterId = scanTest.ScanTestMasterId,
                        LocationId = model.LocationId

                    };
                    var detail = await this.unitOfWork.LocationScanTestMasterMaps.InsertAsync(locationScanTestMasterMap, transaction);

                    transaction.Commit();
                    return scanTest.ScanTestMasterId;

                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message.ToString());
                    return 0;
                }
        }


        public async Task<int> UpdateAsync(ScanTestInsertModel model)
        {
            using (var transaction = this.unitOfWork.BeginTransaction())
                try
                {
                    var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""ScanTestMasterId"") FROM ""ScanTestMaster"" WHERE TRIM(UPPER(""ScanTestName"")) = '{model.ScanTestName.ToUpper().Trim()}' AND ""ScanTestMasterId"" <> {model.ScanTestMasterId}");
                    if (checkIf > 0)
                    {
                        return -1;
                    }

                    var scanTest = await this.unitOfWork.ScanTestMasters.FindAsync(m => m.ScanTestMasterId == model.ScanTestMasterId);
                    scanTest.ScanTestName = model.ScanTestName;
                    scanTest.Active = true;
                    scanTest.ModifiedBy = model.ModifiedBy;
                    scanTest.ModifiedDate = DateTime.Now;
                    scanTest.ScanClassificationId = model.ScanClassificationId;
                    scanTest.ScanSubClassificationId = model.ScanSubClassificationId;
                    scanTest.ScanTestCode = model.ScanTestCode;
                    scanTest.Duration = model.Duration;
                    scanTest.Description = model.Description;
                    var updated = await this.unitOfWork.ScanTestMasters.UpdateAsync(scanTest, transaction);
                    if (updated == 0)
                    {
                        transaction.Rollback();
                        return 0;
                    }

                    var exists = await this.unitOfWork.LocationScanTestMasterMaps.FindAsync(
                                    m => m.ScanTestMasterId == model.ScanTestMasterId && m.LocationId == model.LocationId, transaction);
                    if (exists != null)
                    {
                        exists.LocationId = model.LocationId;
                        var response = await this.unitOfWork.LocationScanTestMasterMaps.UpdateAsync(exists);
                        if (response <= 0)
                        {
                            transaction.Rollback();
                            return -2;
                        }
                    }
                    else
                    {
                        var locationScanTestMasterMap = new LocationScanTestMasterMap
                        {
                            ScanTestMasterId = scanTest.ScanTestMasterId,
                            LocationId = model.LocationId

                        };
                        var res = await this.unitOfWork.LocationScanTestMasterMaps.InsertAsync(locationScanTestMasterMap, transaction);
                        if (res <= 0)
                        {
                            transaction.Rollback();
                            return -1;
                        }
                    }


                    transaction.Commit();
                    return 1;
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message.ToString());
                    return 0;
                }
        }

        public async Task<int> ActivateOrDeactivateTest(ScanTestMasterModel model)
        {
            var record = await this.unitOfWork.ScanTestMasters.FindAsync(x => x.ScanTestMasterId == model.ScanTestMasterId);
            var item = this.unitOfWork.Current.QueryFirstOrDefault<int>($@"select count(""ScanMachineTestMapId"") from ""ScanMachineTestMap"" where ""ScanTestMasterId"" = {model.ScanTestMasterId}");
            if (record == null) { return -1; }
            else if (item > 0) { return -1; }
            record.ModifiedBy = model.ModifiedBy;
            record.ModifiedDate = DateTime.Now;
            record.Active = (bool)model.Active ? true : false;

            return await this.unitOfWork.ScanTestMasters.UpdateAsync(record);
        }

        public async Task<IEnumerable<ScanTestMasterModel>> FetchAltScanTests(ScanTestMasterModel model)
        {
            var where = $@" where 1=1";
            if (model.LocationId != null)
            {
                where += $@" and ls.""LocationId"" = {model.LocationId}";
            }
            var query = $@"SELECT st.""ScanTestMasterId"",st.""ScanTestName"",st.""ScanTestCode"",ls.""LocationId"" FROM ""ScanTestMaster"" st
                                left join ""LocationScanTestMasterMap"" ls on ls.""ScanTestMasterId"" = st.""ScanTestMasterId""
                                left join ""Location"" l on l.""LocationId"" = ls.""LocationId"" 
                                left join ""ChargeCategory"" cc on cc.""ChargeCategoryId"" = ls.""ChargeCategoryId"" {where} and st.""Active""
                                group by st.""ScanTestMasterId"",st.""ScanTestName"",ls.""LocationId"" Order by ""ScanTestMasterId"" DESC;";
            return await this.unitOfWork.Current.QueryAsync<ScanTestMasterModel>(query);
        }

        public async Task<IEnumerable<ScanTestMasterModel>> FetchAllAsync(ScanTestMasterModel model)
        {
            var where = " where 1=1 ";
            if (model.LocationId != null)
            {
                where += $@" and LHM.""LocationId"" = {model.LocationId}";
            }

            if (!string.IsNullOrEmpty(model.ScanTestName))
            {
                where += $@" and LH.""ScanTestName"" = '{model.ScanTestName}' ";
            }

            var query = $@"SELECT count(LH.*) over() as ""TotalItems"",
                                LHM.""ChargeCategoryId"",
                                LHM.""Amount"",
                                cc.""ChargeCategoryName"",
                                LH.""ScanTestMasterId"", 
                                LH.""ScanTestCode"", 
                                LH.""ScanTestName"", 
                                LH.""Duration"",
                                LH.""Description"",
                                LH.""Active"", 
                                LH.""CreatedBy"", 
                                LH.""CreatedDate"", 
                                LH.""ModifiedBy"",
                                LH.""ModifiedDate"",
								LH.""ScanClassificationId"",
								sc.""ScanClassificationName"",
                                A.""FullName"" as ""CreatedByName"",
							    M.""FullName"" as ""ModifiedByName"",							
                                LHM.""LocationScanTestMasterMapId""
                            FROM ""ScanTestMaster"" LH
                            join ""Account"" A on A.""AccountId"" = LH.""CreatedBy""
                            left join ""Account"" M on M.""AccountId"" = LH.""ModifiedBy""
                            join ""LocationScanTestMasterMap"" LHM on LHM.""ScanTestMasterId"" = LH.""ScanTestMasterId""
                            left join ""ChargeCategory"" cc on cc.""ChargeCategoryId"" = LHM.""ChargeCategoryId""
                            left join ""Location"" l on l.""LocationId"" = LHM.""LocationId""
                            left join ""ScanClassification"" sc on sc.""ScanClassificationId"" = LH.""ScanClassificationId""
                                    {where}
            group by cc.""ChargeCategoryName"",LH.""ScanTestMasterId"",LHM.""ChargeCategoryId"",LHM.""Amount"",A.""FullName"",M.""FullName"",sc.""ScanClassificationName"",LHM.""LocationScanTestMasterMapId"" order by LH.""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<ScanTestMasterModel>(query);
        }

        public async Task<IEnumerable<ScanTestMasterModel>> FetchImportTestsAsync(ScanTestMasterModel model)
        {
            var where = " where 1=1 ";

            if (model.LocationId > 0)
            {
                where += $@" and  lm.""LocationId"" NOt IN ({model.LocationId}) ";
            }
            var query = $@"select Distinct(st.""ScanTestMasterId""),st.""ScanTestName"",st.""ScanTestCode"",st.""Duration"",
                        lm.""LocationId"",l.""Name"" as ""LocationName"", st.""CreatedDate""
                        from ""ScanTestMaster"" st
                        left join ""LocationScanTestMasterMap"" lm on lm.""ScanTestMasterId"" = st.""ScanTestMasterId""
                        left join ""Location"" l on l.""LocationId"" = lm.""LocationId""
                        {where} and lm.""ScanTestMasterId"" not in (select ""ScanTestMasterId"" from ""LocationScanTestMasterMap"" 
                        where ""LocationId"" = {model.LocationId}) order by st.""CreatedDate"" desc";

            if (model.PageIndex == null || model.PageSize == null)
            {
                return await this.unitOfWork.Current.QueryAsync<ScanTestMasterModel>(query);
            }
            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<ScanTestMasterModel>(query);
        }

        public async Task<int> ModifyImportTestAsync(TestImportModel model)
        {
            try
            {
                using (var transaction = this.unitOfWork.BeginTransaction())
                {
                    foreach (var item in model.Tests)
                    {
                        var locationScanTestMasterMap = new LocationScanTestMasterMap
                        {
                            LocationId = (int)model.LocationId,
                            ScanTestMasterId = item.ScanTestMasterId,
                            Amount = item.Amount,
                            ChargeCategoryId = item.ChargeCategoryId
                        };
                        var response = await this.unitOfWork.LocationScanTestMasterMaps.InsertAsync(locationScanTestMasterMap, transaction);

                    }
                    transaction.Commit();
                    return 1;

                }

            }
            catch (Exception ex)
            {
                throw;
            }
        }


        /// <inheritdoc/>
        public async Task<IEnumerable<ScanTestMasterModel>> FetchAllScanTestsAsync(ScanTestMasterModel model)
        {
            var where = "where 1=1 ";
            if (model.LocationId != null)
            {
                where += $@" and lstm.""LocationId"" = {model.LocationId}";
            }
            if (!string.IsNullOrEmpty(model.ScanTestName))
            {
                where += $@" and st.""ScanTestName"" = '{model.ScanTestName}'";
            }
            if (!string.IsNullOrEmpty(model.ScanTestCode))
            {
                where += $@" and st.""ScanTestCode"" = '{model.ScanTestCode}'";
            }
            if (model.ScanTestMasterId > 0)
            {
                where += $@" and  st.""ScanTestMasterId"" = {model.ScanTestMasterId} ";
            }
            if (model.ScanClassificationId > 0)
            {
                where += $@" and  st.""ScanClassificationId"" = {model.ScanClassificationId} ";
            }
            if (model.Active != null)
            {
                where += $@" AND st.""Active"" IS {((bool)model.Active ? "TRUE" : "FALSE")}";
            }
            if (!string.IsNullOrEmpty(model.Term))
            {
                where += $@" and (lower(st.""ScanTestName"") ilike '%{model.Term.ToLower()}%' or lower(st.""ScanTestCode"") ilike '%{model.Term.ToLower()}%')";
            }
            var query = $@"select count(st.*) over() as ""TotalItems"", st.""ScanTestMasterId"",st.""ScanTestName"",st.""ScanTestCode"",st.""Duration"",st.""ScanClassificationId"",sc.""ScanClassificationName"",
                        st.""ScanSubClassificationId"",ssc.""ScanSubClassificationName""
                        , st.""Description"", st.""CreatedDate"", st.""ModifiedDate"", st.""CreatedBy"", st.""ModifiedBy"", st.""Active"",
                        A.""FullName"" as ""CreatedByName"",M.""FullName"" as ""ModifiedByName""
                        from ""ScanTestMaster"" st 
						left join ""LocationScanTestMasterMap"" lstm on st.""ScanTestMasterId"" = lstm.""ScanTestMasterId"" 
                        left join ""ScanClassification"" sc on sc.""ScanClassificationId"" = st.""ScanClassificationId""
                        left join ""ScanSubClassification"" ssc on ssc.""ScanSubClassificationId"" = st.""ScanSubClassificationId""
                        left join ""Account"" A on A.""AccountId""=st.""CreatedBy""
		                left join ""Account"" M on M.""AccountId""=st.""ModifiedBy""
                        {where} group by (st.""ScanTestMasterId"",sc.""ScanClassificationName"",A.""FullName"",M.""FullName"",ssc.""ScanSubClassificationName"")
                        order by st.""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<ScanTestMasterModel>(query);
        }

        public async Task<IEnumerable<ScanTestMasterModel>> FetchAllScanTestCodesAsync(ScanTestMasterModel model)
        {
            var query = $@"select Distinct ""ScanTestCode"" from ""ScanTestMaster"" order by ""ScanTestCode"" asc";
            return await this.unitOfWork.Current.QueryAsync<ScanTestMasterModel>(query);
        }

        /// <inheritdoc/>
        public async Task<IEnumerable<ScanTestMasterServiceModel>> FetchAllScanAsync(ScanTestMasterServiceModel model)
        {
            var where = $@"where 1=1";
            if (model.ScanTestMasterId > 0)
            {
                where += $@" and S.""ScanTestMasterId""={model.ScanTestMasterId}";
            }
            if (model.Active != null)
            {
                where += $@" AND S.""Active"" IS {((bool)model.Active ? "TRUE" : "FALSE")} ";
            }
            if (!string.IsNullOrEmpty(model.Term))
            {
                where += $@" and (lower(S.""ScanTestName"") ilike '%{model.Term.ToLower()}%' )";
            }
            if ((model.LocationId > 0) && (model.LocationId != null))
            {
                where += $@" and smt.""LocationId""={model.LocationId}";
            }
            var query = $@"SELECT Distinct S.""ScanTestMasterId"",S.""ScanTestName"",count(S.*) over() as ""TotalItems"", S.""ScanTestCode"", S.""Active"", S.""CreatedBy"", S.""CreatedDate"", S.""Duration"",
		                            S.""ModifiedBy"", S.""ModifiedDate""
, smt.""LocationId"",L.""Name"" as LocationName
,CS.""FullName"" as ""CreatedByName"",CSR.""RoleName"" as ""CreatedByRole"",
		                            MS.""FullName"" as ""ModifiedByName"",MSR.""RoleName"" as ""ModifiedByRole""
	                            FROM ""ScanTestMaster"" S
								join ""ScanMachineTestMap"" smt on smt.""ScanTestMasterId"" = S.""ScanTestMasterId""
	                            join ""Account"" CS on CS.""AccountId"" = S.""CreatedBy""
	                            join ""Role"" CSR on CSR.""RoleId"" = CS.""RoleId""
	                            left join ""Account"" MS on MS.""AccountId"" =S.""ModifiedBy""								
                            	left join ""Location"" L on L.""LocationId"" =smt.""LocationId""
	                            left join ""Role"" MSR on MSR.""RoleId"" = MS.""RoleId"" 
                            {where} order by S.""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}";
            }
            var response = await this.unitOfWork.Current.QueryAsync<ScanTestMasterServiceModel>(query);
            return response;

        }
    }
}
