using GeoRitmMover.Mover.Data;

using Microsoft.EntityFrameworkCore;

using Monitoring.Core.Domain;

namespace GeoRitmMover.Services.Implementations.TableMovers;
public class PhotoTableMover(
	ILogger<PhotoTableMover> logger,
	IContexts contexts
) : ITableMover
{
	public async Task Move(CancellationToken cancellationToken)
	{
		logger.LogInformation("Moving photos");

		var count = await contexts.Source.Photos.CountAsync(cancellationToken);

		var maps = await contexts.Middleware.Photos
			.Select(x => x.To)
			.ToListAsync(cancellationToken);

		for (var i = 0; i < count; i += Constants.BatchSize)
		{
			await PassBatch(i, Constants.BatchSize, cancellationToken);
		}

		logger.LogInformation("Done moving photos");
	}

	private async Task PassBatch(int startIndex, int count,
		CancellationToken cancellationToken)
	{
		logger.LogInformation("Start photo batch with index: {startIndex}", startIndex);

		var sourceAreaList = await contexts.Source.Photos
			.AsNoTracking()
			.OrderBy(x => x.Id)
			.Skip(startIndex)
			.Take(count)
			.ToListAsync(cancellationToken);
		var sourcePhotoDictionary = sourceAreaList.ToDictionary(x => x.Id);

		var photoMapList = sourceAreaList
			.Select(x => PhotoMap.Create(Guid.NewGuid(), x.Id, Guid.NewGuid()))
			.ToList();

		var sourceObjectIdList = sourceAreaList.Select(x => x.ObjectId).Distinct().ToList();
		var assetMapList = await contexts.Middleware.Assets
			.AsNoTracking()
			.Where(x => sourceObjectIdList.Contains(x.From))
			.ToListAsync(cancellationToken);
		var assetMapDictionary = assetMapList
			.ToDictionary(x => x.From, x => x.To);

		var photoList = photoMapList
			.Select(map =>
			{
				var sourcePhoto = sourcePhotoDictionary[map.From];

				var base64 = $"data:{sourcePhoto.MimeType};base64,{Convert.ToBase64String(sourcePhoto.Payload)}";

				return new Photo()
				{
					Id = map.To,
					Name = sourcePhoto.FileName,
					AssetId = assetMapDictionary[sourcePhoto.ObjectId],
					Description = sourcePhoto.Caption ?? "-",
					Base64 = base64,
				};
			})
			.ToList();

		using var middlewareTransaction = await contexts.Middleware.Database
			.BeginTransactionAsync(cancellationToken);

		try
		{
			logger.LogInformation("Try save photo maps");

			await contexts.Middleware.Photos.AddRangeAsync(photoMapList, cancellationToken);
			_ = await contexts.Middleware.SaveChangesAsync(cancellationToken);

			logger.LogInformation("Photo maps are saved");

			using var transaction = await contexts.Destination.Database
				.BeginTransactionAsync(cancellationToken);

			try
			{
				logger.LogInformation("Try save photos");

				await contexts.Destination.Photos.AddRangeAsync(photoList, cancellationToken);
				_ = await contexts.Destination.SaveChangesAsync(cancellationToken);

				logger.LogInformation("Photos are saved");

				await transaction.CommitAsync(cancellationToken);
			}
			catch (Exception ex)
			{
				logger.LogError(ex, "Error while saving photos");
				await transaction.RollbackAsync(cancellationToken);

				throw;
			}

			await middlewareTransaction.CommitAsync(cancellationToken);
		}
		catch (Exception ex)
		{
			logger.LogError(ex, "Error while saving photo maps");
			await middlewareTransaction.RollbackAsync(cancellationToken);
		}

		logger.LogInformation("Done moving photo batch: start = {start}, count = {count}", startIndex, Constants.BatchSize);
	}
}
