using Lada.Framework.Data.Domains;
using Lada.Framework.Data.Domains.Categories;
using Lada.Framework.Data.Repositories;
using Lada.Framework.DTO;
using Lada.Framework.DTO.SmartTable;
using Lada.Framework.Infrastructures;
using Lada.Framework.Infrastructures.Helpers;
using Ladaer.BackEnd.Infrastructures.DTO;
using Ladaer.BackEnd.Infrastructures.DTO.Items.Categories;
using Ladaer.BackEnd.Infrastructures.DTO.Models.Categories;
using Ladaer.BackEnd.Infrastructures.DTO.Results.Categories;
using Microsoft.EntityFrameworkCore;

namespace Ladaer.BackEnd.Infrastructures.Services.Categories
{
    public interface IAgencyService
    {
        /// <summary>
        /// Tìm kiếm đại lý
        /// </summary>
        /// <param name="param"></param>
        /// <returns></returns>
        Task<SmartTableResult<AgencyItem>> Search(SmartTableParam param);

        /// <summary>
        /// Tạo/cập nhật đại lý
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        Task<AgencyResult> CreateOrUpdate(AgencyModel model);

        /// <summary>
        /// Lấy thông tin đại lý
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        Task<AgencyModel> GetById(int id);

        /// <summary>
        /// Xóa đại lý
        /// </summary>
        /// <param name="id"></param>
        /// <param name="currentUser"></param>
        /// <returns></returns>
        Task<BaseResult> Delete(int id, CurrentUser currentUser);

        /// <summary>
        /// Cập nhật trạng thái đại lý
        /// </summary>
        /// <param name="agencyStatusModel"></param>
        /// <returns></returns>
        Task<BaseResult> UpdateStatus(AgencyStatusModel agencyStatusModel);

        /// <summary>
        /// Lấy danh sách chi nhánh
        /// </summary>
        /// <param name="keyword"></param>
        /// <returns></returns>
        Task<List<AgencyTreeItem>> GetTree(string keyword);


        Task<List<AgencyItem>> AutoComplete(string keyword);

        Task<AgencyCoverageModel> GetCoverageModel(int agencyId);
    }


    public class AgencyService : IAgencyService
    {
        private readonly IRepository<Agency> _agencyRepository;
        private readonly IRepository<Location> _locationRepository;

        public AgencyService(IRepository<Agency> agencyRepository, IRepository<Location> locationRepository)
        {
            _agencyRepository = agencyRepository;
            _locationRepository = locationRepository;
        }

        #region SEARCH
        public async Task<SmartTableResult<AgencyItem>> Search(SmartTableParam param)
        {
            var query = _agencyRepository.Query();
            if (param.Search.PredicateObject != null)
            {
                dynamic search = param.Search.PredicateObject;
                if (search.Keyword != null)
                {
                    string keyword = search.Keyword;
                    keyword = keyword.Trim().ToLower();
                    query = query.Where(x => x.Name.ToLower().Contains(keyword)
                                    || x.Description.ToLower().Contains(keyword));
                }

                if (search.CreatedStart != null && search.CreatedEnd != null)
                {
                    DateTimeOffset start = search.CreatedStart;
                    DateTimeOffset end = search.CreatedEnd;
                    DateTimeOffset startParam = start.StartOfDay();
                    DateTimeOffset endParam = end.EndOfDay();
                    query = query.Where(x => x.CreatedTime >= startParam && x.CreatedTime <= endParam);
                }

                if (search.ParentId != null)
                {
                    int parentId = search.ParentId;
                    query = query.Where(x => x.ParentId == parentId);
                }

                if (search.Status != null)
                {
                    AgencyStatus status = search.Status;
                    query = query.Where(x => x.Status == status);
                }

                if (search.AgencyType != null)
                {
                    AgencyType type = search.AgencyType;
                    query = query.Where(x => x.AgencyType == type);
                }

            }
            param.Sort = new Sort() { Predicate = "DisplayOrder", Reverse = false };
            var gridData = await query.ToSmartTableResultAsync(param, x => x.ToItem());
            if (gridData.Items.Any())
            {
                var cityIds = gridData.Items.Select(i => i.CityId).ToList();
                var districtIds = gridData.Items.Select(i => i.DistrictId).ToList();
                var wardIds = gridData.Items.Select(i => i.WardId).ToList();
                var locationIds = cityIds.Concat(districtIds).Concat(wardIds).Distinct().ToList();
                var locations = _locationRepository.Query().Where(l => locationIds.Contains(l.Id)).ToList();
                foreach (var item in gridData.Items)
                {
                    if (item.CityId > 0)
                    {
                        var city = locations.FirstOrDefault(c => c.Id == item.CityId);
                        if (city != null && city.Id > 0)
                        {
                            item.City = city.Name;
                        }
                    }
                    if (item.DistrictId > 0)
                    {
                        var district = locations.FirstOrDefault(c => c.Id == item.DistrictId);
                        if (district != null && district.Id > 0)
                        {
                            item.District = district.Name;
                        }
                    }
                    if (item.WardId > 0)
                    {
                        var ward = locations.FirstOrDefault(c => c.Id == item.WardId);
                        if (ward != null && ward.Id > 0)
                        {
                            item.Ward = ward.Name;
                        }
                    }
                }
            }
            return gridData;
        }

