public MultilingualAccommodation Correct(AccommodationDataForCorrection accommodationDataForCorrection)
    {
        var normalizedSupplierAccommodationDetails = accommodationDataForCorrection.SuppliersRawAccommodationData
                                                     .ToDictionary(sa => sa.Key,
                                                                   sa => _multilingualDataHelper.NormalizeAccommodation(sa.Value, accommodationDataForCorrection.CountryNames,
                                                                                                                        accommodationDataForCorrection.LocalityNames, accommodationDataForCorrection.LocalityZoneNames));

        var name = MergeMultilingualData(accommodationDataForCorrection.SuppliersPriorities[AccommodationDataTypes.Name],
                                         normalizedSupplierAccommodationDetails.ToDictionary(s => s.Key, s => s.Value.Name) !,
                                         accommodationDataForCorrection.ManualCorrectedData.Name, string.IsNullOrEmpty);

        var category = MergeMultilingualData(accommodationDataForCorrection.SuppliersPriorities[AccommodationDataTypes.Category],
                                             normalizedSupplierAccommodationDetails.ToDictionary(s => s.Key, s => s.Value.Category) !,
                                             accommodationDataForCorrection.ManualCorrectedData.Category, string.IsNullOrEmpty);

        var rating = MergeData(accommodationDataForCorrection.SuppliersPriorities[AccommodationDataTypes.Rating],
                               normalizedSupplierAccommodationDetails.ToDictionary(s => s.Key, s => s.Value.Rating),
                               accommodationDataForCorrection.ManualCorrectedData.Rating,
                               r => r == 0);

        var contactInfo = MergeContactInfo(accommodationDataForCorrection.SuppliersPriorities[AccommodationDataTypes.ContactInfo],
                                           normalizedSupplierAccommodationDetails, accommodationDataForCorrection.ManualCorrectedData);

        var locationInfo = MergeLocationInfo(accommodationDataForCorrection.SuppliersPriorities[AccommodationDataTypes.LocationInfo],
                                             normalizedSupplierAccommodationDetails, accommodationDataForCorrection.ManualCorrectedData, accommodationDataForCorrection.CountryNames,
                                             accommodationDataForCorrection.LocalityNames,
                                             accommodationDataForCorrection.LocalityZoneNames);

        var photos = MergeData(accommodationDataForCorrection.SuppliersPriorities[AccommodationDataTypes.Photos],
                               normalizedSupplierAccommodationDetails.ToDictionary(s => s.Key, s => s.Value.Photos),
                               accommodationDataForCorrection.ManualCorrectedData.Photos, p => p == null ! || !p.Any());

        var textualDescriptions = MergeData(accommodationDataForCorrection.SuppliersPriorities[AccommodationDataTypes.TextualDescriptions],
                                            normalizedSupplierAccommodationDetails.ToDictionary(s => s.Key, s => s.Value.TextualDescriptions),
                                            accommodationDataForCorrection.ManualCorrectedData.TextualDescriptions,
                                            p => p == null ! || !p.Any());

        var additionalInfo = MergeMultilingualData(accommodationDataForCorrection.SuppliersPriorities[AccommodationDataTypes.AdditionalInfo],
                                                   normalizedSupplierAccommodationDetails.ToDictionary(s => s.Key, s => s.Value.AdditionalInfo) !,
                                                   accommodationDataForCorrection.ManualCorrectedData.AdditionalInfo, p => p == null ! || !p.Any());

        var accommodationAmenities = MergeMultilingualData(
            accommodationDataForCorrection.SuppliersPriorities[AccommodationDataTypes.AccommodationAmenities],
            normalizedSupplierAccommodationDetails.ToDictionary(s => s.Key, s => s.Value.AccommodationAmenities) !,
            accommodationDataForCorrection.ManualCorrectedData.AccommodationAmenities,
            p => p == null ! || !p.Any());

        var scheduleInfo = MergeScheduleInfo(accommodationDataForCorrection.SuppliersPriorities[AccommodationDataTypes.Schedule],
                                             normalizedSupplierAccommodationDetails, accommodationDataForCorrection.ManualCorrectedData);

        var propertyType = MergeData(accommodationDataForCorrection.SuppliersPriorities[AccommodationDataTypes.PropertyType],
                                     normalizedSupplierAccommodationDetails.ToDictionary(s => s.Key, s => s.Value.Type),
                                     accommodationDataForCorrection.ManualCorrectedData.Type, t => t == 0);

        var hasDirectContractList = normalizedSupplierAccommodationDetails.Select(ac => ac.Value.HasDirectContract).ToList();

        hasDirectContractList.Add(accommodationDataForCorrection.ManualCorrectedData.HasDirectContract);
        var hasDirectContract = MergeBoolData(hasDirectContractList);

        return(new MultilingualAccommodation
               (
                   string.Empty,
                   name: name,
                   category: category,
                   location: locationInfo,
                   photos: photos,
                   rating: rating,
                   type: propertyType,
                   accommodationAmenities: accommodationAmenities,
                   contacts: contactInfo,
                   additionalInfo: additionalInfo,
                   schedule: scheduleInfo,
                   textualDescriptions: textualDescriptions,
                   hasDirectContract: hasDirectContract,
                   isActive: true
               ));
    }
    public async Task CorrectAccommodations(List <RichAccommodationDetails> notCorrectedAccommodations, CancellationToken cancellationToken = default)
    {
        var accommodationsToPublish = new List <(AccommodationData accommodation, int localityId)>();

        await GetRawAccommodations()
        .Bind(CorrectAll)
        .BindWithTransaction(_context, () => SaveCorrected()
                             .Tap(PublishCorrected)
                             .Tap(SaveCorrectionLogs));


        async Task <Result <List <RawAccommodation> > > GetRawAccommodations()
        {
            var supplierAccommodationIds = notCorrectedAccommodations
                                           .SelectMany(ac => ac.SupplierAccommodationCodes).Select(ac => ac.Value).ToList();

            var rawAccommodations = await _context.RawAccommodations.Where(ra
                                                                           => supplierAccommodationIds.Contains(ra.SupplierAccommodationId))
                                    .Select(ra => new RawAccommodation
            {
                Accommodation           = ra.Accommodation,
                Supplier                = ra.Supplier,
                SupplierAccommodationId = ra.SupplierAccommodationId,
                LocalityNames           = ra.LocalityNames,
                LocalityZoneNames       = ra.LocalityZoneNames
            })
                                    .ToListAsync(cancellationToken);

            return(rawAccommodations);
        }

        async Task <Result> CorrectAll(List <RawAccommodation> rawAccommodations)
        {
            foreach (var accommodation in notCorrectedAccommodations)
            {
                await GetAccommodationSuppliersData(accommodation, rawAccommodations)
                .Bind(suppliersData => CorrectAccommodation(accommodation, suppliersData))
                .Bind(GetCorrectedAccommodationKeyData)
                .Bind(data => CollectDataForPublish(accommodation, data.CorrectedData, data.KeyData))
                .Bind(data => UpdateAccommodation(accommodation, data.CorrectedData, data.KeyData))
                .Tap(correctedData => CollectCorrectionLog(accommodation.Id, correctedData));
            }

            return(Result.Success());
        }

        Result <(MultilingualAccommodation CorrectedData, AccommodationKeyData KeyData)>
        GetCorrectedAccommodationKeyData(MultilingualAccommodation correctedData)
        => (correctedData, _multilingualDataHelper.GetAccommodationKeyData(correctedData));


        async Task <Result> SaveCorrected()
        {
            await _context.SaveChangesAsync(cancellationToken);

            return(Result.Success());
        }

        async Task PublishCorrected()
        {
            await _accommodationChangePublisher.PublishUpdated(accommodationsToPublish);
        }

        async Task SaveCorrectionLogs()
        {
            await _accommodationChangeLogCollectorService.SaveCollectedEventLogs(cancellationToken);
        }

        Result <Dictionary <string, MultilingualAccommodation> > GetAccommodationSuppliersData(RichAccommodationDetails accommodation,
                                                                                               List <RawAccommodation> rawAccommodations)
        {
            var supplierAccommodations = (from ra in rawAccommodations
                                          join sa in accommodation.SupplierAccommodationCodes on ra.SupplierAccommodationId equals sa.Value
                                          where ra.Supplier == sa.Key
                                          select ra).ToList();

            return((from sa in supplierAccommodations
                    join acs in accommodation.SupplierAccommodationCodes
                    on new { Supplier = sa.Supplier, SupplierAccommodationId = sa.SupplierAccommodationId }
                    equals new { Supplier = acs.Key, SupplierAccommodationId = acs.Value }
                    select sa).ToDictionary(a => a.Supplier, a => JsonConvert.DeserializeObject <MultilingualAccommodation>(a.Accommodation.RootElement
                                                                                                                            .ToString() !)));
        }

        async Task <Result <MultilingualAccommodation> > CorrectAccommodation(RichAccommodationDetails accommodation,
                                                                              Dictionary <string, MultilingualAccommodation> supplierAccommodations)
        {
            var dataForCorrection = new AccommodationDataForCorrection
            {
                SuppliersRawAccommodationData = supplierAccommodations,
                ManualCorrectedData           = accommodation.AccommodationWithManualCorrections,
                CountryNames        = accommodation.Country.Names,
                LocalityNames       = accommodation.Locality.Names,
                LocalityZoneNames   = accommodation.LocalityZone?.Names,
                SuppliersPriorities = accommodation.SuppliersPriority.Any()
                    ? accommodation.SuppliersPriority
                    : await GetSupplierPriorities()
            };

            return(_dataCorrectionHelper.Correct(dataForCorrection));
        }

        Result <(MultilingualAccommodation CorrectedData, AccommodationKeyData KeyData)> CollectDataForPublish(RichAccommodationDetails accommodation,
                                                                                                               MultilingualAccommodation correctedData, AccommodationKeyData keyData)
        {
            var dataToPublish = Convert(accommodation.Id, accommodation.CountryCode, keyData);

            if (!dataToPublish.Equals(Convert(accommodation.Id, accommodation.CountryCode, accommodation.KeyData)))
            {
                accommodationsToPublish.Add((dataToPublish, accommodation.LocalityId ?? default));
            }
            return(correctedData, keyData);
        }

        Result <MultilingualAccommodation> UpdateAccommodation(RichAccommodationDetails accommodation, MultilingualAccommodation correctedData,
                                                               AccommodationKeyData keyData)
        {
            var dbAccommodation = new RichAccommodationDetails
            {
                Id = accommodation.Id
            };

            _context.Accommodations.Attach(dbAccommodation);
            var dbEntry = _context.Entry(dbAccommodation);

            dbAccommodation.IsCorrected        = true;
            dbAccommodation.FinalAccommodation = correctedData;
            dbAccommodation.HasDirectContract  = correctedData.HasDirectContract;
            dbAccommodation.KeyData            = keyData;
            dbAccommodation.Modified           = DateTimeOffset.UtcNow;

            dbEntry.Property(p => p.FinalAccommodation).IsModified = true;
            dbEntry.Property(p => p.HasDirectContract).IsModified  = true;
            dbEntry.Property(p => p.IsCorrected).IsModified        = true;
            dbEntry.Property(p => p.Modified).IsModified           = true;
            dbEntry.Property(p => p.KeyData).IsModified            = true;

            return(correctedData);
        }

        void CollectCorrectionLog(int accommodationId, MultilingualAccommodation correctedData)
        => _accommodationChangeLogCollectorService.CollectDataCorrectionEvent(accommodationId, AccommodationChangeEventSources.Updater, correctedData);
    }