using Lada.Framework.Data.Domains;
using Lada.Framework.Data.Domains.Categories;
using Lada.Framework.Data.Domains.GoogleMap.PlaceAutocomplete;
using Lada.Framework.Data.Repositories;
using Lada.Framework.DTO;
using Lada.Framework.DTO.Results.GoogleMap;
using Lada.Framework.DTO.Results.GoogleMap.PlaceAutocomplete;
using Lada.Framework.DTO.SmartTable;
using Lada.Framework.Infrastructures;
using Lada.Framework.Infrastructures.Helpers;
using Lada.Framework.Infrastructures.Services;
using Ladaer.BackEnd.Infrastructures.AppSettings;
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;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using static IdentityServer4.Models.IdentityResources;

namespace Ladaer.BackEnd.Infrastructures.Services.Categories
{
    /// <summary>
    /// Dịch vụ xử lý các thao tác liên quan đến địa điểm
    /// </summary>
    public interface ILocationService
    {
        /// <summary>
        /// Tìm kiếm địa điểm theo tham số SmartTable
        /// </summary>
        /// <param name="param">Tham số tìm kiếm</param>
        /// <returns>Kết quả tìm kiếm</returns>
        Task<SmartTableResult<LocationItem>> Search(SmartTableParam param);

        /// <summary>
        /// Tạo mới hoặc cập nhật địa điểm
        /// </summary>
        /// <param name="model">Thông tin địa điểm</param>
        /// <returns>Kết quả tạo mới hoặc cập nhật</returns>
        Task<LocationResult> CreateOrUpdate(LocationModel model);

        /// <summary>
        /// Lấy danh sách gợi ý địa điểm theo từ khóa
        /// </summary>
        /// <param name="keyword">Từ khóa tìm kiếm</param>
        /// <param name="sugType">Loại gợi ý</param>
        /// <param name="parentId">ID cha</param>
        /// <returns>Danh sách gợi ý</returns>
        Task<List<SugLocationItem>> GetSugLocations(string keyword, SuggestionType sugType, int parentId);

        /// <summary>
        /// Lấy danh sách gợi ý địa điểm theo ID
        /// </summary>
        /// <param name="sugType">Loại gợi ý</param>
        /// <param name="id">ID địa điểm</param>
        /// <returns>Danh sách gợi ý</returns>
        Task<List<SugLocationItem>> GetSugLocationById(SuggestionType sugType, int id);

        /// <summary>
        /// Lấy thông tin địa điểm theo ID
        /// </summary>
        /// <param name="id">ID địa điểm</param>
        /// <returns>Thông tin địa điểm</returns>
        Task<LocationModel> GetById(int id);

        /// <summary>
        /// Lấy danh sách địa điểm theo danh sách ID
        /// </summary>
        /// <param name="ids">Danh sách ID</param>
        /// <returns>Danh sách địa điểm</returns>
        Task<List<LocationItem>> GetByIds(List<int> ids);

        /// <summary>
        /// Xóa địa điểm theo ID
        /// </summary>
        /// <param name="id">ID địa điểm</param>
        /// <param name="currentUser">Người dùng hiện tại</param>
        /// <returns>Kết quả xóa</returns>
        Task<BaseResult> Delete(int id, CurrentUser currentUser);

        /// <summary>
        /// Lấy địa chỉ đầy đủ theo ID và cấp độ
        /// </summary>
        /// <param name="id">ID địa điểm</param>
        /// <param name="level">Cấp độ địa điểm</param>
        /// <returns>Địa chỉ đầy đủ</returns>
        Task<string> GetFullAddress(int id, int level);

        /// <summary>
        /// Lấy danh sách địa chỉ đầy đủ theo danh sách ID và cấp độ
        /// </summary>
        /// <param name="ids">Danh sách ID</param>
        /// <param name="level">Cấp độ địa điểm</param>
        /// <returns>Danh sách địa chỉ đầy đủ</returns>
        Task<List<FullAddressItem>> GetFullAddress(List<int> ids, int level);

