using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;

using Monitoring.Common;
using Monitoring.Controllers;
using Monitoring.Core.Domain;
using Monitoring.Core.Domain.AssetManagement;
using Monitoring.Core.Domain.EventManagement;
using Monitoring.Extensions;
using Monitoring.Features.LockedEventCodes.CreateLockedEventCode;
using Monitoring.Features.LockedEventCodes.GetLockedEventCodeList;
using Monitoring.Infrastructure.Data;
using Monitoring.Models;
using Monitoring.Services;

namespace Monitoring.Features.LockedEventCodes;
[Route("api/organizations/{organizationId}/assets/{assetId}/locked-event-codes")]
[Authorize(Roles = $"{Roles.Manager},{Roles.Watcher}")]
public class LockedEventCodesController(
	DataContext dataContext,
	INotificationService notificationService,
	UsersService usersService,
	ILogger<LockedEventCodesController> logger
) : BaseController
{
	public record LockedEventCodeModel(
		Guid Id,
		Guid AssetId,
		Guid EventCodeId
	);

	[HttpGet(Name = "GetLockedEventCodeList")]
	[ProducesResponseType<TableDataResponseModel<LockedEventCodeModel>>(StatusCodes.Status200OK)]
	public async Task<IActionResult> GetList(
		[FromQuery] GetLockedEventCodeListRequest request,
		[FromRoute] Guid organizationId,
		[FromRoute] Guid assetId,
		CancellationToken cancellationToken)
	{
		AssetId entityAssetId = new(assetId);

		return Ok(
			await dataContext.LockedEventCodes
				.Where(x => x.AssetId == entityAssetId &&
					x.Asset.Organization.Members.Any(y => y.UserId == UserId))
				.OrderBy(x => x.EventCode.Value)
				.GetTable(request, x => new LockedEventCodeModel(
					x.Id.Value,
					x.AssetId.Value,
					x.EventCodeId.Value
				), cancellationToken)
		);
	}

	[HttpPost(Name = "CreateLockedEventCode")]
	[ProducesResponseType<LockedEventCodeModel>(StatusCodes.Status201Created)]
	public async Task<IActionResult> Create(
		[FromBody] CreateLockedEventCodeRequest request,
		DataContext dataContext,
		CancellationToken cancellationToken)
	{
		logger.LogInformation("Try to create event code: EventCodeId = {EventCodeId}, AssetId = {AssetId}", request.EventCodeId, request.AssetId);

		var entity = new LockedEventCode(
			new LockedEventCodeId(Guid.NewGuid()),
			new EventCodeId(request.EventCodeId),
			new AssetId(request.AssetId)
		);

		_ = await dataContext.LockedEventCodes.AddAsync(entity, cancellationToken);
		_ = await dataContext.SaveChangesAsync(cancellationToken);

		var organizationId = await dataContext.Assets
			.AsNoTracking()
			.Where(x => x.Id == entity.AssetId)
			.Select(x => x.OrganizationId)
			.FirstOrDefaultAsync(cancellationToken);

		logger.LogInformation("Event code created: EventCodeId = {EventCodeId}, AssetId = {AssetId}", request.EventCodeId, request.AssetId);

		return Created($"/api/organizations/{organizationId}/assets/{request.AssetId}/locked-event-codes/{entity.Id}", new LockedEventCodeModel(
			entity.Id.Value,
			entity.AssetId.Value,
			entity.EventCodeId.Value
		));
	}

	[HttpDelete("{id:guid}", Name = "DeleteLockedEventCode")]
	[ProducesResponseType(StatusCodes.Status204NoContent)]
	[ProducesResponseType<string>(StatusCodes.Status404NotFound)]
	public async Task<IActionResult> Delete(Guid id, CancellationToken cancellationToken)
	{
		logger.LogInformation("Try to delete event code: {Id}", id);

		var entity = await dataContext.LockedEventCodes
			.Where(x => x.Id == new LockedEventCodeId(id))
			.FirstOrDefaultAsync(cancellationToken);

		if (entity is null)
		{
			return NotFound(nameof(LockedEventCode));
		}

		dataContext.LockedEventCodes.Remove(entity);
		_ = await dataContext.SaveChangesAsync(cancellationToken);

		logger.LogInformation("Event code deleted: {Id}", id);

		return NoContent();
	}

	[HttpGet("{id:guid}", Name = "GetLockedEventCode")]
	[ProducesResponseType<string>(StatusCodes.Status404NotFound)]
	[ProducesResponseType<LockedEventCodeModel>(StatusCodes.Status200OK)]
	public async Task<IActionResult> Get(Guid id, CancellationToken cancellationToken)
	{
		logger.LogInformation("Try to get event code: {Id}", id);

		var entity = await dataContext.LockedEventCodes
			.AsNoTracking()
			.Where(x => x.Id == new LockedEventCodeId(id))
			.FirstOrDefaultAsync(cancellationToken);

		if (entity is null)
		{
			return NotFound(nameof(LockedEventCode));
		}

		logger.LogInformation("Event code deleted: {Id}", id);

		return Ok(new LockedEventCodeModel(
			entity.Id.Value,
			entity.AssetId.Value,
			entity.EventCodeId.Value
		));
	}
}
