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;
    using Hims.Shared.UserModels.Common;
    using Shared.EntityModels;

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

        /// <inheritdoc cref="ICubicleService" />
        public CubicleService(IUnitOfWork unitOfWork)
        {
            this.unitOfWork = unitOfWork;
        }

        /// <inheritdoc />
        public Task<IEnumerable<CubicleModel>> FetchAsync(CubicleFilterModel model)
        {
            var where = " WHERE 1 = 1 ";
            if (!string.IsNullOrEmpty(model.CubicleName))
            {
                where += $@" AND c.""Name"" = '{model.CubicleName}'";
            }
            if (model.Active != null)
            {
                where += $@" AND s.""Name"" = '{((bool)model.Active ? "Active" : "InActive")}'";
            }
            if (model.LocationId != 0 && model.LocationId != null)
            {
                where += $@" AND c.""LocationId""={model.LocationId} ";
            }
            if (model.Assign == 1)
            {
                where += $@" AND c.""ProviderId"" is not null ";
            }
            else if (model.Assign == 0)
            {
                where += $@" AND c.""ProviderId"" is null ";
            }
            var query = $@"SELECT
                    Count(*) OVER() AS ""TotalItems"",
                    c.*,
                    p.""ProviderId"",
                    p.""FullName"" as ""ProviderName"",
                    ppa.""FullName"" as ""AssignedName"",
                    s.""Name"" as ""ActiveStatusName"",
					a.""FullName"" AS ""CreatedByName"" , ct.""FullName"" AS ""ModifiedByName"", l.""Name"" AS ""LocationName""
                FROM ""Cubicle"" c
                LEFT JOIN ""Account"" pp on pp.""AccountId"" = c.""AccountId""
                LEFT JOIN ""Account"" ppa on ppa.""AccountId"" = pp.""AccountId""
				LEFT JOIN ""AccountProviderMap"" apm on apm.""AccountId"" = pp.""AccountId""
				LEFT JOIN ""Provider"" p on p.""ProviderId"" = apm.""ProviderId""
				LEFT JOIN ""Account"" a on a.""AccountId"" = c.""CreatedBy""
				LEFT JOIN ""Account"" ct on ct.""AccountId"" = c.""ModifiedBy""
				LEFT JOIN ""Location"" l on l.""LocationId"" = c.""LocationId""
                LEFT JOIN ""ActiveStatus"" s on s.""ActiveStatusId"" = c.""ActiveStatusId"" {where}
                order by c.""CubicleId"" desc";

            if (model.PageIndex <= 0)
            {
                return this.unitOfWork.Current.QueryAsync<CubicleModel>(query);
            }

            model.PageIndex -= 1;
            query += " LIMIT " + model.PageSize + " offset " + (model.PageIndex * model.PageSize);
            return this.unitOfWork.Current.QueryAsync<CubicleModel>(query);
        }

        /// <inheritdoc />
        public async Task<GenericResponse> InsertAsync(CubicleModel model)
        {
            var transaction = this.unitOfWork.BeginTransaction();

            var query = $@"SELECT ""CubicleId"" from ""Cubicle"" WHERE UPPER(TRIM(""Name"")) = UPPER(TRIM('{model.Name}'))";
            var isExists = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>(query, transaction);
            if (isExists > 0)
            {
                transaction.Rollback();
                return new GenericResponse
                {
                    Status = GenericStatus.Info
                };
            }

            var cubicle = new Cubicle
            {
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                Name = model.Name,
                //ActiveStatusId = model.ActiveStatusId,
                ActiveStatusId = 1,
                LocationId = model.LocationId
            };

            var widgetId = await this.unitOfWork.Cubicle.InsertAsync(cubicle, transaction);
            if (widgetId <= 0)
            {
                transaction.Rollback();
                return new GenericResponse
                {
                    Status = GenericStatus.Error
                };
            }

            transaction.Commit();
            return new GenericResponse
            {
                Status = GenericStatus.Success
            };
        }

        public async Task<GenericResponse> UpdateAsync(CubicleModel model)
        {
            var transaction = this.unitOfWork.BeginTransaction();
            var query = $@"SELECT COUNT(""CubicleId"") from ""Cubicle"" WHERE  UPPER(TRIM(""Name"")) = UPPER(TRIM('{model.Name}')) and ""CubicleId"" <> {model.CubicleId}";
            var isExists = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>(query, transaction);
            if (isExists > 0)
            {
                return new GenericResponse
                {
                    Status = GenericStatus.Info
                };
            }

            var cubicle = await this.unitOfWork.Cubicle.FindAsync(x => x.CubicleId == model.CubicleId, transaction);

            if (cubicle.AccountId > 0)
            {
                return new GenericResponse
                {
                    Status = GenericStatus.Info
                };
            }
            cubicle.ModifiedBy = model.ModifiedBy;
            cubicle.ModifiedDate = DateTime.Now;
            cubicle.Name = model.Name;
            //cubicle.ActiveStatusId = model.ActiveStatusId;
            cubicle.ActiveStatusId = 1;
            cubicle.LocationId = model.LocationId;
            var updateResponse = await this.unitOfWork.Cubicle.UpdateAsync(cubicle, transaction);

            if (updateResponse <= 0)
            {
                transaction.Rollback();
                return new GenericResponse
                {
                    Status = GenericStatus.Error
                };
            }


            transaction.Commit();
            return new GenericResponse
            {
                Status = GenericStatus.Success
            };
        }

        public async Task<int> AssignAsync(CubicleModel model)
        {
            var account = await this.unitOfWork.Accounts.FindAsync(x => x.AccountId == model.AccountId);
            // Check Is Consultant Doctor Assigned or not
            var check = await this.AssignConsultantDoctorAsync(new CubicleModel
            {
                AccountId = model.AccountId
            });
            if (check == null)
            {
                return -2;
            }

            if (model.AccountId != null)
            {
                var checkRecord = await this.unitOfWork.Cubicle.FindAsync(x => x.CubicleId == model.CubicleId && x.AccountId == model.AccountId);
                if (checkRecord != null)
                {
                    return -1;
                }
            }

            var record = await this.unitOfWork.Cubicle.FindAsync(x => x.CubicleId == model.CubicleId);
            record.AccountId = model.AccountId;
            return await this.unitOfWork.Cubicle.UpdateAsync(record);
        }
        public async Task<int> UnassignAsync(CubicleModel model)
        {
            var checkRecord = await this.unitOfWork.Cubicle.FindAsync(x => x.CubicleId == model.CubicleId && x.AccountId == model.AccountId);
            if (checkRecord != null)
            {
                checkRecord.AccountId = null;
                return await this.unitOfWork.Cubicle.UpdateAsync(checkRecord);
            }
            return -1;


        }
        public async Task<Resource> AssignConsultantDoctorAsync(CubicleModel model)
        {
            var checkQuery = $@"select p.""ProviderId"" as ""Id"", p.""FullName"" as ""Value"" FROM ""AccountProviderMap"" ap JOIN ""Provider"" p on p.""ProviderId"" = ap.""ProviderId"" WHERE ap.""AccountId"" = {model.AccountId} ";
            var checkResults = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<Resource>(checkQuery);
            if (checkResults != null)
            {
                if (model.ProviderId != null)
                {
                    var updateQuery = $@"UPDATE ""AccountProviderMap"" SET ""ProviderId"" = {model.ProviderId} WHERE ""AccountId"" = {model.AccountId}";
                    var updateResults = await this.unitOfWork.Current.ExecuteAsync(updateQuery);
                    if (updateResults > 0)
                    {
                        return await this.unitOfWork.Current.QueryFirstOrDefaultAsync<Resource>(checkQuery);
                    }
                }
                return checkResults;
            }

            if (model.ProviderId != null)
            {
                var insertQuery = $@"INSERT INTO ""AccountProviderMap""(""AccountId"", ""ProviderId"") VALUES({model.AccountId}, {model.ProviderId})";
                var insertResults = await this.unitOfWork.Current.ExecuteAsync(insertQuery);
                if (insertResults > 0)
                {
                    return await this.unitOfWork.Current.QueryFirstOrDefaultAsync<Resource>(checkQuery);
                }
            }

            return null;
        }

        public async Task<int> UnAssignConsultantDoctorAsync(CubicleModel model)
        {
            var deleteQuery = $@"DELETE FROM ""AccountProviderMap"" WHERE ""AccountId"" = {model.AccountId}";
            return await this.unitOfWork.Current.ExecuteAsync(deleteQuery);
        }

        /// <inheritdoc />
        public async Task<int> UpadteStatusAsync(int id, int accountStatusId, int modifiedBy)
        {
            var transaction = this.unitOfWork.BeginTransaction();
            var query = $@"SELECT ""CubicleId"" from ""Cubicle"" WHERE ""CubicleId"" = {id}";
            var isExists = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>(query, transaction);

            var result = 0;
            if (isExists > 0)
            {
                var cubicle = await this.unitOfWork.Cubicle.FindAsync(x => x.CubicleId == id, transaction);

                cubicle.ModifiedBy = modifiedBy;
                cubicle.ModifiedDate = DateTime.Now;
                cubicle.ActiveStatusId = accountStatusId;

                result = await this.unitOfWork.Cubicle.UpdateAsync(cubicle, transaction);
            }

            if (result <= 0)
            {
                transaction.Rollback();
                return -1;
            }

            transaction.Commit();
            return result;
        }

        /// <inheritdoc />
        public async Task<int> DeleteAsync(int cubicleId)
        {

            var query = $@"DELETE FROM ""Cubicle"" WHERE ""CubicleId"" = {cubicleId} AND ""AccountId"" IS NULL";
            var result = await this.unitOfWork.Current.ExecuteAsync(query);
            return result;

        }
    }
}