        /// <summary>
        /// Cập nhật tọa độ latlong cho các phường/xã chưa có
        /// </summary>
        /// <returns></returns>
        Task UpdateWardLatLong();

        /// <summary>
        /// Cập nhật khoảng cách và văn bản khoảng cách của EndPoints
        /// </summary>
        /// <returns></returns>
        Task UpdateEndPointsDistance();

        /// <summary>
        /// Cập nhật tên kết hợp
        /// </summary>
        /// <returns></returns>
        Task UpdateCombinedName();

        /// <summary>
        /// Tìm kiếm địa điểm tự động hoàn thành
        /// </summary>
        /// <param name="keyword">Từ khóa tìm kiếm</param>
        /// <returns>Kết quả tự động hoàn thành</returns>
        Task<PlaceAutocompleteResult> PlaceAutocomplete(string keyword);

        /// <summary>
        /// Lấy chi tiết địa điểm theo Place ID
        /// </summary>
        /// <param name="placeId">Place ID</param>
        /// <returns>Chi tiết địa điểm</returns>
        Task<PlaceDetailResult> PlaceDetail(string placeId);
    }

    public class LocationService : ILocationService
    {
        private readonly IRepository<Location> _locationRepository;
        private readonly IRepository<EndPoints> _endPointsRepository;
        private readonly IGoogleMapService _googleMapService;

        private readonly IRepository<PlacePrediction> _placePredictionRepository;



        private readonly ILogger<LocationService> _logger;
        private readonly string _apiKey;
        private const string BaseUrl = "https://maps.googleapis.com/maps/api/directions/json";
        private const string GeocodingBaseUrl = "https://maps.googleapis.com/maps/api/geocode/json";
        private readonly HttpClient _httpClient;

        public LocationService(IOptions<AppSetting> options, HttpClient httpClient, IRepository<Location> locationRepository,
            ILogger<LocationService> logger, IGoogleMapService googleMapService, IRepository<PlacePrediction> placePredictionRepository,
            IRepository<EndPoints> endPointsRepository)
        {
            _locationRepository = locationRepository;
            _logger = logger;
            _googleMapService = googleMapService;
            _apiKey = options.Value.GoogleMapsApi.ApiKey;
            _httpClient = httpClient;
            _endPointsRepository = endPointsRepository;
            _placePredictionRepository = placePredictionRepository;
        }