        public async Task<List<AgencyItem>> AutoComplete(string keyword)
        {
            List<AgencyItem> rs = new List<AgencyItem>();
            var agencies = await _agencyRepository.Query().Where(x => x.Name.Contains(keyword)
            || (x.CompanyPhone != null && x.CompanyPhone.Contains(keyword))
            || (x.Code != null && x.Code.Contains(keyword))
            || (x.FounderName != null && x.FounderName.Contains(keyword))
            ).Take(10).ToListAsync();
            rs = agencies.ToItem();
            return rs;
        }
        #endregion

        #region CRUD
        public async Task<AgencyResult> CreateOrUpdate(AgencyModel model)
        {
            var agency = model.ToEntity();

            //Cập nhật thông tin chung của thực thể
            agency = agency.UpdateCommonInt(model.UpdatedBy, model.UpdatedByUserName);

            if (agency.Id > 0)
            {
                //Cập nhật
                return await Update(agency);
            }
            else
            {
                //Thêm mới
                return await Create(agency);
            }
        }

        private async Task<AgencyResult> Create(Agency agency)
        {
            var result = new AgencyResult();
            try
            {
                await _agencyRepository.InsertAsync(agency);
                result.Id = agency.Id;

            }
            catch (Exception e)
            {
                result.Result = Result.SystemError;
                result.Message = e.ToString();
                result.Id = 0;
            }
            return result;
        }

        private async Task<AgencyResult> Update(Agency agency)
        {
            var result = new AgencyResult() { Result = Result.Success };
            var agencyForUpdate = await _agencyRepository.Query().FirstOrDefaultAsync(c => c.Id == agency.Id);
            if (agencyForUpdate == null || agency.Id <= 0)
            {
                result.Result = Result.Failed;
                result.Message = "Không tìm thấy hồ sơ yêu cầu!";
                return result;
            }

            try
            {
                agencyForUpdate.Name = agency.Name;
                agencyForUpdate.Description = agency.Description;
                agencyForUpdate.DisplayOrder = agency.DisplayOrder;
                agencyForUpdate.Address = agency.Address;
                agencyForUpdate.CityId = agency.CityId;
                agencyForUpdate.DistrictId = agency.DistrictId;
                agencyForUpdate.WardId = agency.WardId;
                agencyForUpdate.ParentId = agency.ParentId;
                agencyForUpdate.RepresentativeId = agency.RepresentativeId;
                agencyForUpdate.ManagerId = agency.ManagerId;
                agencyForUpdate.Code = agency.Code;
                agencyForUpdate.AgencyType = agency.AgencyType;
                //Cập nhật thông tin chung của thực thể
                agencyForUpdate = agencyForUpdate.UpdateCommonInt();

                await _agencyRepository.UpdateAsync(agencyForUpdate);
                result.Id = agencyForUpdate.Id;
            }
            catch (Exception e)
            {
                result.Result = Result.SystemError;
                result.Message = e.ToString();
            }

            return result;
        }

        public async Task<AgencyModel> GetById(int id)
        {
            var result = new AgencyModel();
            if (id <= 0)
            {
                return result;
            }
            var agency = await _agencyRepository.Query().FirstOrDefaultAsync(c => c.Id == id);
            if (agency == null || agency.Id <= 0)
            {
                return result;
            }
            var agencyModel = agency.ToModel();
            return agencyModel;
        }

