using Lada.Framework.Data.Domains;
using Lada.Framework.Data.Domains.Medias;
using Lada.Framework.Data.Domains.Premises;
using Lada.Framework.Data.Enums;
using Lada.Framework.Data.Repositories;
using Lada.Framework.DTO;
using Lada.Framework.DTO.SmartTable;
using Lada.Framework.Infrastructures.Helpers;
using Lada.Framework.Infrastructures.Services;
using Ladaer.BackEnd.Infrastructures.DTO.Items.Premises;
using Ladaer.BackEnd.Infrastructures.DTO.Models.Premises;
using Ladaer.BackEnd.Infrastructures.DTO.Profiles;
using Ladaer.BackEnd.Infrastructures.DTO.Results.Premises;
using Microsoft.EntityFrameworkCore;

namespace Ladaer.BackEnd.Infrastructures.Services.Premises
{
    public interface IPremiseService
    {
        Task<SmartTableResult<PremiseItem>> Search(SmartTableParam param);
        Task<BaseResult> CreateOrUpdate(PremiseModel model);
        Task<PremiseResult> GetById(int id);
        Task<BaseResult> Delete(int id);
        Task<BaseResult> ChangeStatus(PremiseStatusModel model);
        Task<BaseResult> UpdateFinancial(PremiseFinancialModel model);
        Task<BaseResult> DeleteMedia(int premiseMediaId);
    }

    public class PremiseService : IPremiseService
    {
        private readonly IRepository<Premise> _repository;
        private readonly IRepository<PremiseContact> _contactRepository;
        private readonly IRepository<PremiseScore> _scoreRepository;
        private readonly IRepository<PremiseFinancial> _financialRepository;
        private readonly IRepository<PremiseScoringOption> _scoringOptionRepository;
        private readonly IRepository<PremiseMedia> _premiseMediaRepository;
        private readonly IRepository<Media> _mediaRepository;
        private readonly IUnifiedMediaService _unifiedMediaService;

        public PremiseService(
            IRepository<Premise> repository,
            IRepository<PremiseContact> contactRepository,
            IRepository<PremiseScore> scoreRepository,
            IRepository<PremiseFinancial> financialRepository,
            IRepository<PremiseScoringOption> scoringOptionRepository,
            IRepository<PremiseMedia> premiseMediaRepository,
            IRepository<Media> mediaRepository,
            IUnifiedMediaService unifiedMediaService)
        {
            _repository = repository;
            _contactRepository = contactRepository;
            _scoreRepository = scoreRepository;
            _financialRepository = financialRepository;
            _scoringOptionRepository = scoringOptionRepository;
            _premiseMediaRepository = premiseMediaRepository;
            _mediaRepository = mediaRepository;
            _unifiedMediaService = unifiedMediaService;
        }

        public async Task<SmartTableResult<PremiseItem>> Search(SmartTableParam param)
        {
            var query = _repository.Query()
                .Include(x => x.Agency)
                .Include(x => x.City)
                .Include(x => x.District)
                .Include(x => x.Ward)
                .Where(x => !x.IsDeleted);

            // Filter by search
            if (param.Search?.PredicateObject != null)
            {
                dynamic search = param.Search.PredicateObject;
                if (search.Keyword != null)
                {
                    string keyword = ((string)search.Keyword).Trim();
                    if (!string.IsNullOrWhiteSpace(keyword))
                    {
                        query = query.Where(x =>
                            x.Code.Contains(keyword) ||
                            x.AddressDetail.Contains(keyword));
                    }
                }
            }

            // Sắp xếp mặc định
            param.Sort = new Sort() { Predicate = "CreatedTime", Reverse = true };

            // Sử dụng AutoMapper extension
            return await query.ToSmartTableResultAsync(param, x => x.ToItem());
        }

