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


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

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


        public async Task<int> AddAsync(ScanMachineInsertModel model)
        {
            var query = $@"Select count(*) from ""ScanMachineMaster"" where upper(""MachineName"")= upper('{model.MachineName}')";
            //var query = $@"Select count(*) from ""ScanMachineMaster"" smm left join ""ScanMachineTestMap"" smtm on smtm.""ScanMachineMasterId"" = smm.""ScanMachineMasterId""
            //                where upper(""MachineName"")= upper('{model.MachineName}') and smtm.""LocationId"" in ({model.LocationIds})";
            var checkIf = this.unitOfWork.Current.QuerySingleOrDefault<int>(query);
            if (checkIf != 0)
            {
                return -1;
            }
            var transaction = this.unitOfWork.BeginTransaction();
            var scanMachine = new ScanMachineMaster
            {
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                MachineName = model.MachineName,
                Active = true,
                DisplayName = model.DisplayName
            };
            scanMachine.ScanMachineMasterId = await this.unitOfWork.ScanMachineMasters.InsertAsync(scanMachine, transaction);
            if (scanMachine.ScanMachineMasterId == 0)
            {
                return -2;
            }

            if (!string.IsNullOrEmpty(model.PerformedTestId))
            {
                var testId = model.PerformedTestId.Split(',');
                var models = testId.Select(m => new ScanMachineTestMap
                {
                    ScanMachineMasterId = scanMachine.ScanMachineMasterId,
                    LocationId = model.LocationId,
                    ScanTestMasterId = int.Parse(m)
                });
                var testResponse = await this.unitOfWork.ScanMachineTestMaps.BulkInsertAsync(models, transaction);
                if (testResponse == 0)
                {
                    transaction.Rollback();
                    return -2;
                }
            }
            transaction.Commit();
            return scanMachine.ScanMachineMasterId;
        }

        public async Task<int> UpdateAsync(ScanMachineInsertModel model)
        {
            try
            {
                var checkQuery = $@"Select count(*) from ""ScanMachineMaster"" where lower(""MachineName"") = lower('{model.MachineName}') and ""ScanMachineMasterId"" <> {model.ScanMachineMasterId}";
                var checkIf = await this.unitOfWork.Current.QuerySingleOrDefaultAsync<int>(checkQuery);
                if (checkIf != 0)
                {
                    return -1;
                }
                var scanMachine = await this.unitOfWork.ScanMachineMasters.FindAsync(m => m.ScanMachineMasterId == model.ScanMachineMasterId);
                if (scanMachine == null)
                {
                    return -2;
                }
                var transaction = this.unitOfWork.BeginTransaction();
                scanMachine.ModifiedBy = model.ModifiedBy;
                scanMachine.ModifiedDate = DateTime.Now;
                scanMachine.DisplayName = model.DisplayName;
                scanMachine.MachineName = model.MachineName;
                var updateResponse = await this.unitOfWork.ScanMachineMasters.UpdateAsync(scanMachine, transaction);
                if (updateResponse == 0)
                {
                    transaction.Rollback();
                    return -1;
                }
                if (!string.IsNullOrEmpty(model.PerformedTestId))
                {
                    var query = $@"DELETE FROM ""ScanMachineTestMap"" where ""ScanMachineMasterId"" = {model.ScanMachineMasterId}";
                    await this.unitOfWork.Current.QueryAsync(query, transaction);
                    var performedTests = model.PerformedTestId.Split(',');
                    var models = performedTests.Select(m => new ScanMachineTestMap
                    {
                        LocationId = model.LocationId,
                        ScanMachineMasterId = scanMachine.ScanMachineMasterId,
                        ScanTestMasterId = int.Parse(m)
                    });
                    var testResponse = await this.unitOfWork.ScanMachineTestMaps.BulkInsertAsync(models, transaction);
                    if (testResponse == 0)
                    {
                        transaction.Rollback();
                        return -4;
                    }
                }
                transaction.Commit();
                return scanMachine.ScanMachineMasterId;
            }
            catch (Exception ex)
            {
                return -5;
            }

        }

        public async Task<IEnumerable<ScanMachineFetchModel>> FetchAllScanMachineTests(ScanMachineFetchModel model)
        {

            var where = "where 1=1";
            if (model.LocationId > 0)
            {
                where += $@" and  smtm.""LocationId"" = {model.LocationId} ";
            }
            if (model.ScanMachineMasterId > 0)
            {
                where += $@" and  SMM.""ScanMachineMasterId"" = {model.ScanMachineMasterId} ";
            }
            if ((model.ScanTestId) > 0)
            {
                where += $@" and  smt.""ScanTestMasterId"" = {model.ScanTestId} ";
            }
            if (!string.IsNullOrEmpty(model.MachineName))
            {
                where += $@" and SMM.""MachineName"" = '{model.MachineName}'";
            }
            if (model.Active != null)
            {
                where += $@" AND SMM.""Active"" = {model.Active}";
            }
            if (!string.IsNullOrEmpty(model.DisplayName))
            {
                where += $@" and SMM.""DisplayName"" = '{model.DisplayName}'";
            }
            if (!string.IsNullOrEmpty(model.ScanTestName))
            {
                where += $@" and smt.""ScanTestName"" = '{model.ScanTestName}'";
            }
            //if (!string.IsNullOrEmpty(model.LocationNames))
            //{
            //    where += $@" and ll.""NameLoc"" = '{model.LocationNames}'";
            //}
            //            var query = $@"SELECT count(SMM.*) over() as ""TotalItems"",string_agg(l.""ScanTestMasterId""::text, ', ') as ""ScanTestMasterId"",
            //                                string_agg(stm.""ScanTestName"", ',') as ""ScanTestName"",L.""LocationId"",
            //                                --ll.""LocationId"",ll.""NameLoc"" as ""LocationNames"",
            //                                --string_agg(ll.""LocationId""::text, ', ') as ""LocationIds"", string_agg(ll.""NameLoc"", ', ')  as ""LocationNames"",
            //                                SMM.""ScanMachineMasterId"", SMM.""MachineName"",SMM.""DisplayName"", 
            //SMM.""CreatedBy"", SMM.""CreatedDate"", SMM.""ModifiedBy"", 
            //		                        SMM.""ModifiedDate"",SMM.""Active"" , CA.""FullName"" as ""CreatedByName"",
            //		                        CM.""FullName"" as ""ModifiedByName""

            //                            FROM ""ScanMachineMaster"" SMM
            //                            join ""Account"" CA on CA.""AccountId"" = SMM.""CreatedBy""

            //                            left join ""ScanMachineTestMap"" L on L.""ScanMachineMasterId"" = SMM.""ScanMachineMasterId""

            //                            left join ""ScanTestMaster"" stm on stm.""ScanTestMasterId"" = l.""ScanTestMasterId""
            //                            left join ""Location"" ll on ll.""LocationId"" = L.""LocationId""

            //                            left join ""Account"" CM on CM.""AccountId"" = SMM.""ModifiedBy""
            //                            {where} group by SMM.""ScanMachineMasterId"",CA.""FullName"" ,CM.""FullName"",L.""LocationId""
            //                            order by SMM.""CreatedDate"" desc";

            var query = $@"select count(SMM.*) over() as ""TotalItems"", smm.""ScanMachineMasterId"",smm.""MachineName"",smm.""DisplayName"",
                string_agg(smt.""ScanTestMasterId""::text, ', ') as ""ScanTestMasterId"",string_agg(smt.""ScanTestName"", ', ') as ""ScanTestName"",smm.""CreatedBy"",
                smm.""CreatedDate"",smm.""ModifiedBy"",SMM.""ModifiedDate"",SMM.""Active"" , CA.""FullName"" as ""CreatedByName"",CM.""FullName"" as ""ModifiedByName"",smtm.""LocationId"",ll.""NameLoc"" as ""LocationNames""
                    FROM ""ScanMachineMaster"" SMM
                    left join ""ScanMachineTestMap"" smtm on smtm.""ScanMachineMasterId"" = SMM.""ScanMachineMasterId""
                    left join ""Location"" ll on ll.""LocationId"" = smtm.""LocationId""
                    left join ""ScanTestMaster"" smt on smt.""ScanTestMasterId"" = smtm.""ScanTestMasterId"" and smt.""Active"" IS true
                    left join ""Account"" CA on CA.""AccountId"" = SMM.""CreatedBy""
                    left join ""Account"" CM on CM.""AccountId"" = SMM.""ModifiedBy""
                    {where} group by  smm.""ScanMachineMasterId"", smm.""MachineName"", smm.""DisplayName"", CA.""FullName"", CM.""FullName"",smtm.""LocationId"",ll.""NameLoc""
                    order by SMM.""CreatedDate"" desc";

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

        public async Task<int> ActivateOrDeactivateMachine(ScanMachineFetchModel model)

        {
            try
            {
                var record = await this.unitOfWork.ScanMachineMasters.FindAsync(x => x.ScanMachineMasterId == model.ScanMachineMasterId);
                var item = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"select count(""ScanMachineMasterId"") from ""BookScanAppointment"" where ""AppointmentDate"" > CURRENT_DATE and ""ScanMachineMasterId"" = {model.ScanMachineMasterId}");
                
                if (record == null) { return -1; }
                else if (item > 0) { return -2; }

                record.ModifiedBy = model.ModifiedBy;
                record.ModifiedDate = DateTime.Now;
                record.Active = model.Active == true ? true : false;

                return await this.unitOfWork.ScanMachineMasters.UpdateAsync(record);
            }
            catch (Exception ex)
            {
                throw;
            }
        }

        public async Task<IEnumerable<ScanMachineFetchModel>> FetchImportMachineTests(ScanMachineFetchModel model)
        {
            var where = "where 1=1";
            if (model.LocationId > 0)
            {
                where += $@" and  L.""LocationId"" NOT IN ({model.LocationId}) ";
            }
            var query = $@"SELECT count(SMM.*) over() as ""TotalItems"",string_agg(l.""ScanTestMasterId""::text, ', ') as ""ScanTestMasterId"",
string_agg(stm.""ScanTestName"", ',') as ""ScanTestName"",ll.""NameLoc"" as ""LocationNames"",
SMM.""ScanMachineMasterId"", SMM.""MachineName"",SMM.""DisplayName"", SMM.""CreatedBy"", SMM.""CreatedDate"", SMM.""ModifiedBy"", 
		                        SMM.""ModifiedDate"",SMM.""Active"" , CA.""FullName"" as ""CreatedByName"",
		                        CM.""FullName"" as ""ModifiedByName""

                            FROM ""ScanMachineMaster"" SMM
                            join ""Account"" CA on CA.""AccountId"" = SMM.""CreatedBy""

                            left join ""ScanMachineTestMap"" L on L.""ScanMachineMasterId"" = SMM.""ScanMachineMasterId""

                            left join ""ScanTestMaster"" stm on stm.""ScanTestMasterId"" = l.""ScanTestMasterId""
                            left join ""Location"" ll on ll.""LocationId"" = L.""LocationId""

                            left join ""Account"" CM on CM.""AccountId"" = SMM.""ModifiedBy""
                            {where}  and L.""ScanMachineMasterId"" not in (select ""ScanMachineMasterId"" from ""ScanMachineTestMap"" 
                        where ""LocationId"" = {model.LocationId}) group by    SMM.""ScanMachineMasterId"",ll.""NameLoc"",CA.""FullName"" ,CM.""FullName""
                            order by SMM.""CreatedDate"" desc";
            if (model.PageIndex == null || model.PageSize == null)
            {
                return await this.unitOfWork.Current.QueryAsync<ScanMachineFetchModel>(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<ScanMachineFetchModel>(query);
        }

        public async Task<int> ModifyImportAsync(MachineImportModel model)
        {
            try
            {
                using (var transaction = this.unitOfWork.BeginTransaction())
                {
                    foreach (var item in model.Machines)
                    {
                        var testId = item.ScanTestMasterId.Split(',');
                        var models = testId.Select(m => new ScanMachineTestMap { ScanMachineMasterId = item.ScanMachineMasterId, LocationId = model.LocationId, ScanTestMasterId = int.Parse(m) });
                        var response = await this.unitOfWork.ScanMachineTestMaps.BulkInsertAsync(models, transaction);

                    }
                    transaction.Commit();
                    return 1;
                }
            }
            catch (Exception ex)
            {
                throw;
            }
        }
    }
}