        public async Task<BaseResult> Delete(int id, CurrentUser currentUser)
        {
            var result = new BaseResult() { Result = Result.Success };
            var agencyForUpdate = await _agencyRepository.Query().FirstOrDefaultAsync(c => c.Id == id);
            if (agencyForUpdate == null)
            {
                result.Result = Result.Failed;
                result.Message = "Không tìm thấy bản ghi yêu cầu";
                return result;
            }
            var childAgencys = await _agencyRepository.Query().CountAsync(l => l.ParentId == agencyForUpdate.Id);
            if (childAgencys > 0)
            {
                var msg = "";
                msg = "Vui lòng xóa đại lý con thuộc đại lý này trước";
                result.Result = Result.Failed;
                result.Message = msg;
                return result;
            }

            try
            {

                agencyForUpdate.IsDeleted = true;

                agencyForUpdate = agencyForUpdate.UpdateCommonInt(currentUser.UserId, currentUser.UserName);

                await _agencyRepository.UpdateAsync(agencyForUpdate);
            }
            catch (Exception e)
            {
                result.Result = Result.SystemError;
                result.Message = e.Message;
            }

            return result;
        }
        #endregion

        public async Task<BaseResult> UpdateStatus(AgencyStatusModel agencyStatusModel)
        {
            var result = new BaseResult() { Result = Result.Success };
            var agencyForUpdate = await _agencyRepository.Query().FirstOrDefaultAsync(c => c.Id == agencyStatusModel.Id);
            if (agencyForUpdate == null)
            {
                result.Result = Result.Failed;
                result.Message = "Không tìm thấy bản ghi yêu cầu";
                return result;
            }


            try
            {

                agencyForUpdate.Status = agencyStatusModel.NewStatus;

                agencyForUpdate = agencyForUpdate.UpdateCommonInt(agencyStatusModel.UpdatedBy, agencyStatusModel.UpdatedByUserName);

                await _agencyRepository.UpdateAsync(agencyForUpdate);
            }
            catch (Exception e)
            {
                result.Result = Result.SystemError;
                result.Message = e.Message;
            }
            return result;
        }


        public async Task<List<AgencyTreeItem>> GetTree(string keyword)
        {
            var result = new List<AgencyTreeItem>();
            var menuList = new List<Agency>();
            var query = _agencyRepository.Query().Where(a => a.Status == AgencyStatus.Active);

            if (!string.IsNullOrEmpty(keyword))
            {
                keyword = keyword.Trim();
                menuList = await query.Where(t => t.Name.Contains(keyword)).ToListAsync();
            }
            else
            {
                menuList = await query.ToListAsync();
            }

            var rootItems = menuList.Where(c => c.ParentId == 0).OrderBy(c => c.DisplayOrder).ToList();
            foreach (var item in rootItems)
            {
                //result.Add(new CategoryTreeItem() { Id=item.Id,Name= });
                result.AddRange(GetChilds(item, menuList, 0));
            }
            return result;

        }

        private List<AgencyTreeItem> GetChilds(Agency agency, List<Agency> agencies, int level)
        {
            var result = new List<AgencyTreeItem>();
            var mainItem = new AgencyTreeItem() { Id = agency.Id, Label = agency.Name };
            if (level > 0)
            {
                string prefix = "";
                for (int i = 0; i < level; i++)
                {
                    prefix += "|---";
                }
                mainItem.Label = prefix + mainItem.Label;
            }
            result.Add(mainItem);
            var childs = agencies.Where(c => c.ParentId == agency.Id).OrderBy(m => m.DisplayOrder);
            if (childs.Any())
            {
                level += 1;
                foreach (var item in childs)
                {
                    result.AddRange(GetChilds(item, agencies, level));
                }
            }
            return result;


        }

        public async Task<AgencyCoverageModel> GetCoverageModel(int agencyId)
        {
            var result = new AgencyCoverageModel();
            if (agencyId <= 0)
            {
                return result;
            }
            var agency = await _agencyRepository.Query().FirstOrDefaultAsync(c => c.Id == agencyId);
            if (agency == null || agency.Id <= 0)
            {
                return result;
            }
            result.AgencyId = agency.Id;

            result.Locations = await _locationRepository.Query().Where(l => l.AgencyId == agencyId).Select(l=>l.ToItem()).ToListAsync();

            return result;
        }
    }
}