        public async Task<BaseResult> CreateOrUpdate(PremiseModel model)
        {
            BaseResult result = new BaseResult { Result = Result.Success };
            if (model.Id > 0)
            {
                var entity = await _repository.GetByIdAsync(model.Id);
                if (entity != null)
                {
                    entity.Code = model.Code;
                    entity.AgencyId = model.AgencyId;
                    entity.CityId = model.CityId;
                    entity.DistrictId = model.DistrictId;
                    entity.WardId = model.WardId;
                    entity.AddressDetail = model.AddressDetail;
                    entity.Latitude = model.Latitude;
                    entity.Longitude = model.Longitude;
                    entity.GoogleMapUrl = model.GoogleMapUrl;
                    entity.Description = model.Description;
                    entity = entity.UpdateCommonInt(model.UpdatedBy, model.UpdatedByUserName);

                    var rs = await _repository.UpdateAsync(entity);
                    if (rs >= 0)
                    {
                        result.Result = Result.Success;

                        // Sync Contacts, Scores, Medias & Financial
                        await SyncContacts(entity.Id, model.Contacts, model.UpdatedBy, model.UpdatedByUserName);
                        await SyncScores(entity.Id, model.Scores, model.UpdatedBy, model.UpdatedByUserName);
                        await SyncMedias(entity.Id, model);
                        if (model.Financial != null)
                        {
                            model.Financial.PremiseId = entity.Id;
                            model.Financial.UpdatedBy = model.UpdatedBy;
                            model.Financial.UpdatedByUserName = model.UpdatedByUserName;
                            await UpdateFinancial(model.Financial);
                        }
                    }
                }
                else
                {
                    result.Result = Result.Failed;
                    result.Message = "Không tìm thấy mặt bằng";
                }
            }
            else
            {
                var entity = model.ToEntity();
                entity.Status = PremiseStatus.Surveying;
                entity = entity.UpdateCommonInt(model.UpdatedBy, model.UpdatedByUserName);

                try
                {
                    var rs = await _repository.InsertAsync(entity);
                    if (rs > 0)
                    {
                        result.Result = Result.Success;

                        // Sync Contacts, Scores, Medias & Financial
                        await SyncContacts(entity.Id, model.Contacts, model.UpdatedBy, model.UpdatedByUserName);
                        await SyncScores(entity.Id, model.Scores, model.UpdatedBy, model.UpdatedByUserName);
                        await SyncMedias(entity.Id, model);
                        if (model.Financial != null)
                        {
                            model.Financial.PremiseId = entity.Id;
                            model.Financial.UpdatedBy = model.UpdatedBy;
                            model.Financial.UpdatedByUserName = model.UpdatedByUserName;
                            await UpdateFinancial(model.Financial);
                        }
                    }
                }
                catch (Exception e)
                {
                    result.Result = Result.SystemError;
                    result.Message = e.Message;
                }
            }
            return result;
        }

        public async Task<PremiseResult> GetById(int id)
        {
            var result = new PremiseResult();
            if (id <= 0)
            {
                result.Result = Result.Failed;
                result.Message = "Không tìm thấy mặt bằng";
                return result;
            }

            var entity = await _repository.Query()
                .Include(x => x.Agency)
                .Include(x => x.City)
                .Include(x => x.District)
                .Include(x => x.Ward)
                .Include(x => x.Financial)
                .Include(x => x.Contacts.Where(c => !c.IsDeleted))
                .Include(x => x.Scores.Where(s => !s.IsDeleted))
                    .ThenInclude(s => s.Criteria)
                .Include(x => x.Scores.Where(s => !s.IsDeleted))
                    .ThenInclude(s => s.SelectedOption)
                .Include(x => x.PremiseMedias)
                    .ThenInclude(m => m.Media)
                .FirstOrDefaultAsync(x => x.Id == id && !x.IsDeleted);

            if (entity == null)
            {
                result.Result = Result.Failed;
                result.Message = "Không tìm thấy mặt bằng";
                return result;
            }

            result.Id = entity.Id;
            result.Code = entity.Code;
            result.Status = entity.Status;
            result.AgencyId = entity.AgencyId;
            result.AgencyName = entity.Agency?.Name ?? "";
            result.CityId = entity.CityId;
            result.CityName = entity.City?.Name ?? "";
            result.DistrictId = entity.DistrictId;
            result.DistrictName = entity.District?.Name ?? "";
            result.WardId = entity.WardId;
            result.WardName = entity.Ward?.Name ?? "";
            result.AddressDetail = entity.AddressDetail;
            result.Latitude = entity.Latitude;
            result.Longitude = entity.Longitude;
            result.GoogleMapUrl = entity.GoogleMapUrl;
            result.TotalScore = entity.TotalScore;
            result.Description = entity.Description;
            result.CreatedByUserName = entity.CreatedByUserName;
            result.CreatedTime = entity.CreatedTime;

            if (entity.Financial != null)
            {
                result.Financial = entity.Financial.ToDetail();
            }

            result.Contacts = entity.Contacts
                .Select(c => c.ToDetail())
                .ToList();

            result.Scores = entity.Scores
                .Select(s => s.ToDetail())
                .ToList();

            // Map Medias
            var mediaDetails = new List<PremiseMediaDetail>();
            foreach (var pm in entity.PremiseMedias.OrderBy(m => m.DisplayOrder))
            {
                var url = pm.Media?.SystemPath != null
                    ? await _unifiedMediaService.GetUrlBySystemPathAsync(pm.Media.SystemPath)
                    : string.Empty;

                mediaDetails.Add(new PremiseMediaDetail
                {
                    Id = pm.Id,
                    MediaId = pm.MediaId,
                    MediaType = pm.MediaType,
                    Url = url,
                    DisplayOrder = pm.DisplayOrder
                });
            }
            result.Medias = mediaDetails;

            return result;
        }

