using System.Data;
using System.Text.Json;

using CSharpFunctionalExtensions;

using Microsoft.EntityFrameworkCore;

using Monitoring.Core.Domain.EventManagement;

namespace Monitoring.Features.Reports.ReportFactories;

internal class SummaryEventsReportFactory : IReportFactory
{
	private record Item(
		Guid Id,
		string Name,
		int TotalCount,
		int AlarmCount
	);

	private record Data(
		List<Item> Items,
		DateTime From,
		DateTime To
	);

	public Result<DataTable> Export(MakeExportContext context)
	{
		DataTable dataTable = new("Assets");
		dataTable.Columns.AddRange([
			new(Translations.Translate(context.Language, "Name"), typeof(CellContainer)),
			new(Translations.Translate(context.Language, "TotalCount"), typeof(CellContainer)),
			new(Translations.Translate(context.Language, "AlarmCount"), typeof(CellContainer)),
		]);

		var itemsResult = JsonSerializer.Deserialize<Data>(context.Report.Data);
		if (itemsResult is null)
		{
			return Result.Failure<DataTable>("Report data is failed to deserialize to Data");
		}

		foreach (var log in itemsResult.Items)
		{
			_ = dataTable.Rows.Add(
				new CellContainer(log.Name, 1),
				new CellContainer(log.TotalCount.ToString(), 1),
				new CellContainer(log.AlarmCount.ToString(), 1)
			);
		}

		var totalEvents = itemsResult.Items.Sum(x => x.TotalCount);
		var totalAlarms = itemsResult.Items.Sum(x => x.AlarmCount);
		
		_ = dataTable.Rows.Add(
			new CellContainer("ИТОГО:", 1),
			new CellContainer(totalEvents.ToString(), 1),
			new CellContainer(totalAlarms.ToString(), 1)
		);

		return dataTable;
	}

	public async ValueTask<Result<string>> Report(
		MakeReportContext context,
		CancellationToken cancellationToken)
	{
		if (string.IsNullOrEmpty(context.Extra))
		{
			return Result.Failure<string>("Extra is required");
		}

		var extra = JsonSerializer.Deserialize<Data>(context.Extra);
		if (extra is null)
		{
			return Result.Failure<string>("Extra is failed to deserialize to Extra");
		}

		var summary = await context.DataContext.Events
			.AsNoTracking()
			.Where(x => context.AssetIdList.Contains(x.AssetId))
			.Where(x => x.CreatedAt >= extra.From && x.CreatedAt <= extra.To)
			.GroupBy(x => x.AssetId)
			.Select(eventsGroup => new Item(
				eventsGroup.Key.Value,
				eventsGroup.First().Asset.Name.Value,
				eventsGroup.Count(),
				eventsGroup.Count(ev => ev.Code.Group == EventCodeGroup.Alarm && ev.Code.IsActive)
			))
			.ToListAsync(cancellationToken);

		var data = new Data(summary, extra.From, extra.To);

		return Result.Success(JsonSerializer.Serialize(data));
	}
}
