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

using Monitoring.Common;
using Monitoring.Controllers;
using Monitoring.Core.Domain.OrganizationManagement;
using Monitoring.Extensions;
using Monitoring.Features.Organizations.CreateOrganization;
using Monitoring.Features.Organizations.GetOrganizationList;
using Monitoring.Features.Organizations.UpdateOrganization;
using Monitoring.Infrastructure.Data;
using Monitoring.Models;
using Monitoring.Services;

namespace Monitoring.Features.Organizations;

[Route("api/[controller]")]
[Authorize(Roles = $"{Roles.Manager},{Roles.Watcher},{Roles.Administrator}")]
public class OrganizationsController(
	DataContext dataContext,
	INotificationService notificationService
) : BaseController
{
	[HttpGet(Name = "GetOrganizationList")]
	[ProducesResponseType<TableDataResponseModel<OrganizationModel>>(StatusCodes.Status200OK)]
	public async Task<IActionResult> GetList(
		[FromQuery] GetOrganizationListRequest request,
		CancellationToken cancellationToken)
	{
		IQueryable<Organization> query = dataContext.Organizations;
		if (Role != Roles.Administrator)
		{
			query = query.Where(x => x.Members.Any(x => x.UserId == UserId));
		}

		return Ok(await query
			.GetTable(request, OrganizationModel.Projection, cancellationToken));
	}

	[HttpGet("{id:guid}", Name = "GetOrganization")]
	[ProducesResponseType<OrganizationModel>(StatusCodes.Status200OK)]
	[ProducesResponseType(StatusCodes.Status404NotFound)]
	public async Task<IActionResult> Get(Guid id, CancellationToken cancellationToken)
	{
		IQueryable<Organization> organizations = dataContext.Organizations;
		if (Role == Roles.Manager)
		{
			organizations = organizations.Where(x => x.Members!.Any(x => x.UserId == UserId));
		}

		var organizationId = new OrganizationId(id);
		var model = await organizations
			.Where(x => x.Id == organizationId)
			.Select(OrganizationModel.Projection)
			.FirstOrDefaultAsync(cancellationToken);
		return model is null ? NotFound() : Ok(model);
	}

	[HttpPost(Name = "CreateOrganization")]
	[Authorize(Roles = Roles.Administrator)]
	[ProducesResponseType<OrganizationModel>(StatusCodes.Status200OK)]
	[ProducesResponseType<ProblemDetails>(StatusCodes.Status409Conflict)]
	public async Task<IActionResult> Create(CreateOrganizationRequest request,
	CancellationToken cancellationToken)
	{
		var name = OrganizationName.Create(request.Name);
		var existingOrganization = await dataContext.Organizations
			.FirstOrDefaultAsync(o => o.Name == name, cancellationToken);
		if (existingOrganization is not null)
		{
			return Conflict();
		}

		var description = OrganizationDescription.Create(request.Description);
		var organization = Organization.Create(name, description);

		_ = await dataContext.Organizations.AddAsync(organization, cancellationToken);
		_ = await dataContext.SaveChangesAsync(cancellationToken);

		var model = await dataContext.Organizations
			.Where(x => x.Id == organization.Id)
			.Select(OrganizationModel.Projection)
			.FirstAsync(cancellationToken);

		await notificationService.NotifyAdministrators(
			model,
			"ORGANIZATIONS.CREATED",
			cancellationToken
		);

		return Ok(model);
	}

	[HttpPut("{id:guid}", Name = "UpdateOrganization")]
	[Authorize(Roles = Roles.Administrator)]
	[ProducesResponseType(StatusCodes.Status204NoContent)]
	[ProducesResponseType(StatusCodes.Status404NotFound)]
	public async Task<IActionResult> Update(Guid id, UpdateOrganizationRequest request,
	CancellationToken cancellationToken)
	{
		var organizationId = new OrganizationId(id);
		var organization = await dataContext.Organizations
			 .FirstOrDefaultAsync(o => o.Id == organizationId, cancellationToken);
		if (organization is null)
		{
			return NotFound();
		}

		organization.Name = OrganizationName.Create(request.Name);
		organization.Description = OrganizationDescription.Create(request.Description);

		_ = await dataContext.SaveChangesAsync(cancellationToken);

		await notificationService.NotifyAdministrators(organization,
			"ORGANIZATIONS.UPDATED", cancellationToken);

		return NoContent();
	}

	[HttpDelete("{id:guid}", Name = "DeleteOrganization")]
	[Authorize(Roles = Roles.Administrator)]
	[ProducesResponseType(StatusCodes.Status204NoContent)]
	[ProducesResponseType(StatusCodes.Status404NotFound)]
	public async Task<IActionResult> Handler(
		Guid id,
		CancellationToken cancellationToken
	)
	{
		var organizationId = new OrganizationId(id);
		var organization = await dataContext.Organizations
			 .FirstOrDefaultAsync(o => o.Id == organizationId, cancellationToken);
		if (organization is null)
		{
			return NotFound();
		}

		_ = dataContext.Organizations.Remove(organization);
		_ = await dataContext.SaveChangesAsync(cancellationToken);

		await notificationService.NotifyAdministrators(organization,
			"ORGANIZATIONS.DELETED", cancellationToken);

		return NoContent();
	}
}

