using Lada.Framework.Data.Domains.Categories;
using Lada.Framework.Data.Domains.Crm;
using Lada.Framework.Data.Domains.Finance.Quotations;
using Lada.Framework.Data.Enums;
using Lada.Framework.Data.Repositories;
using Lada.Framework.DTO.Models;
using Lada.Framework.DTO.Results;
using Microsoft.EntityFrameworkCore;

namespace Lada.Framework.Infrastructures.Services
{
    public interface IFeeService
    {
        /// <summary>
        /// Lấy giá theo phường
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        Task<FeeResult> GetFeeByWard(FeeByWardModel model,string apiKey);

        /// <summary>
        /// Lấy giá theo khoảng cách
        /// </summary>
        /// <param name="model"></param>
        /// <returns></returns>
        Task<FeeResult> GetFee(FeeModel model);
    }


    public class FeeService : IFeeService
    {
        private readonly IRepository<Price> _priceRepository;
        private readonly IRepository<Route> _routeRepository;
        private readonly IRepository<Customer> _customerRepository;
        private readonly IRepository<Location> _locationRepository;
        private readonly IRepository<EndPoints> _endPointsRepository;
        private readonly IGoogleMapService _googleMapService;

        public FeeService(IRepository<Price> priceRepository, IRepository<Route> routeRepository,
            IRepository<Customer> customerRepository, IRepository<Location> locationRepository,
            IRepository<EndPoints> endPointsRepository, IGoogleMapService googleMapService)
        {
            _priceRepository = priceRepository;
            _routeRepository = routeRepository;
            _customerRepository = customerRepository;
            _locationRepository = locationRepository;
            _endPointsRepository = endPointsRepository;
            _googleMapService = googleMapService;
        }

        public async Task<FeeResult> GetFeeByWard(FeeByWardModel model, string apiKey)
        {
            var result = new FeeResult();
            if (model.SourceWardId <= 0 || model.DestWardId <= 0)
            {
                result.Result = DTO.Result.Failed;
                result.Message = "Thông tin địa chỉ phải đầy đủ cả điểm gửi và điểm nhận";
                return result;
            }

            var wardIds = new List<int> { model.SourceWardId, model.DestWardId };
            var wards = await _locationRepository.Query().Where(l => wardIds.Contains(l.Id)).ToListAsync();
            if (wards.Any())
            {
                var sourceWard = wards.FirstOrDefault(w => w.Id == model.SourceWardId);
                var destWard = wards.FirstOrDefault(w => w.Id == model.DestWardId);
                if (sourceWard == null || sourceWard.Id == 0 || destWard == null || destWard.Id == 0)
                {
                    result.Result = DTO.Result.Failed;
                    result.Message = "Thông tin địa chỉ không hợp lệ";
                    return result;
                }


                int distance = 0;

                var endPoints = await _endPointsRepository.Query().
                    FirstOrDefaultAsync(e => e.StartId == sourceWard.Id && e.EndId == destWard.Id);
                if (endPoints == null || endPoints.Id <= 0 || endPoints.Distance <= 0)
                {
                    //Nếu không xác định được khoảng cách thì cập nhật khoảng cách cho nó
                    distance = await _googleMapService.UpdateEndPointsDistance(sourceWard.Id, destWard.Id, apiKey);
                    if (distance == 0)
                    {
                        result.Result = DTO.Result.Failed;
                        result.Message = "Không xác định được khoảng cách";
                        return result;
                    }

                }
                else
                {
                    distance = endPoints.Distance;
                }
                var feeModel = new FeeModel()
                {
                    CustomerId = model.CustomerId,
                    Weight = model.Weight,
                    Distance = distance
                };
                result = await GetFee(feeModel);

            }
            else
            {
                result.Result = DTO.Result.Failed;
                result.Message = "Không tìm thấy địa điểm yêu cầu";
                return result;
            }
            return result;
        }