        public async Task<BaseResult> Delete(int id)
        {
            BaseResult result = new BaseResult { Result = Result.Success };
            if (id > 0)
            {
                var entity = await _repository.GetByIdAsync(id);
                if (entity != null)
                {
                    var rs = await _repository.DeleteAsync(entity);
                    if (rs > 0)
                    {
                        result.Result = Result.Success;
                    }
                }
                else
                {
                    result.Result = Result.Failed;
                    result.Message = "Không tìm thấy mặt bằng";
                }
            }
            else
            {
                result.Result = Result.Failed;
                result.Message = "Không tìm thấy mặt bằng";
            }
            return result;
        }

        public async Task<BaseResult> ChangeStatus(PremiseStatusModel model)
        {
            BaseResult result = new BaseResult { Result = Result.Success };
            if (model.Id > 0)
            {
                var entity = await _repository.GetByIdAsync(model.Id);
                if (entity != null)
                {
                    entity.Status = model.Status;
                    entity = entity.UpdateCommonInt(model.UpdatedBy, model.UpdatedByUserName);

                    var rs = await _repository.UpdateAsync(entity);
                    if (rs >= 0)
                    {
                        result.Result = Result.Success;
                    }
                }
                else
                {
                    result.Result = Result.Failed;
                    result.Message = "Không tìm thấy mặt bằng";
                }
            }
            else
            {
                result.Result = Result.Failed;
                result.Message = "Không tìm thấy mặt bằng";
            }
            return result;
        }

        public async Task<BaseResult> UpdateFinancial(PremiseFinancialModel model)
        {
            BaseResult result = new BaseResult { Result = Result.Success };
            var premise = await _repository.GetByIdAsync(model.PremiseId);
            if (premise == null)
            {
                result.Result = Result.Failed;
                result.Message = "Không tìm thấy mặt bằng";
                return result;
            }

            var financial = await _financialRepository.Query()
                .FirstOrDefaultAsync(x => x.PremiseId == model.PremiseId && !x.IsDeleted);

            if (financial == null)
            {
                // Create
                financial = model.ToEntity();
                financial = financial.UpdateCommonInt(model.UpdatedBy, model.UpdatedByUserName);
                await _financialRepository.InsertAsync(financial);
            }
            else
            {
                // Update
                financial.AskingRent = model.AskingRent;
                financial.NegotiatedRent = model.NegotiatedRent;
                financial.DepositMonths = model.DepositMonths;
                financial.PaymentPeriodMonths = model.PaymentPeriodMonths;
                financial.FreeFitOutDays = model.FreeFitOutDays;
                financial.AvailableFromDate = model.AvailableFromDate;
                financial.CanProvideVatInvoice = model.CanProvideVatInvoice;
                financial.LegalNote = model.LegalNote;
                financial = financial.UpdateCommonInt(model.UpdatedBy, model.UpdatedByUserName);
                await _financialRepository.UpdateAsync(financial);
            }

            return result;
        }

        #region Private Helpers

