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.Extensions;
using Monitoring.Features.ResponsiblePersons.CreateResponsiblePerson;
using Monitoring.Features.ResponsiblePersons.GetResponsiblePersonList;
using Monitoring.Features.ResponsiblePersons.UpdateResponsiblePerson;
using Monitoring.Infrastructure.Data;
using Monitoring.Models;
using Monitoring.Services;

namespace Monitoring.Features.ResponsiblePersons;
[Route("api/responsible-persons")]
[Authorize(Roles = $"{Roles.Manager},{Roles.Watcher}")]
public class ResponsiblePersonsController(
	DataContext dataContext,
	INotificationService notificationService,
	UsersService usersService
) : BaseController
{
	[HttpGet(Name = "GetResponsiblePersonList")]
	[ProducesResponseType<TableDataResponseModel<ResponsiblePersonModel>>(StatusCodes.Status200OK)]
	public async Task<IActionResult> GetList(
		[FromQuery] GetResponsiblePersonListRequest request,
		CancellationToken cancellationToken
	)
	{
		var assetId = new AssetId(request.AssetId);
		var asset = await dataContext.Assets
			.FirstOrDefaultAsync(x => x.Id == assetId &&
				x.Organization!.Members!.Any(y => y.UserId == UserId), cancellationToken);
		return asset is null
			? NotFound(nameof(Asset))
			: Ok(await dataContext.ResponsiblePersons
			.Where(x => x.Asset == asset)
			.GetTable(request, ResponsiblePersonModel.FromEntity, cancellationToken));
	}

	[HttpGet("{id:guid}", Name = "GetResponsiblePerson")]
	[ProducesResponseType<ResponsiblePersonModel>(StatusCodes.Status200OK)]
	[ProducesResponseType<string>(StatusCodes.Status404NotFound)]
	public async Task<IActionResult> Get(Guid id, CancellationToken cancellationToken)
	{
		var model = await dataContext.ResponsiblePersons
			.Where(x => x.Id == id &&
				x.Asset!.Organization!.Members!.Any(x => x.UserId == UserId))
			.Select(ResponsiblePersonModel.FromEntity)
			.FirstOrDefaultAsync(cancellationToken);
		return model is null ? NotFound(nameof(ResponsiblePerson)) : Ok(model);
	}

	[HttpPost(Name = "CreateResponsiblePerson")]
	[Authorize(Roles = Roles.Manager)]
	[ProducesResponseType<ResponsiblePersonModel>(StatusCodes.Status200OK)]
	[ProducesResponseType<string>(StatusCodes.Status404NotFound)]
	public async Task<IActionResult> Create(
		[FromBody] CreateResponsiblePersonRequest request,
		CancellationToken cancellationToken
	)
	{
		var assetId = new AssetId(request.AssetId);
		var asset = await dataContext.Assets
			.FirstOrDefaultAsync(x => x.Id == assetId &&
				x.Organization!.Members!.Any(member => member.UserId == UserId), cancellationToken);
		if (asset is null)
		{
			return NotFound(nameof(Asset));
		}

		ResponsiblePerson responsiblePerson = new()
		{
			Name = request.Name,
			Key = request.Key,
			Phone1 = request.Phone1,
			Phone2 = request.Phone2,
			Note = request.Note,
			Asset = asset,
		};

		_ = await dataContext.ResponsiblePersons.AddAsync(responsiblePerson, cancellationToken);
		_ = await dataContext.SaveChangesAsync(cancellationToken);

		var responsiblePersonModel = await dataContext.ResponsiblePersons
			.Where(x => x.Id == responsiblePerson.Id)
			.Select(ResponsiblePersonModel.FromEntity)
			.FirstAsync(cancellationToken);

		await notificationService.NotifyUsers(
			await usersService.GetUserIdListByOrganization(asset.OrganizationId, cancellationToken),
			"RESPONSIBLE-PERSONS.CREATED",
			responsiblePersonModel,
			cancellationToken
		);

		return Ok(responsiblePersonModel);
	}

	[HttpPut("{id:guid}", Name = "UpdateResponsiblePerson")]
	[Authorize(Roles = Roles.Manager)]
	[ProducesResponseType<ResponsiblePersonModel>(StatusCodes.Status200OK)]
	[ProducesResponseType<string>(StatusCodes.Status404NotFound)]
	public async Task<IActionResult> Update(
			Guid id,
			[FromBody] UpdateResponsiblePersonRequest request,
			CancellationToken cancellationToken
		)
	{
		var responsiblePerson = await dataContext.ResponsiblePersons
			.Include(x => x.Asset!)
			.FirstOrDefaultAsync(x => x.Id == id &&
				x.Asset!.Organization!.Members!.Any(member => member.UserId == UserId), cancellationToken);
		if (responsiblePerson is null)
		{
			return NotFound(nameof(ResponsiblePerson));
		}

		responsiblePerson.Name = request.Name;
		responsiblePerson.Key = request.Key;
		responsiblePerson.Phone1 = request.Phone1;
		responsiblePerson.Phone2 = request.Phone2;
		responsiblePerson.Note = request.Note;

		_ = await dataContext.SaveChangesAsync(cancellationToken);

		var responsiblePersonModel = await dataContext.ResponsiblePersons
			.Where(x => x.Id == responsiblePerson.Id)
			.Select(ResponsiblePersonModel.FromEntity)
			.FirstAsync(cancellationToken);

		await notificationService.NotifyUsers(
			await usersService.GetUserIdListByOrganization(responsiblePerson.Asset!.OrganizationId, cancellationToken),
			"RESPONSIBLE-PERSONS.UPDATED",
			responsiblePersonModel,
			cancellationToken
		);

		return Ok(responsiblePersonModel);
	}

	[HttpDelete("{id:guid}", Name = "DeleteResponsiblePerson")]
	[Authorize(Roles = Roles.Manager)]
	[ProducesResponseType(StatusCodes.Status204NoContent)]
	[ProducesResponseType<string>(StatusCodes.Status404NotFound)]
	public async Task<IActionResult> Delete(Guid id, CancellationToken cancellationToken)
	{
		var responsiblePerson = await dataContext.ResponsiblePersons
			.Include(x => x.Asset!)
			.FirstOrDefaultAsync(x => x.Id == id &&
				x.Asset!.Organization!.Members!.Any(member => member.UserId == UserId), cancellationToken);
		if (responsiblePerson is null)
		{
			return NotFound(nameof(ResponsiblePerson));
		}

		var organizationId = responsiblePerson.Asset.OrganizationId;

		var responsiblePersonModel = await dataContext.ResponsiblePersons
			.Where(x => x.Id == responsiblePerson.Id)
			.Select(ResponsiblePersonModel.FromEntity)
			.FirstAsync(cancellationToken);

		_ = dataContext.ResponsiblePersons.Remove(responsiblePerson);
		_ = await dataContext.SaveChangesAsync(cancellationToken);

		await notificationService.NotifyUsers(
			await usersService.GetUserIdListByOrganization(organizationId, cancellationToken),
			"RESPONSIBLE-PERSONS.DELETED",
			responsiblePersonModel,
			cancellationToken
		);

		return NoContent();
	}
}