        public async Task<FeeResult> GetFee(FeeModel model)
        {
            var result = new FeeResult();
            //Lấy ra tuyến của đơn hàng
            var route = await _routeRepository.Query().FirstOrDefaultAsync(r => model.Distance > r.DistanceMin && model.Distance <= r.DistanceMax);

            #region Lấy ra thông tin khách hàng
            var customerId = await GetFeeCustomerId(model.CustomerId);
            if (customerId == 0)
            {
                result.Result = DTO.Result.Failed;
                result.Message = $"Thông tin khách hàng không hợp lệ";
                return result;
            }
            #endregion


            if (route != null && route.Id > 0)
            {
                result = await GetFee(model, route.Id, customerId);
                result.RouteName = route.Name;
            }
            else
            {
                result.Result = DTO.Result.Failed;
                result.Message = $"Không tìm thấy tuyến phù hợp với khoảng cách {model.Distance / 1000}km yêu cầu";
            }
            return result;
        }


        private async Task<FeeResult> GetFee(FeeModel model, int routeId, int customerId)
        {
            var result = new FeeResult();

            //Lấy ra tuyến của đơn hàng
            var priceListByRoute = await _priceRepository.Query().Where(p => p.CustomerId == customerId && p.RouteId == routeId).ToListAsync();
            if (priceListByRoute.Any())
            {
                var price = priceListByRoute.FirstOrDefault(p => model.Weight >= p.MinWeight && model.Weight <= p.MaxWeight);
                if (price != null && price.Id > 0)
                {
                    switch (price.PriceType)
                    {
                        case PriceType.FixPrice:
                            result.Fee = price.Value;
                            break;
                        case PriceType.RangeStepPrice:
                            //Lấy ra fixprice lớn nhất của tuyến này
                            var fixedPrice = priceListByRoute.Where(p => p.PriceType == PriceType.FixPrice).OrderByDescending(p => p.Value).FirstOrDefault();
                            if (fixedPrice != null && fixedPrice.Id > 0)
                            {
                                var maxFixFee = fixedPrice.Value;
                                result.Fee = Math.Ceiling((model.Weight - fixedPrice.MaxWeight) / price.StepWeight) * price.Value + maxFixFee;
                            }
                            else
                            {
                                result.Result = DTO.Result.Failed;
                                result.Message = $"Giá khoảng nhưng giá fix không có";
                            }
                            //result.Fee = 
                            break;
                        default:
                            result.Result = DTO.Result.Failed;
                            result.Message = $"Trọng lượng {model.Weight / 1000}kg không hợp lệ";
                            break;
                    }
                }
                else
                {
                    result.Result = DTO.Result.Failed;
                    result.Message = $"Trọng lượng {model.Weight / 1000}kg không hợp lệ";
                }


            }
            else
            {
                result.Result = DTO.Result.Failed;
                result.Message = $"Tuyến chưa có cấu hình bảng giá";
            }
            return result;
        }


        private async Task<int> GetFeeCustomerId(int customerId)
        {
            var priceCustomerId = customerId;
            //Nếu customerId truyền vào là 0 thì lấy ra khách hàng mặc định
            if (customerId == 0)
            {
                var customer = await _customerRepository.Query().FirstOrDefaultAsync(c => c.IsDefaultCustomer);
                if (customer != null && customer.Id > 0)
                {
                    priceCustomerId = customer.Id;
                }
                else
                {
                    priceCustomerId = 0;
                }

            }
            else
            {
                var customer = await _customerRepository.Query().FirstOrDefaultAsync(c => c.Id == customerId);
                if (customer != null && customer.Id > 0)
                {
                    //Nếu sử dụng bảng giá riêng thì truyền id khách hàng này vào
                    if (customer.UseCustomPriceList)
                    {
                        priceCustomerId = customer.Id;
                    }
                    else
                    {
                        //Ngược lại thì lấy ra Id của khách hàng mặc định
                        var defaultCustomer = await _customerRepository.Query().FirstOrDefaultAsync(c => c.IsDefaultCustomer);
                        if (defaultCustomer != null && defaultCustomer.Id > 0)
                        {
                            priceCustomerId = defaultCustomer.Id;
                        }
                        else
                        {
                            priceCustomerId = 0;

                        }
                    }
                }
                else
                {
                    priceCustomerId = 0;

                }
            }
            return priceCustomerId;
        }
    }
}