        private async Task SyncContacts(int premiseId, List<PremiseContactModel> contacts, int updatedBy, string updatedByUserName)
        {
            var existingContacts = await _contactRepository.Query()
                .Where(x => x.PremiseId == premiseId && !x.IsDeleted)
                .ToListAsync();

            // Soft delete removed contacts
            var updatedIds = contacts.Where(c => c.Id > 0).Select(c => c.Id).ToList();
            foreach (var existing in existingContacts.Where(e => !updatedIds.Contains(e.Id)))
            {
                existing.IsDeleted = true;
                existing.UpdateCommonInt(updatedBy, updatedByUserName);
                await _contactRepository.UpdateAsync(existing);
            }

            // Create or update
            foreach (var contactModel in contacts)
            {
                if (contactModel.Id > 0)
                {
                    var existing = existingContacts.FirstOrDefault(e => e.Id == contactModel.Id);
                    if (existing != null)
                    {
                        existing.FullName = contactModel.FullName;
                        existing.PhoneNumber = contactModel.PhoneNumber;
                        existing.Role = contactModel.Role;
                        existing.Note = contactModel.Note;
                        existing.UpdateCommonInt(updatedBy, updatedByUserName);
                        await _contactRepository.UpdateAsync(existing);
                    }
                }
                else
                {
                    var newContact = contactModel.ToEntity();
                    newContact.PremiseId = premiseId;
                    newContact.UpdateCommonInt(updatedBy, updatedByUserName);
                    await _contactRepository.InsertAsync(newContact);
                }
            }
        }

        private async Task SyncScores(int premiseId, List<PremiseScoreModel> scores, int updatedBy, string updatedByUserName)
        {
            var totalScore = 0;

            // Xóa mềm các score cũ bị xóa khỏi danh sách mới
            var updatedCriteriaIds = scores.Select(s => s.CriteriaId).ToList();
            var existingScoresToDel = await _scoreRepository.Query()
                .Where(x => x.PremiseId == premiseId && !updatedCriteriaIds.Contains(x.CriteriaId) && !x.IsDeleted)
                .ToListAsync();

            foreach (var item in existingScoresToDel)
            {
                item.IsDeleted = true;
                item.UpdateCommonInt(updatedBy, updatedByUserName);
                await _scoreRepository.UpdateAsync(item);
            }

            foreach (var scoreModel in scores)
            {
                // Auto-resolve score from option
                var score = 0;

                if (scoreModel.NumericValue.HasValue)
                {
                    // Range matching: ưu tiên NumericValue để tính lại score chính xác
                    var matchedOption = await _scoringOptionRepository.Query()
                        .Where(x => x.CriteriaId == scoreModel.CriteriaId
                            && (!x.MinValue.HasValue || x.MinValue <= scoreModel.NumericValue)
                            && (!x.MaxValue.HasValue || x.MaxValue >= scoreModel.NumericValue))
                        .FirstOrDefaultAsync();

                    if (matchedOption != null)
                    {
                        score = matchedOption.Score;
                        scoreModel.SelectedOptionId = matchedOption.Id;
                    }
                }
                else if (scoreModel.SelectedOptionId.HasValue && scoreModel.SelectedOptionId.Value > 0)
                {
                    // Option-based scoring (dropdown/radio)
                    var option = await _scoringOptionRepository.GetByIdAsync(scoreModel.SelectedOptionId.Value);
                    score = option?.Score ?? 0;
                }

                // Upsert score
                var existingScore = await _scoreRepository.Query()
                    .FirstOrDefaultAsync(x => x.PremiseId == premiseId
                        && x.CriteriaId == scoreModel.CriteriaId
                        && !x.IsDeleted);

                // Xử lý giá trị 0 thành null để tránh lỗi Foreign Key
                var safeSelectedOptionId = (scoreModel.SelectedOptionId.HasValue && scoreModel.SelectedOptionId.Value > 0) 
                    ? scoreModel.SelectedOptionId 
                    : null;

                if (existingScore != null)
                {
                    existingScore.SelectedOptionId = safeSelectedOptionId;
                    existingScore.TextValue = scoreModel.TextValue;
                    existingScore.NumericValue = scoreModel.NumericValue;
                    existingScore.Score = score;
                    existingScore.Note = scoreModel.Note;
                    existingScore.UpdateCommonInt(updatedBy, updatedByUserName);
                    await _scoreRepository.UpdateAsync(existingScore);
                }
                else
                {
                    var newScore = new PremiseScore
                    {
                        PremiseId = premiseId,
                        CriteriaId = scoreModel.CriteriaId,
                        SelectedOptionId = safeSelectedOptionId,
                        TextValue = scoreModel.TextValue,
                        NumericValue = scoreModel.NumericValue,
                        Score = score,
                        Note = scoreModel.Note
                    };
                    newScore.UpdateCommonInt(updatedBy, updatedByUserName);
                    await _scoreRepository.InsertAsync(newScore);
                }

                totalScore += score;
            }

            // Update TotalScore on Premise
            var premise = await _repository.GetByIdAsync(premiseId);
            if (premise != null)
            {
                premise.TotalScore = totalScore;
                await _repository.UpdateAsync(premise);
            }
        }

