using GeoRitmMover.GeoRitm.Data;
using GeoRitmMover.Mover.Data;

using Microsoft.EntityFrameworkCore;

using Monitoring.Core.Domain.EventManagement;

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

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

		var maps = await contexts.Middleware.EventCodes
			.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 alert templates");
	}

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

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

		var areaMapList = sourceAreaList
			.Select(x => EventCodeMap.Create(Guid.NewGuid(), x.Id, new(Guid.NewGuid())))
			.ToList();

		var areaList = areaMapList
			.Select(map =>
			{
				var sourceArea = sourceAreasDictionary[map.From];

				return EventCode.Create(
					map.To,
					new EventCodeValue(sourceArea.Code),
					GetGroup(sourceArea, sourceAreaList),
					sourceArea.IsAlarm
				);
			})
			.ToList();

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

		if (areaList.GroupBy(x => x.Value).Any(g => g.Count() > 1))
		{
			logger.LogWarning("Duplicate event codes found");
			// handle duplicates, e.g., remove them
			areaList = areaList.DistinctBy(x => x.Value).ToList();
		}

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

			await contexts.Middleware.EventCodes.AddRangeAsync(areaMapList, cancellationToken);
			_ = await contexts.Middleware.SaveChangesAsync(cancellationToken);

			logger.LogInformation("Alert template maps is saved");

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

			try
			{
				logger.LogInformation("Try save event codes");

				await contexts.Destination.EventCodes.AddRangeAsync(areaList, cancellationToken);
				_ = await contexts.Destination.SaveChangesAsync(cancellationToken);

				logger.LogInformation("Event codes is saved");

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

				throw;
			}

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

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

	private static EventCodeGroup GetGroup(SourceAlertTemplate template, List<SourceAlertTemplate> templateList)
	{
		if (template.GroupId == 2)
		{
			if (template.Code % 10 == 3)
			{
				var alarmTemplate = templateList.FirstOrDefault(x => x.Code == template.Code - 2);
				if (alarmTemplate is not null)
				{
					return GetGroup(alarmTemplate, templateList);
				}
			}

			return EventCodeGroup.Miscellaneous;
		}

		return template.GroupId switch
		{
			1 => EventCodeGroup.Alarm,
			3 => EventCodeGroup.Fault,
			4 => EventCodeGroup.Arming,
			5 => EventCodeGroup.System,
			_ => EventCodeGroup.Miscellaneous,
		};
	}
}
