using FluentValidation;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Monitoring.Common;
using Monitoring.Controllers;
using Monitoring.Core.Domain.EventManagement;
using Monitoring.Infrastructure.Data;

namespace Monitoring.Features.Analytics;

[Route("api/analytics")]
[Authorize(Roles = $"{Roles.Manager},{Roles.Watcher}")]
public class AnalyticsController(DataContext dataContext) : BaseController
{
	[HttpGet("asset-online-status", Name = "GetAssetOnlineStatus")]
	[ProducesResponseType<OnlineAssetStatusResponseModel>(StatusCodes.Status200OK)]
	public async Task<IActionResult> GetAssetOnlineStatus()
	{
		var assets = await dataContext
			.Assets.Where(x => x.Groups.Any(x => x.Accesses.Any(x => x.Member.UserId == UserId)))
			.AsNoTracking()
			.ToListAsync();
		return Ok(new OnlineAssetStatusResponseModel(assets.Count, assets.Count(x => x.IsOnline)));
	}

	[HttpGet("count-of-events", Name = "GetCountOfEvents")]
	[ProducesResponseType<CountOfEventsResponseModel>(StatusCodes.Status200OK)]
	public async Task<IActionResult> GetCountOfEvents(
		[FromQuery] CountOfEventsRequestModel request,
		CancellationToken cancellationToken
	)
	{
		var events = await GetEventsAsync(request, cancellationToken);
		var alarms = await GetAlarmsAsync(request, cancellationToken);
		var faults = await GetFaultsAsync(request, cancellationToken);
		var operatorActions = await GetUserActionsAsync(request, cancellationToken);

		return Ok(
			new CountOfEventsResponseModel()
			{
				Events = [.. events],
				Alarms = [.. alarms],
				Faults = [.. faults],
				OperatorActions = [.. operatorActions],
			}
		);
	}

	private async Task<IEnumerable<CountOfEventsItemResponseModel>> GetEventsAsync(
		CountOfEventsRequestModel request,
		CancellationToken cancellationToken
	)
	{
		var query = dataContext
			.Events.Where(x =>
				x.Asset.Groups.Any(x => x.Accesses.Any(x => x.Member.UserId == UserId))
			)
			.AsNoTracking();

		if (request.Type == CountOfEventsType.Day)
		{
			query = query.Where(x => x.CreatedAt >= DateTime.UtcNow.AddDays(-1));
		}
		else if (request.Type == CountOfEventsType.Week)
		{
			query = query.Where(x => x.CreatedAt >= DateTime.UtcNow.AddDays(-7));
		}
		else if (request.Type == CountOfEventsType.Month)
		{
			query = query.Where(x => x.CreatedAt >= DateTime.UtcNow.AddMonths(-1));
		}
		else if (request.Type == CountOfEventsType.Year)
		{
			query = query.Where(x => x.CreatedAt >= DateTime.UtcNow.AddYears(-1));
		}

		return await query
			.GroupBy(x => x.Asset)
			.Select(x => new CountOfEventsItemResponseModel(x.Count(), x.Key.Name.Value))
			.ToListAsync(cancellationToken);
	}

	private async Task<IEnumerable<CountOfEventsItemResponseModel>> GetAlarmsAsync(
		CountOfEventsRequestModel request,
		CancellationToken cancellationToken
	)
	{
		var query = dataContext
			.Events.Where(x =>
				x.Asset.Groups.Any(x => x.Accesses.Any(x => x.Member.UserId == UserId))
				&& x.Code.Group == EventCodeGroup.Alarm
			)
			.AsNoTracking();

		if (request.Type == CountOfEventsType.Day)
		{
			query = query.Where(x => x.CreatedAt >= DateTime.UtcNow.AddDays(-1));
		}
		else if (request.Type == CountOfEventsType.Week)
		{
			query = query.Where(x => x.CreatedAt >= DateTime.UtcNow.AddDays(-7));
		}
		else if (request.Type == CountOfEventsType.Month)
		{
			query = query.Where(x => x.CreatedAt >= DateTime.UtcNow.AddMonths(-1));
		}
		else if (request.Type == CountOfEventsType.Year)
		{
			query = query.Where(x => x.CreatedAt >= DateTime.UtcNow.AddYears(-1));
		}

		return await query
			.GroupBy(x => x.Asset)
			.Select(x => new CountOfEventsItemResponseModel(x.Count(), x.Key.Name.Value))
			.ToListAsync(cancellationToken);
	}

	private async Task<IEnumerable<CountOfEventsItemResponseModel>> GetFaultsAsync(
		CountOfEventsRequestModel request,
		CancellationToken cancellationToken
	)
	{
		var query = dataContext
			.Events.Where(x =>
				x.Asset.Groups.Any(x => x.Accesses.Any(x => x.Member.UserId == UserId))
				&& x.Code.Group == EventCodeGroup.Fault
			)
			.AsNoTracking();

		if (request.Type == CountOfEventsType.Day)
		{
			query = query.Where(x => x.CreatedAt >= DateTime.UtcNow.AddDays(-1));
		}
		else if (request.Type == CountOfEventsType.Week)
		{
			query = query.Where(x => x.CreatedAt >= DateTime.UtcNow.AddDays(-7));
		}
		else if (request.Type == CountOfEventsType.Month)
		{
			query = query.Where(x => x.CreatedAt >= DateTime.UtcNow.AddMonths(-1));
		}
		else if (request.Type == CountOfEventsType.Year)
		{
			query = query.Where(x => x.CreatedAt >= DateTime.UtcNow.AddYears(-1));
		}

		return await query
			.GroupBy(x => x.Asset)
			.Select(x => new CountOfEventsItemResponseModel(x.Count(), x.Key.Name.Value))
			.ToListAsync(cancellationToken);
	}

	private async Task<IEnumerable<CountOfEventsItemResponseModel>> GetUserActionsAsync(
		CountOfEventsRequestModel request,
		CancellationToken cancellationToken
	)
	{
		var query = dataContext
			.UserLogs.Where(x =>
				x.Asset.Groups.Any(x => x.Accesses.Any(x => x.Member.UserId == UserId))
			)
			.AsNoTracking();

		if (request.Type == CountOfEventsType.Day)
		{
			query = query.Where(x => x.CreatedAt >= DateTime.UtcNow.AddDays(-1));
		}
		else if (request.Type == CountOfEventsType.Week)
		{
			query = query.Where(x => x.CreatedAt >= DateTime.UtcNow.AddDays(-7));
		}
		else if (request.Type == CountOfEventsType.Month)
		{
			query = query.Where(x => x.CreatedAt >= DateTime.UtcNow.AddMonths(-1));
		}
		else if (request.Type == CountOfEventsType.Year)
		{
			query = query.Where(x => x.CreatedAt >= DateTime.UtcNow.AddYears(-1));
		}

		return await query
			.GroupBy(x => x.Asset)
			.Select(x => new CountOfEventsItemResponseModel(x.Count(), x.Key.Name.Value))
			.ToListAsync(cancellationToken);
	}
}