        public async Task<SmartTableResult<LocationItem>> Search(SmartTableParam param)
        {
            var query = _locationRepository.Query();
            int level = 1;
            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.Level != null)
                {
                    level = search.Level;
                    query = query.Where(x => x.Level == level);

                    if (level == 2 && search.CityId != null)
                    {
                        int parentId = search.CityId;
                        if (parentId > 0)
                        {
                            query = query.Where(x => x.ParentId == parentId);

                        }

                    }
                    if (level == 3 && (search.CityId != null || search.DistrictId != null))
                    {
                        int cityId = 0;
                        int districtId = 0;
                        if (search.CityId != null)
                        {
                            cityId = search.CityId;
                        }
                        if (search.DistrictId != null)
                        {
                            districtId = search.DistrictId;
                        }


                        if (cityId > 0 && districtId == 0)
                        {
                            var districtIds = _locationRepository.Query().Where(l => l.ParentId == cityId).Select(l => l.Id).ToList();
                            query = query.Where(x => districtIds.Contains(x.ParentId));
                        }
                        if (districtId > 0)
                        {
                            int parentId = search.DistrictId;
                            if (parentId > 0)
                            {
                                query = query.Where(x => x.ParentId == parentId);

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

            }
            param.Sort = new Sort() { Predicate = "DisplayOrder", Reverse = false };
            var gridData = await query.ToSmartTableResultAsync(param, x => x.ToItem());

            if (gridData.Items.Count() > 0)
            {

                if (level == 2)
                {
                    //Lấy ra tên tỉnh / thành phố
                    var cityIds = gridData.Items.Select(d => d.ParentId).Distinct().ToList();
                    if (cityIds.Any())
                    {
                        var cities = _locationRepository.Query().Where(c => cityIds.Contains(c.Id)).ToList();
                        foreach (var item in gridData.Items)
                        {
                            var city = cities.FirstOrDefault(c => c.Id == item.ParentId);
                            if (city != null && city.Id > 0)
                            {
                                item.City = city.Name;
                            }

                        }
                    }
                }

                if (level == 3)
                {
                    //Lấy ra tên tỉnh / thành phố
                    var districtIds = gridData.Items.Select(w => w.ParentId).Distinct().ToList();
                    if (districtIds.Any())
                    {
                        var districts = _locationRepository.Query().Where(c => districtIds.Contains(c.Id)).ToList();
                        var cityIds = districts.Select(d => d.ParentId).Distinct().ToList();
                        var cities = _locationRepository.Query().Where(c => cityIds.Contains(c.Id)).ToList();
                        foreach (var item in gridData.Items)
                        {
                            var district = districts.FirstOrDefault(w => w.Id == item.ParentId);
                            if (district != null && district.Id > 0)
                            {
                                var city = cities.FirstOrDefault(w => w.Id == district.ParentId);
                                item.District = district.Name;
                                item.City = city.Name;
                            }
                        }
                    }
                }


            }

            return gridData;
        }

        #region CRUD
        public async Task<LocationResult> CreateOrUpdate(LocationModel model)
        {
            var location = model.ToEntity();

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

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

        private async Task<LocationResult> Create(Location location)
        {
            var result = new LocationResult();
            try
            {
                await _locationRepository.InsertAsync(location);
                result.Id = location.Id;

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

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

            try
            {
                locationForUpdate.Name = location.Name;
                locationForUpdate.Description = location.Description;
                locationForUpdate.DisplayOrder = location.DisplayOrder;
                locationForUpdate.ParentId = location.ParentId;
                locationForUpdate.Prefix = location.Prefix;
                locationForUpdate.CombinedName = await GetCombinedName(locationForUpdate);
                locationForUpdate.UnsignCombinedName = !string.IsNullOrEmpty(locationForUpdate.CombinedName) ? locationForUpdate.CombinedName.Unsigned() : "";
                locationForUpdate.Type = location.Type;
                //Cập nhật thông tin chung của thực thể
                locationForUpdate = locationForUpdate.UpdateCommonInt();

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

            return result;
        }

        public async Task<LocationModel> GetById(int id)
        {
            var result = new LocationModel();
            if (id <= 0)
            {
                return result;
            }
            var location = await _locationRepository.Query().FirstOrDefaultAsync(c => c.Id == id);
            if (location == null || location.Id <= 0)
            {
                return result;
            }
            var locationModel = location.ToModel();
            if (location.Level == 2 && location.ParentId > 0)
            {
                var city = await _locationRepository.Query().FirstOrDefaultAsync(c => c.Id == location.ParentId);
                if (city != null && city.Id > 0)
                {
                    locationModel.ParentName = city.Name;
                }
            }
            if (location.Level == 3 && location.ParentId > 0)
            {
                var district = await _locationRepository.Query().FirstOrDefaultAsync(c => c.Id == location.ParentId);
                if (district != null && district.Id > 0)
                {
                    locationModel.ParentName = district.Name;
                    if (district.ParentId > 0)
                    {
                        var city = await _locationRepository.Query().FirstOrDefaultAsync(c => c.Id == district.ParentId);
                        if (city != null && city.Id > 0)
                        {
                            locationModel.CityName = city.Name;
                            locationModel.CityId = city.Id;
                        }
                    }

                }
            }

            return locationModel;
        }

        public async Task<List<LocationItem>> GetByIds(List<int> ids)
        {
            var result = new List<LocationItem>();
            if (!ids.Any())
            {
                return result;
            }
            return await _locationRepository.Query().Where(c => ids.Contains(c.Id)).Select(c => c.ToItem()).ToListAsync();


        }


        public async Task<BaseResult> Delete(int id, CurrentUser currentUser)
        {
            var result = new BaseResult() { Result = Result.Success };
            var locationForUpdate = await _locationRepository.Query().FirstOrDefaultAsync(c => c.Id == id);
            if (locationForUpdate == null)
            {
                result.Result = Result.Failed;
                result.Message = "Không tìm thấy bản ghi yêu cầu";
                return result;
            }
            var childLocations = await _locationRepository.Query().CountAsync(l => l.ParentId == locationForUpdate.Id);
            if (childLocations > 0)
            {
                var msg = "";
                if (locationForUpdate.Level == 1)
                {
                    msg = "Vui lòng xóa quận/huyện thuộc tỉnh/thành phố này trước";
                }
                else if (locationForUpdate.Level == 2)
                {
                    msg = "Vui lòng xóa phường/xã thuộc quận/huyện này trước";
                }
                result.Result = Result.Failed;
                result.Message = msg;
                return result;
            }

            try
            {

                locationForUpdate.IsDeleted = true;

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

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

            return result;
        }

        /// <summary>
        /// Lấy danh sách gợi ý địa điểm theo từ khóa
        /// </summary>
        /// <param name="keyword">Từ khóa tìm kiếm</param>
        /// <param name="sugType">Loại gợi ý</param>
        /// <param name="parentId">ID cha</param>
        /// <returns>Danh sách gợi ý</returns>
        public async Task<List<SugLocationItem>> GetSugLocations(string keyword, SuggestionType sugType, int parentId)
        {
            var result = new List<SugLocationItem>();
            result = await GetSugLocation(keyword, sugType, parentId);

            return result;
        }

        /// <summary>
        /// Lấy danh sách gợi ý địa điểm theo ID
        /// </summary>
        /// <param name="sugType">Loại gợi ý</param>
        /// <param name="id">ID địa điểm</param>
        /// <returns>Danh sách gợi ý</returns>
        public async Task<List<SugLocationItem>> GetSugLocationById(SuggestionType sugType, int id)
        {
            return await GetSugById(id);
        }
        #endregion

        #region Suggestions


        private async Task<List<SugLocationItem>> GetSugLocation(string keyword, SuggestionType sugType, int parentId = 0)
        {
            int level = 0;
            switch (sugType)
            {
                case SuggestionType.District:
                    level = 2;
                    break;
                case SuggestionType.Ward:
                    level = 3;
                    break;
                case SuggestionType.City:
                    level = 1;
                    break;
                default:
                    return new List<SugLocationItem>();
            }
            var query = _locationRepository.Query().Where(l => l.Level == level && (l.Type == LocationType.Urban || l.Type == LocationType.SubUrban));
            if (!string.IsNullOrEmpty(keyword))
            {
                keyword = keyword.Trim().ToLower();
                var unsignKeyword = keyword.Unsigned(); //Tên không dấu


                query = query.Where(x => (x.Name.ToLower().Contains(keyword)));

            }
            if (parentId > 0)
            {
                query = query.Where(x => x.ParentId == parentId);
            }
            var result = await query.Take(50).Select(x => x.ToSugItem()).ToListAsync();
            return result;
        }

        private async Task<List<SugLocationItem>> GetSugById(int id)
        {
            if (id <= 0)
            {
                return new List<SugLocationItem>();
            }
            var query = _locationRepository.Query().Where(l => l.Id == id);
            var result = await query.Take(25).Select(x => x.ToSugItem()).ToListAsync();
            return result;
        }

        #endregion



        public async Task<PlaceAutocompleteResult> PlaceAutocomplete(string keyword)
        {
            //var options = $"&types=street_address|neighborhood|establishment|point_of_interest";
            //var options = $"&types=street_address|neighborhood|establishment|point_of_interest";

            return await _googleMapService.PlaceAutocomplete(keyword, _apiKey);
        }

        public async Task<PlaceDetailResult> PlaceDetail(string placeId)
        {
            //var options = $"&types=street_address|neighborhood|establishment|point_of_interest";
            //var options = $"&types=street_address|neighborhood|establishment|point_of_interest";

            //return await _googleMapService.PlaceDetail(placeId, _apiKey);

            var result = new PlaceDetailResult();
            var place = await _placePredictionRepository.Query().FirstOrDefaultAsync(p => p.PlaceId == placeId);
            if (place != null)
            {
                if (place.CityId == 0 && place.DistrictId == 0 && place.WardId == 0)
                {
                    result = await PlaceDetailFromGoogle(placeId, _apiKey);
                }
                else
                {
                    result.CityId = place.CityId;
                    result.DistrictId = place.DistrictId;
                    result.WardId = place.WardId;
                    if (result.CityId > 0)
                    {
                        var city = await _locationRepository.Query().FirstOrDefaultAsync(c => c.Id == result.CityId);
                        if (city != null && city.Id > 0)
                        {
                            result.City = city.Name;
                        }
                    }
                    if (result.DistrictId > 0)
                    {
                        var district = await _locationRepository.Query().FirstOrDefaultAsync(c => c.Id == result.DistrictId);
                        if (district != null && district.Id > 0)
                        {
                            result.District = district.CombinedName;
                        }
                    }
                    if (result.WardId > 0)
                    {
                        var ward = await _locationRepository.Query().FirstOrDefaultAsync(c => c.Id == result.WardId);
                        if (ward != null && ward.Id > 0)
                        {
                            result.Ward = ward.Name;
                        }
                    }
                }
            }
            else
            {
                result = await PlaceDetailFromGoogle(placeId, _apiKey);
            }



            return result;
        }


        public async Task<PlaceDetailResult> PlaceDetailFromGoogle(string placeId, string apiKey)
        {
            var result = new PlaceDetailResult();
            string geocodeUrl = $"https://maps.googleapis.com/maps/api/geocode/json?place_id={placeId}&key={apiKey}";
            try
            {
                var response = await _httpClient.GetStringAsync(geocodeUrl);
                var data = JObject.Parse(response);

                if (data["status"].ToString() == "OK")
                {
                    var addressComponents = data["results"][0]["address_components"];


                    foreach (var component in addressComponents)
                    {
                        var types = component["types"];
                        if (types.Any(t => t.ToString() == "administrative_area_level_1"))
                        {
                            result.City = component["long_name"].ToString();
                        }
                        else if (types.Any(t => t.ToString() == "administrative_area_level_2"))
                        {
                            result.District = component["long_name"].ToString();
                        }
                        else if (types.Any(t => t.ToString() == "sublocality_level_1"))
                        {
                            result.Ward = component["long_name"].ToString();
                        }
                    }

                    if (!string.IsNullOrEmpty(result.City))
                    {
                        var unsignCity = result.City.Unsigned().Trim();
                        var city = await _locationRepository.Query().FirstOrDefaultAsync(c => c.Level == 1 && (c.Name == result.City || c.UnsignCombinedName == unsignCity));

                        if (city != null && city.Id > 0 && !string.IsNullOrEmpty(result.District))
                        {
                            result.CityId = city.Id;
                            var district = _locationRepository.Query().FirstOrDefaultAsync(d => d.ParentId == city.Id && (d.Name == result.District));
                            if (district != null && district.Id > 0 && !string.IsNullOrEmpty(result.Ward))
                            {
                                result.DistrictId = district.Id;
                                var ward = _locationRepository.Query().FirstOrDefaultAsync(d => d.ParentId == district.Id && (d.Name == result.Ward));
                                if (ward != null && ward.Id > 0)
                                {
                                    result.WardId = ward.Id;

                                }
                            }
                        }

                    }
                }
                else
                {
                    result.Result = Result.Failed;
                    result.Message = "Không lấy được dữ liệu";

                }
            }
            catch (Exception ex)
            {
                result.Result = Result.SystemError;
                result.Message = ex.Message;

            }

            return result;
        }

        #region Hangfire Jobs
        public async Task UpdateWardLatLong()
        {
            var wards = _locationRepository.Query().Where(l =>
            l.Level == 3 &&
            l.Lat == 0 && l.Long == 0 &&
            (l.Type == LocationType.Urban || l.Type == LocationType.SubUrban)).Take(500).ToList();
            if (wards.Any())
            {
                var districtIds = wards.Select(w => w.ParentId).ToList();
                var districts = _locationRepository.Query().Where(d => districtIds.Contains(d.Id)).ToList();

                var cityIds = districts.Select(d => d.ParentId).ToList();
                var cities = _locationRepository.Query().Where(c => cityIds.Contains(c.Id)).ToList();


                foreach (var ward in wards)
                {
                    var district = districts.FirstOrDefault(d => d.Id == ward.ParentId);
                    if (district == null || district.Id <= 0)
                    {
                        continue;
                    }
                    var city = cities.FirstOrDefault(c => c.Id == district.ParentId);
                    if (city == null || city.Id <= 0)
                    {
                        continue;
                    }
                    if (ward.Id > 0 && district.Id > 0 && city.Id > 0)
                    {
                        var address = $"{ward.Name},{district.Name},{city.Name}";
                        var latlongResult = await GetLatLngFromAddress(address);
                        if (latlongResult.Result == Result.Success)
                        {
                            ward.Lat = latlongResult.Latitude;
                            ward.Long = latlongResult.Longitude;
                            await UpdateWardLatLong(ward);
                        }

                    }
                }
            }

        }

        public async Task UpdateEndPointsDistance()
        {
            await _googleMapService.UpdateEndPointsDistance(_apiKey);
        }


        public async Task UpdateCombinedName()
        {
            var locations = _locationRepository.Query().Where(l => (string.IsNullOrEmpty(l.CombinedName) || string.IsNullOrEmpty(l.UnsignCombinedName)) &&
            (l.Type == LocationType.Urban || l.Type == LocationType.SubUrban)).Take(500).ToList();
            if (locations.Any())
            {
                foreach (var location in locations)
                {
                    var locationForUpdate = location;
                    locationForUpdate.CombinedName = await GetCombinedName(locationForUpdate);
                    locationForUpdate.UnsignCombinedName = !string.IsNullOrEmpty(locationForUpdate.CombinedName) ? locationForUpdate.CombinedName.Unsigned() : "";
                    locationForUpdate = locationForUpdate.UpdateCommonInt();
                    await _locationRepository.UpdateAsync(locationForUpdate);

                }
            }




        }
        #endregion

        #region Utilities
        private async Task<string> GetCombinedName(Location location)
        {
            var result = "";
            if (location != null && location.Id > 0)
            {
                switch (location.Level)
                {
                    //nếu là tỉnh/thành phố hoặc phường/xã thì lấy ra quận/huyện và tỉnh/thành phố cha
                    case 1:
                    case 3:
                        result = $"{location.Prefix} {location.Name}";
                        break;
                    case 2:
                        //nếu là quận / huyện thì lấy ra tỉnh / thành phố cha
                        if (location.ParentId > 0)
                        {
                            var city = await _locationRepository.Query().FirstOrDefaultAsync(c => c.Id == location.ParentId);
                            if (city != null && city.Id > 0)
                            {
                                result = $"{location.Prefix} {location.Name} - {city.Prefix} {city.Name}";
                            }
                        }
                        break;

                    default:
                        break;
                }
            }




            return result;
        }


        private async Task<LatLongResult> GetLatLngFromAddress(string address)
        {
            var result = new LatLongResult();
            var url = $"{GeocodingBaseUrl}?address={Uri.EscapeDataString(address)}&key={_apiKey}";

            var response = await _httpClient.GetAsync(url);

            if (!response.IsSuccessStatusCode)
            {
                result.Result = Lada.Framework.DTO.Result.Failed;
                result.Message = $"Failed to get latlng from Google Maps Geocoding API: {response.ReasonPhrase}";
                return result;
            }

            var content = await response.Content.ReadAsStringAsync();
            var responseJson = JsonConvert.DeserializeObject<GoogleMapsGeocodingResponse>(content);

            if (responseJson.Status != "OK" || responseJson.Results.Length == 0)
            {
                result.Result = Lada.Framework.DTO.Result.Failed;
                result.Message = $"Failed to get latlng from Google Maps Geocoding API.";
                return result;

            }

            var location = responseJson.Results[0].Geometry.Location;
            result.Latitude = location.Lat;
            result.Longitude = location.Lng;
            return result;
        }

        private async Task UpdateWardLatLong(Location ward)
        {
            var wardForUpdate = _locationRepository.Query().FirstOrDefault(w => ward.Id == w.Id);
            if (wardForUpdate != null && wardForUpdate.Id > 0)
            {
                wardForUpdate.Lat = ward.Lat;
                wardForUpdate.Long = ward.Long;
                wardForUpdate = wardForUpdate.UpdateCommonInt();
                await _locationRepository.UpdateAsync(ward);
            }
        }

        private async Task<DistanceResult> GetDistance(string origin, string destination)
        {
            var result = new DistanceResult();

            var url = $"{BaseUrl}?origin={origin}&destination={destination}&key={_apiKey}";

            var response = await _httpClient.GetAsync(url);

            if (!response.IsSuccessStatusCode)
            {
                result.Result = Result.Failed;
                result.Message = $"Failed to get distance from Google Maps API: {response.ReasonPhrase}";
                return result;
            }

            var content = await response.Content.ReadAsStringAsync();
            var responseJson = JsonConvert.DeserializeObject<GoogleMapsApiResponse>(content);

            if (responseJson.Status != "OK")
            {
                result.Result = Result.Failed;
                result.Message = $"Failed to get distance from Google Maps API: {responseJson.Status}";
                return result;

            }

            result.Distance = responseJson.Routes[0].Legs[0].Distance.Value;
            result.DistaceText = responseJson.Routes[0].Legs[0].Distance.Text;

            return result;
        }

        private async Task UpdateEndPointsDistance(EndPoints endPoints)
        {
            var endPointsForUpdate = _endPointsRepository.Query().FirstOrDefault(w => endPoints.Id == w.Id);
            if (endPointsForUpdate != null && endPointsForUpdate.Id > 0)
            {
                endPointsForUpdate.Distance = endPoints.Distance;
                endPointsForUpdate.DistanceText = endPoints.DistanceText;
                endPointsForUpdate = endPointsForUpdate.UpdateCommonInt();
                await _endPointsRepository.UpdateAsync(endPoints);
            }
        }


        #endregion


        public async Task<string> GetFullAddress(int id, int level)
        {
            string address = "";
            var city = new Location();
            var district = new Location();
            var ward = new Location();
            switch (level)
            {
                case 1:
                    city = await _locationRepository.Query().FirstOrDefaultAsync(l => l.Id == id);
                    address += city != null ? city.Name : "";
                    break;
                case 2:
                    district = await _locationRepository.Query().FirstOrDefaultAsync(l => l.Id == id);
                    if (district != null)
                    {
                        address = $"{district.Name}";
                        if (district.ParentId > 0)
                        {
                            city = await _locationRepository.Query().FirstOrDefaultAsync(l => l.Id == district.ParentId);
                            if (city != null)
                            {
                                address = $"{district.Name}, {city.Name}";
                            }
                        }


                    }
                    break;
                case 3:
                    ward = await _locationRepository.Query().FirstOrDefaultAsync(l => l.Id == id);
                    if (ward != null)
                    {
                        address = $"{ward.Name}";
                        if (ward.ParentId > 0)
                        {
                            district = await _locationRepository.Query().FirstOrDefaultAsync(l => l.Id == ward.ParentId);
                            if (district != null)
                            {
                                address = $"{ward.Name}, {district.Name}";
                                if (district.ParentId > 0)
                                {
                                    city = await _locationRepository.Query().FirstOrDefaultAsync(l => l.Id == district.ParentId);
                                    if (city != null)
                                    {
                                        address = $"{ward.Name}, {district.Name}, {city.Name}";
                                    }
                                }
                            }
                        }


                    }
                    break;
                default:
                    break;
            }
            return address;
        }

        public async Task<List<FullAddressItem>> GetFullAddress(List<int> ids, int level)
        {
            var addresses = new List<FullAddressItem>();
            var locations = await _locationRepository.Query().Where(l => ids.Contains(l.Id)).ToListAsync();
            if (locations != null && locations.Any())
            {
                switch (level)
                {
                    //Thành phố
                    case 1:

                        foreach (var city in locations)
                        {
                            addresses.Add(new FullAddressItem() { FullAddress = city.Name, Id = city.Id, Level = 1 });
                        }
                        break;
                    //Quận / huyện
                    case 2:
                        var cities = new List<Location>();
                        var cityIds = locations.Select(d => d.ParentId).ToList();
                        if (cityIds != null && cityIds.Any())
                        {
                            cities = await _locationRepository.Query().Where(c => cityIds.Contains(c.Id)).ToListAsync();
                        }

                        foreach (var district in locations)
                        {
                            var address = new FullAddressItem() { Id = district.Id, Level = 2 };
                            address.FullAddress = $"{district.Name}";
                            if (district.ParentId > 0)
                            {
                                var city = cities.FirstOrDefault(c => c.Id == district.ParentId);
                                if (city != null)
                                {
                                    address.FullAddress = $"{district.Name}, {city.Name}";
                                }
                            }
                            addresses.Add(address);
                        }

                        break;
                    //Phường xã
                    case 3:
                        var wcities = new List<Location>();
                        var wdistricts = new List<Location>();
                        var districtIds = locations.Select(d => d.ParentId).ToList();
                        if (districtIds != null && districtIds.Any())
                        {
                            wdistricts = await _locationRepository.Query().Where(d => districtIds.Contains(d.Id)).ToListAsync();
                            if (wdistricts != null && wdistricts.Any())
                            {
                                var wcityIds = wdistricts.Select(d => d.ParentId).ToList();
                                if (wcityIds != null && wcityIds.Any())
                                {
                                    wcities = await _locationRepository.Query().Where(c => wcityIds.Contains(c.Id)).ToListAsync();
                                }
                            }
                        }
                        foreach (var ward in locations)
                        {
                            var address = new FullAddressItem() { Id = ward.Id, Level = 3 };
                            address.FullAddress = $"{ward.Name}";
                            if (ward.ParentId > 0 && wdistricts != null)
                            {
                                var wdistrict = wdistricts.FirstOrDefault(l => l.Id == ward.ParentId);
                                if (wdistrict != null)
                                {
                                    address.FullAddress = $"{ward.Name}, {wdistrict.Name}";
                                    if (wdistrict.ParentId > 0)
                                    {
                                        var wcity = wcities.FirstOrDefault(l => l.Id == wdistrict.ParentId);
                                        if (wcity != null)
                                        {
                                            address.FullAddress = $"{ward.Name}, {wdistrict.Name}, {wcity.Name}";
                                            address.CityName = wcity.Name;
                                        }
                                    }
                                }
                            }
                            addresses.Add(address);
                        }

                        break;
                    default:
                        break;
                }
            }

            return addresses;
        }
    }
}
