using GeoRitmMover.Mover.Data;
using Microsoft.EntityFrameworkCore;
using Monitoring.Core.Domain.AssetGroupManagement;
using Monitoring.Core.Domain.OrganizationManagement;

namespace GeoRitmMover.Services.Implementations.TableMovers;

public class AssetGroupTableMover(ILogger<AssetGroupTableMover> logger, IContexts contexts)
	: ITableMover
{
	public async Task Move(CancellationToken cancellationToken)
	{
		logger.LogInformation("Moving asset groups");

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

		var maps = await contexts
			.Middleware.Groups.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 asset groups");
	}

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

		var sourceAssetGroups = await contexts
			.Source.Groups.AsNoTracking()
			.OrderBy(x => x.Id)
			.Skip(startIndex)
			.Take(count)
			.ToListAsync(cancellationToken);
		var sourceAssetGroupsDictionary = sourceAssetGroups.ToDictionary(x => x.Id);

		var assetGroupMaps = sourceAssetGroups
			.Select(x => AssetGroupMap.Create(Guid.NewGuid(), x.Id, new(Guid.NewGuid())))
			.ToList();
		var assetGroupMapDictionary = assetGroupMaps.ToDictionary(x => x.From);

		var organizationId = new OrganizationId(Constants.OrganizationId);
		var assetGroups = assetGroupMaps
			.Select(x =>
			{
				var source = sourceAssetGroupsDictionary[x.From];
				var name = AssetGroupName.Create(source.Name);
				var parentId = source.ParentId.HasValue
					? assetGroupMapDictionary[source.ParentId.Value].To
					: null;
				var destination = AssetGroup.Create(x.To, name, organizationId, false);
				destination.SetParent(parentId);
				return destination;
			})
			.ToList();

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

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

			await contexts.Middleware.Groups.AddRangeAsync(assetGroupMaps, cancellationToken);
			_ = await contexts.Middleware.SaveChangesAsync(cancellationToken);

			logger.LogInformation("Asset group maps is saved");

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

			try
			{
				logger.LogInformation("Try save asset groups");

				await contexts.Destination.AssetGroups.AddRangeAsync(
					assetGroups,
					cancellationToken
				);
				_ = await contexts.Destination.SaveChangesAsync(cancellationToken);

				logger.LogInformation("Asset groups is saved");

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

				throw;
			}

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

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