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

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

        /// <inheritdoc cref="IProviderService" />
        public PracticeServices(IUnitOfWork unitOfWork) => this.unitOfWork = unitOfWork;

        /// <inheritdoc />
        public async Task<int> CreateAsync(PracticeModel model)
        {
            using (var transaction = this.unitOfWork.BeginTransaction())
            {
                var checkIf = await this.CheckPracticeAsync(model.TIN, model.PracticeId, transaction);
                if (checkIf != 0)
                {
                    transaction.Rollback();
                    return checkIf;
                }
                var practice = new Practice
                {
                    FullName = model.FullName,
                    TIN = model.TIN,
                    CreatedBy = model.CreatedBy,
                    CreatedDate = DateTime.UtcNow,
                    PracticeType = model.PracticeType,
                    Active = true
                };
                practice.PracticeId = await this.unitOfWork.Practices.InsertAsync(practice, transaction);
                if (practice.PracticeId == 0)
                {
                    transaction.Rollback();
                    return 0;
                }
                if (model.Locations != null && model.Locations.Count > 0)
                {
                    foreach (var item in model.Locations)
                    { // need to check location name should be unique .
                        var location = new Location
                        {
                            PracticeId = practice.PracticeId,
                            Name = !string.IsNullOrEmpty(item.FullName) ? item.FullName : item.City + "," + item.State,
                            Active = true,
                            CountryId = item.CountryId,
                            CreatedBy = model.CreatedBy,
                            CreatedDate = DateTime.UtcNow
                        };

                        location.LocationId = await this.unitOfWork.Locations.InsertAsync(location, transaction);
                        if (location.LocationId != 0)
                        {
                            continue;
                        }

                        transaction.Rollback();
                        return 0;
                    }
                }
                transaction.Commit();
                return practice.PracticeId;
            }
        }

        /// <inheritdoc />
        public Task<AccountModel> ApproveAsync(int practiceId) => throw new NotImplementedException();

        /// <inheritdoc />
        public Task<int> RejectAsync(int practiceId, string rejectedComments) => throw new NotImplementedException();

        /// <inheritdoc />
        public Task<PracticeModel> FindAsync(int practiceId) => throw new NotImplementedException();

        /// <inheritdoc />
        public Task<IEnumerable<PracticeLocationModel>> FetchAsync(PracticeFilterModel model)
        {
            var where = $@"WHERE prcl.""Active"" IS TRUE ";

            if (model.PracticeId != null && model.PracticeId > 0)
            {
                where += $@" AND prc.""PracticeId"" = {model.PracticeId} ";
            }

            if (!string.IsNullOrEmpty(model.FullName))
            {
                where += $@" AND prc.""FullName"" ILIKE '%{model.FullName}%' ";
            }

            if (!string.IsNullOrEmpty(model.TIN))
            {
                where += $@" AND prc.""TIN"" ILIKE '%{model.TIN}%' ";
            }

            if (model.Active != null)
            {
                where += $@" AND prcl.""Active"" IS {((bool)model.Active ? "TRUE" : "FALSE")} ";
            }

            if (!string.IsNullOrEmpty(model.Email))
            {
                where += $@" AND prcl.""Email"" ILIKE '%{model.Email}%' ";
            }

            if (!string.IsNullOrEmpty(model.Mobile))
            {
                where += $@" AND prcl.""Phone"" ILIKE '%{model.Mobile}%' ";
            }

            if (!string.IsNullOrEmpty(model.CreatedDate))
            {
                where += $@" AND prcl.""CreatedDate""::DATE = '{model.CreatedDate}'::DATE ";
            }            
            var query = $@"SELECT Count(prcl.""LocationId"") OVER() AS ""TotalItems"", prc.""PracticeId"", prc.""FullName"" AS ""PracticeName"",
                                prc.""TIN"" AS ""PracticeTIN"", prc.""PracticeType"", prcl.*,ProcLoc.""Email"", ProcLoc. ""Phone"",ProcLoc.""StreetAddress"",ProcLoc.""AddressLine2"",ProcLoc.""Zipcode"",  ProcLoc.""CountryId"", 
ProcLoc.""StateId"" as State,ProcLoc.""CityId"" as City,ProcLoc.""PracticeLocationId""
                                FROM ""Practice"" prc JOIN ""Location"" prcl ON prc.""PracticeId"" = prcl.""PracticeId""
							
left join ""PracticeLocation"" ProcLoc on ProcLoc.""LocationId""=prcl.""LocationId""
left join ""Location"" loc on loc.""LocationId""=prcl.""LocationId""
                            AND prcl.""Active"" IS TRUE JOIN ""Country"" ctr ON ctr.""CountryId"" = prcl.""CountryId"" AND ctr.""Active"" IS TRUE
                             {where}  ORDER BY prc.""PracticeId"" DESC";

                   // left join ""PracticeLocation"" ProcLoc on ProcLoc.""PracticeId"" = prc.""PracticeId""

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

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

        /// <inheritdoc />
        public async Task<int> UpdateAsync(PracticeModel model)
        {           
                using (var transaction = this.unitOfWork.BeginTransaction())
                {
                    var checkIf = await this.CheckPracticeAsync(model.TIN, model.PracticeId, transaction);
                    if (checkIf != 0)
                    {
                        transaction.Rollback();
                        return checkIf;
                    }

                    var practice = await this.unitOfWork.Practices.FindAsync(m => m.PracticeId == model.PracticeId);
                    practice.FullName = model.FullName;
                    practice.TIN = model.TIN;
                    practice.PracticeType = model.PracticeType;
                    practice.ModifiedBy = model.ModifiedBy;
                    practice.ModifiedDate = DateTime.UtcNow;

                    var updated = await this.unitOfWork.Practices.UpdateAsync(practice);
                    if (updated == 0)
                    {
                        transaction.Rollback();
                        return 0;
                    }

                    if (model.Locations != null && model.Locations.Count > 0)
                    {
                       // var practiceLocation = await this.unitOfWork.Locations.FindAsync(m => m.PracticeId == model.PracticeId);

                        //var query = $@"DELETE FROM ""Location"" WHERE ""LocationId"" NOT IN ({string.Join(",", model.Locations.Select(m => m.PracticeLocationId))}) AND ""PracticeId"" = '{model.PracticeId}'";
                       // await this.unitOfWork.Current.ExecuteAsync(query, null, transaction);
                        updated = 0;
                        foreach (var item in model.Locations)
                        {
                            int response;
                            if (item.PracticeLocationId == 0)
                            {
                                //    var location = new Location
                                //    {
                                //        PracticeId = practice.PracticeId,
                                //        Name = !string.IsNullOrEmpty(item.FullName) ? item.FullName : item.City + "," + item.State,
                                //        Active = true,
                                //        CountryId = item.CountryId,
                                //        CreatedBy = Convert.ToInt32(model.ModifiedBy),
                                //        CreatedDate = DateTime.UtcNow
                                //    };
                                //    response = await this.unitOfWork.Locations.InsertAsync(location, transaction);
                               
                                    var practiceLocation = new PracticeLocation
                                    {
                                        FullName = model.FullName,
                                        Email = item.Email,
                                        Phone = item.Phone,
                                        Extension = item.Extension,
                                        StreetAddress = item.StreetAddress,
                                        AddressLine2 = item.AddressLine2,
                                        Zipcode = item.Zipcode,
                                        CountryId = item.CountryId,
                                        Active = item.Active,
                                        CreatedBy = item.CreatedBy,
                                        CreatedDate = DateTime.Now,
                                        //ModifiedBy = item
                                        //ModifiedDate = item
                                        PracticeId = item.PracticeId,
                                        StateId = item.State,
                                        CityId =item.City,
                                        LocationId=item.LocationId
                                    };
                                    practiceLocation.CountryId = item.CountryId;
                                    response = await this.unitOfWork.PracticeLocations.InsertAsync(practiceLocation);
                               
                            }
                            else
                            {
                               
                                var practiceLocation = await this.unitOfWork.PracticeLocations.FindAsync(m => m.PracticeLocationId == item.PracticeLocationId);
                                //practiceLocation.Name = !string.IsNullOrEmpty(item.FullName) ? item.FullName : item.City + "," + item.State;
                                //practiceLocation.CountryId = item.CountryId;
                                //response = await this.unitOfWork.Locations.UpdateAsync(practiceLocation, transaction);
                                practiceLocation.FullName = model.FullName;
                                practiceLocation.Email = item.Email;
                                practiceLocation.Phone = item.Phone;
                                practiceLocation.Extension = item.Extension;
                                practiceLocation.StreetAddress = item.StreetAddress;
                                practiceLocation.AddressLine2 = item.AddressLine2;
                                practiceLocation.Zipcode = item.Zipcode;
                                practiceLocation.CountryId = item.CountryId;
                                practiceLocation.Active = item.Active;
                                practiceLocation.ModifiedBy = item.CreatedBy;
                                practiceLocation.ModifiedDate = DateTime.Now;
                                practiceLocation.PracticeId = item.PracticeId;
                                practiceLocation.StateId = item.State;
                                practiceLocation.CityId = item.City;
                                practiceLocation.LocationId = item.LocationId;


                                practiceLocation.CountryId = item.CountryId;
                            response = await this.unitOfWork.PracticeLocations.UpdateAsync(practiceLocation);
                        }

                            if (response != 0)
                            {
                                updated++;
                            }
                        }

                        if (updated != model.Locations.Count)
                        {
                            transaction.Rollback();
                            return 0;
                        }
                    }

                    transaction.Commit();
                    return updated;
                }           
        }

        /// <inheritdoc />
        public async Task<IEnumerable<PracticeLocationModel>> FetchOnlyPracticeInfo(PracticeFilterModel model)
        {
            var where = " where 1=1";
            if (!string.IsNullOrEmpty(model.LocationIds))
            {
                where += $@" and l.""LocationId"" in ({model.LocationIds})";
            }

            var query = $@"SELECT p.""PracticeId"", p.""FullName"", p.""PracticeType"", p.""TIN"" as ""PracticeTIN"", l.""Name"" as ""Location"", l.""NameLoc"" , l.""LocationId""
	                                FROM public.""Practice"" p
	                                join ""Location"" l on l.""PracticeId"" = p.""PracticeId"" 
	                                {where}";

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

        /// <summary>
        /// The check provider async.
        /// </summary>
        /// <param name="tin">
        ///     The tin.
        /// </param>
        /// <param name="practiceId">
        ///     The practice Id.
        /// </param>
        /// <param name="transaction">
        /// The transaction
        /// </param>
        /// <returns>
        /// The <see cref="int"/>.
        /// </returns>
        private async Task<int> CheckPracticeAsync(string tin, int practiceId, IDbTransaction transaction)
        {
            var checkIf = await this.unitOfWork.Current.QueryFirstOrDefaultAsync<int>($@"SELECT COUNT(""PracticeId"") FROM ""Practice"" WHERE ""TIN"" = '{tin}' AND  ""PracticeId"" <> '{practiceId}'", transaction);
            return checkIf > 0 ? -1 : 0;
        }
    }
}
