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

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

        /// <summary>
        /// The amazon s 3 configuration.
        /// </summary>
        private readonly IAmazonS3Configuration amazonS3Configuration;

        /// <inheritdoc cref="ISpecializationService" />
        /// 
        public SpecializationServices(IUnitOfWork unitOfWork, IAmazonS3Configuration amazonS3Configuration)
        {
            this.unitOfWork = unitOfWork;
            this.amazonS3Configuration = amazonS3Configuration;
        }
        //  public SpecializationServices(IUnitOfWork unitOfWork) => this.unitOfWork = unitOfWork;

        /// <inheritdoc />
        public Task<IEnumerable<SpecializationModel>> FetchAsync(SpecializationFilterModel model)
        {
            var where = " WHERE 1 = 1 ";
            if (!string.IsNullOrEmpty(model.SpecializationName))
            {
                where += $@" AND ""SpecializationName"" ILIKE '%{model.SpecializationName}%'";
            }
            if (model.SpecializationId != null)
            {
                where += $@" AND s.""SpecializationId"" ={model.SpecializationId}";
            }
            if (model.LocationId != null)
            {
                where += $@" AND L.""LocationId"" ={model.LocationId}";
            }
            if (model.Active != null)
            {
                where += $@" AND s.""Active"" IS {((bool)model.Active ? "TRUE" : "FALSE")}";
            }

            var query = $@"SELECT COUNT(*) OVER () AS ""TotalItems"",s.""SpecializationId"", s.""SpecializationIconId"", s.""SpecializationName"", s.""SpecializationDescription"",s.""Active"", s.""CreatedDate"", s.""ModifiedDate"", I.""IconName"",
                        s.*,(CASE WHEN I.""IconName"" IS NOT NULL THEN CONCAT(I.""IconName"") ELSE NULL END) AS ""Url"", s.""EncounterTypeId"", ET.""EncounterName"",
                        cra.""FullName"" AS ""CreatedByName"", mdf.""FullName"" AS ""ModifiedByName""
                      , string_agg(L.""Name"", ', ') as ""LocationNames"",string_agg(L.""LocationId""::text, ', ') as ""LocationIds""
                        FROM ""Specialization"" s
                        LEFT JOIN ""Account"" cra ON cra.""AccountId"" = s.""CreatedBy"" AND cra.""Active"" IS TRUE  
                        LEFT JOIN ""Account"" mdf ON mdf.""AccountId"" = s.""ModifiedBy"" AND mdf.""Active"" IS TRUE
                        left join ""Icons"" I on I.""IconsId"" = s.""SpecializationIconId""

                         left join ""LocationSpecializationMap"" LS ON LS.""SpecializationId"" = s.""SpecializationId""
                             left join ""Location"" L ON L.""LocationId"" = LS.""LocationId""
                            left join ""EncounterType"" ET ON ET.""EncounterTypeId"" = s.""EncounterTypeId"" 
                         {where} 
                        group by s.""SpecializationId"",cra.""FullName"", mdf.""FullName"",I.""IconName"", s.""EncounterTypeId"", ET.""EncounterName""
                        Order by ""SpecializationId"" DESC";

            //$@"SELECT COUNT(*) OVER () AS ""TotalItems"", ""SpecializationId"", ""SpecializationIconId"", ""SpecializationName"", ""SpecializationDescription"",""Active"", ""CreatedDate"", ""ModifiedDate"" FROM ""Specialization"" {where} Order by ""SpecializationId"" DESC";

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

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

        /// <inheritdoc />
        public async Task<int> AddAsync(SpecializationModel model)
        {
            var transaction = this.unitOfWork.BeginTransaction();
            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""SpecializationId"") FROM ""Specialization"" WHERE TRIM(UPPER(""SpecializationName"")) = '{model.SpecializationName.ToUpper().Trim()}'");
            if (checkIf > 0)
            {
                return -1;
            }

            var specialization = new Specialization
            {
                Active = true,
                SpecializationName = model.SpecializationName,
                SpecializationIconId = model.SpecializationIconId,
                SpecializationDescription = model.SpecializationDescription,
                CreatedBy = model.CreatedBy,
                CreatedDate = DateTime.Now,
                Priority = model.Priority != null ? Convert.ToInt32(model.Priority) : 3,
                EncounterTypeId = model.EncounterTypeId
            };

            var specializationId = await this.unitOfWork.Specializations.InsertAsync(specialization, transaction);
            if (specializationId <= 0)
            {
                transaction.Rollback();
                return 0;
            }

            if (model.LocationIds != null)
            {
                var ids = model.LocationIds.Split(',');
                foreach (var locationId in ids)
                {

                    var location = new LocationSpecializationMap
                    {
                        SpecializationId = specializationId,
                        LocationId = Convert.ToInt32(locationId)
                    };
                    var locationInsertId = await this.unitOfWork.LocationSpecializationMap.InsertAsync(location, transaction);
                    if (locationInsertId == 0)
                    {
                        transaction.Rollback();
                        return 0;
                    }
                }
            }
            transaction.Commit();
            return specializationId;
        }

        /// <inheritdoc />
        public async Task<int> UpdateAsync(SpecializationModel model)
        {
            var transaction = this.unitOfWork.BeginTransaction();
            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""SpecializationId"") FROM ""Specialization"" WHERE TRIM(UPPER(""SpecializationName"")) = '{model.SpecializationName.ToUpper().Trim()}' AND ""SpecializationId"" <> {model.SpecializationId}");
            if (checkIf > 0)
            {
                return -1;
            }

            var specialization = await this.unitOfWork.Specializations.FindAsync(m => m.SpecializationId == model.SpecializationId);
            specialization.SpecializationName = model.SpecializationName;
            specialization.SpecializationIconId = model.SpecializationIconId;
            specialization.SpecializationDescription = model.SpecializationDescription;
            specialization.ModifiedBy = model.ModifiedBy;
            specialization.ModifiedDate = DateTime.UtcNow;
            specialization.Priority = model.Priority != null ? Convert.ToInt32(model.Priority) : 3;
            specialization.EncounterTypeId = model.EncounterTypeId;
            var updateResponse = await this.unitOfWork.Specializations.UpdateAsync(specialization, transaction);

            if (updateResponse <= 0)
            {
                transaction.Rollback();
                return 0;
            }
            var query = $@"DELETE from ""LocationSpecializationMap"" where ""SpecializationId""= {specialization.SpecializationId} ";
            var res = await this.unitOfWork.Current.QuerySingleOrDefaultAsync(query, transaction);
            if (res == 0)
            {
                transaction.Rollback();
                return 0;
            }

            if (model.LocationIds != null)
            {
                var ids = model.LocationIds.Split(',');
                foreach (var locationId in ids)
                {

                    var location = new LocationSpecializationMap
                    {
                        SpecializationId = model.SpecializationId,
                        LocationId = Convert.ToInt32(locationId)
                    };
                    var locationInsertId = await this.unitOfWork.LocationSpecializationMap.InsertAsync(location, transaction);
                    if (locationInsertId == 0)
                    {
                        transaction.Rollback();
                        return -1;
                    }
                }
            }
            transaction.Commit();
            return updateResponse;
        }

        /// <inheritdoc />
        public Task<int> DeleteAsync(int specializationId)
        {
            var query = $@"DELETE FROM ""Specialization"" WHERE ""SpecializationId""= {specializationId}";
            return this.unitOfWork.Current.ExecuteAsync(query);
        }

        /// <inheritdoc />
        public Task<int> ModifyStatusAsync(int specializationId, int modifiedBy, bool status)
        {
            var query = $@"UPDATE ""Specialization"" SET ""Active"" = {!status}, ""ModifiedBy"" = {modifiedBy}, ""ModifiedDate"" = NOW() AT TIME ZONE 'UTC' WHERE ""SpecializationId""= {specializationId}";
            return this.unitOfWork.Current.ExecuteAsync(query);
        }

        /// <inheritdoc />
        public Task<IEnumerable<SpecializationModel>> FetchSpecializationJoinConsultationTypeAsync(SpecializationFilterModel model)
        {
            var where = " WHERE 1 = 1 ";
            if (model.LocationId != null)
            {
                where += $@" AND L.""LocationId"" ={model.LocationId}";
            }
            if (!string.IsNullOrEmpty(model.SpecializationName))
            {
                where += $@" AND (""SpecializationName"" ILIKE '%{model.SpecializationName}%' or c.""Name"" ILIKE '%{model.SpecializationName}%')";
            }
            if (!string.IsNullOrEmpty(model.ConsultationtypeName))
            {
                where += $@" AND  c.""Name"" ILIKE '%{model.ConsultationtypeName}%'";
            }
            if (model.SpecializationId != null)
            {
                where += $@" AND s.""SpecializationId"" ={model.SpecializationId}";
            }
            
            if (model.Active != null)
            {
                where += $@" AND s.""Active"" IS {((bool)model.Active ? "TRUE" : "FALSE")}";
            }

            var query = $@"SELECT COUNT(*) OVER () AS ""TotalItems"",s.""SpecializationId"", s.""SpecializationIconId"", s.""SpecializationName"", s.""SpecializationDescription"",s.""Active"", s.""CreatedDate"", s.""ModifiedDate"", I.""IconName"",
                        s.*,(CASE WHEN I.""IconName"" IS NOT NULL THEN CONCAT(I.""IconName"") ELSE NULL END) AS ""Url"", s.""EncounterTypeId"", ET.""EncounterName"",
                        cra.""FullName"" AS ""CreatedByName"", mdf.""FullName"" AS ""ModifiedByName"",c.""ConsultationTypeId"",c.""Name""as ""ConsultationTypeName""
                      , string_agg(L.""Name"", ', ') as ""LocationNames"",string_agg(L.""LocationId""::text, ', ') as ""LocationIds""
                        FROM ""Specialization"" s
                        LEFT JOIN ""Account"" cra ON cra.""AccountId"" = s.""CreatedBy"" AND cra.""Active"" IS TRUE  
                        LEFT JOIN ""Account"" mdf ON mdf.""AccountId"" = s.""ModifiedBy"" AND mdf.""Active"" IS TRUE
                        left join ""Icons"" I on I.""IconsId"" = s.""SpecializationIconId""

                         left join ""LocationSpecializationMap"" LS ON LS.""SpecializationId"" = s.""SpecializationId""
                             left join ""Location"" L ON L.""LocationId"" = LS.""LocationId""
                            left join ""EncounterType"" ET ON ET.""EncounterTypeId"" = s.""EncounterTypeId""
                         cross join ""ConsultationType"" c 
                         { where} 
                        group by s.""SpecializationId"",cra.""FullName"", mdf.""FullName"",I.""IconName"", s.""EncounterTypeId"", ET.""EncounterName"",c.""ConsultationTypeId"",c.""Name""
                        Order by ""SpecializationId"" DESC";

            //$@"SELECT COUNT(*) OVER () AS ""TotalItems"", ""SpecializationId"", ""SpecializationIconId"", ""SpecializationName"", ""SpecializationDescription"",""Active"", ""CreatedDate"", ""ModifiedDate"" FROM ""Specialization"" {where} Order by ""SpecializationId"" DESC";

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

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