        private async Task SyncMedias(int premiseId, PremiseModel model)
        {
            // 1. Xóa các media được đánh dấu
            if (model.DeleteMediaIds != null && model.DeleteMediaIds.Count > 0)
            {
                foreach (var deleteId in model.DeleteMediaIds)
                {
                    var premiseMedia = await _premiseMediaRepository.GetByIdAsync(deleteId);
                    if (premiseMedia != null && premiseMedia.PremiseId == premiseId)
                    {
                        await _premiseMediaRepository.DeleteAsync(premiseMedia);
                    }
                }
            }

            // 2. Upload và lưu ảnh mới
            if (model.NewMediaFiles != null && model.NewMediaFiles.Count > 0)
            {
                var currentTime = DateTimeHelper.CurrentTime();

                // Lấy DisplayOrder hiện tại cao nhất
                var maxOrder = await _premiseMediaRepository.Query()
                    .Where(x => x.PremiseId == premiseId)
                    .Select(x => (int?)x.DisplayOrder)
                    .MaxAsync() ?? 0;

                for (int i = 0; i < model.NewMediaFiles.Count; i++)
                {
                    var file = model.NewMediaFiles[i];
                    var mediaType = (model.NewMediaTypes != null && i < model.NewMediaTypes.Count)
                        ? model.NewMediaTypes[i]
                        : PremiseMediaType.FrontageImage;

                    try
                    {
                        // Xử lý ảnh
                        var processResult = await _unifiedMediaService.ProcessImageAsync(file, ImageType.Premise);
                        if (processResult.Result == Result.Success)
                        {
                            // Tạo Media entity
                            var media = new Media
                            {
                                Id = Guid.NewGuid(),
                                SystemPath = processResult.SystemPath,
                                AltText = $"Ảnh mặt bằng - {mediaType}",
                                Caption = $"Ảnh mặt bằng - {mediaType}",
                                DisplayOrder = maxOrder + i + 1,
                                CreatedTime = currentTime,
                                UpdatedTime = currentTime,
                                CreatedBy = model.UpdatedBy,
                                CreatedByUserName = model.UpdatedByUserName,
                                UpdatedBy = model.UpdatedBy,
                                UpdatedByUserName = model.UpdatedByUserName
                            };
                            await _mediaRepository.InsertAsync(media);

                            // Tạo PremiseMedia liên kết
                            var premiseMedia = new PremiseMedia
                            {
                                PremiseId = premiseId,
                                MediaId = media.Id,
                                MediaType = mediaType,
                                DisplayOrder = maxOrder + i + 1
                            };
                            premiseMedia.UpdateCommonInt(model.UpdatedBy, model.UpdatedByUserName);
                            await _premiseMediaRepository.InsertAsync(premiseMedia);
                        }
                    }
                    catch (Exception)
                    {
                        // Bỏ qua lỗi từng file, tiếp tục xử lý file khác
                    }
                }
            }
        }

        public async Task<BaseResult> DeleteMedia(int premiseMediaId)
        {
            var result = new BaseResult { Result = Result.Success };
            try
            {
                var premiseMedia = await _premiseMediaRepository.GetByIdAsync(premiseMediaId);
                if (premiseMedia == null)
                {
                    result.Result = Result.Failed;
                    result.Message = "Không tìm thấy ảnh";
                    return result;
                }

                await _premiseMediaRepository.DeleteAsync(premiseMedia);
            }
            catch (Exception e)
            {
                result.Result = Result.SystemError;
                result.Message = e.Message;
            }
            return result;
        }

        #endregion
    }
}
