using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Monitoring.Common;
using Monitoring.Controllers;
using Monitoring.Core.Domain.FireDepartmentManagement;
using Monitoring.Core.Domain.UserPrimitives;
using Monitoring.Extensions;
using Monitoring.Features.FireDepartmentAccesses.CreateFireDepartmentAccess;
using Monitoring.Features.FireDepartmentAccesses.GetFireDepartmentAccessList;
using Monitoring.Features.FireDepartmentAccesses.UpdateFireDepartmentAccess;
using Monitoring.Infrastructure.Data;
using Monitoring.Models;

namespace Monitoring.Features.FireDepartmentAccesses;

[Route("api/fire-department-accesses")]
[Authorize(Roles = Roles.Administrator)]
public class FireDepartmentAccessesController(DataContext dataContext) : BaseController
{
	[HttpGet(Name = "GetFireDepartmentAccessList")]
	[ProducesResponseType<TableDataResponseModel<FireDepartmentAccessModel>>(
		StatusCodes.Status200OK
	)]
	public async Task<IActionResult> GetList(
		[FromQuery] GetFireDepartmentAccessListRequest request,
		CancellationToken cancellationToken
	)
	{
		var query = dataContext.FireDepartmentAccesses.AsQueryable();

		if (request.DepartmentId.HasValue)
		{
			var deptId = new FireDepartmentId(request.DepartmentId.Value);
			query = query.Where(a => a.DepartmentId == deptId);
		}

		if (request.UserId.HasValue)
		{
			var userId = new UserId(request.UserId.Value);
			query = query.Where(a => a.UserId == userId);
		}

		var list = await query
			.AsNoTracking()
			.Select(FireDepartmentAccessModel.FromEntity)
			.GetTable(request, cancellationToken);

		return Ok(list);
	}

	[HttpGet("{id:guid}", Name = "GetFireDepartmentAccess")]
	[ProducesResponseType<string>(StatusCodes.Status404NotFound)]
	[ProducesResponseType<FireDepartmentAccessModel>(StatusCodes.Status200OK)]
	public async Task<IActionResult> Get(Guid id, CancellationToken cancellationToken)
	{
		var accessId = new FireDepartmentAccessId(id);
		var model = await dataContext
			.FireDepartmentAccesses.Where(a => a.Id == accessId)
			.Select(FireDepartmentAccessModel.FromEntity)
			.FirstOrDefaultAsync(cancellationToken);
		if (model is null)
		{
			return NotFound("Access not found");
		}
		return Ok(model);
	}

	[HttpPost(Name = "CreateFireDepartmentAccess")]
	[ProducesResponseType<string>(StatusCodes.Status404NotFound)]
	[ProducesResponseType<string>(StatusCodes.Status409Conflict)]
	[ProducesResponseType<FireDepartmentAccessModel>(StatusCodes.Status200OK)]
	public async Task<IActionResult> Create(
		CreateFireDepartmentAccessRequest request,
		CancellationToken cancellationToken
	)
	{
		var deptId = new FireDepartmentId(request.DepartmentId);
		var userId = new UserId(request.UserId);

		var deptExists = await dataContext.FireDepartments.AnyAsync(
			fd => fd.Id == deptId,
			cancellationToken
		);
		if (!deptExists)
		{
			return NotFound("Fire department not found");
		}

		var userExists = await dataContext.Users.AnyAsync(u => u.Id == userId, cancellationToken);
		if (!userExists)
		{
			return NotFound("User not found");
		}

		var dup = await dataContext.FireDepartmentAccesses.AnyAsync(
			a => a.DepartmentId == deptId && a.UserId == userId,
			cancellationToken
		);
		if (dup)
		{
			return Conflict("Access already exists");
		}

		var access = FireDepartmentAccess.Create(userId, deptId);
		await dataContext.FireDepartmentAccesses.AddAsync(access, cancellationToken);
		await dataContext.SaveChangesAsync(cancellationToken);

		var model = await dataContext
			.FireDepartmentAccesses.Where(a => a.Id == access.Id)
			.Select(FireDepartmentAccessModel.FromEntity)
			.FirstAsync(cancellationToken);
		return Ok(model);
	}

	[HttpPut("{id:guid}", Name = "UpdateFireDepartmentAccess")]
	[ProducesResponseType<string>(StatusCodes.Status404NotFound)]
	[ProducesResponseType<string>(StatusCodes.Status409Conflict)]
	[ProducesResponseType(StatusCodes.Status204NoContent)]
	public async Task<IActionResult> Update(
		Guid id,
		UpdateFireDepartmentAccessRequest request,
		CancellationToken cancellationToken
	)
	{
		var accessId = new FireDepartmentAccessId(id);
		var access = await dataContext.FireDepartmentAccesses.FirstOrDefaultAsync(
			a => a.Id == accessId,
			cancellationToken
		);
		if (access is null)
		{
			return NotFound("Access not found");
		}

		var deptId = new FireDepartmentId(request.DepartmentId);
		var userId = new UserId(request.UserId);

		var deptExists = await dataContext.FireDepartments.AnyAsync(
			fd => fd.Id == deptId,
			cancellationToken
		);
		if (!deptExists)
		{
			return NotFound("Fire department not found");
		}

		var userExists = await dataContext.Users.AnyAsync(u => u.Id == userId, cancellationToken);
		if (!userExists)
		{
			return NotFound("User not found");
		}

		var dup = await dataContext.FireDepartmentAccesses.AnyAsync(
			a => a.DepartmentId == deptId && a.UserId == userId && a.Id != accessId,
			cancellationToken
		);
		if (dup)
		{
			return Conflict("Access already exists");
		}

		typeof(FireDepartmentAccess)
			.GetProperty(nameof(FireDepartmentAccess.DepartmentId))!
			.SetValue(access, deptId);
		typeof(FireDepartmentAccess)
			.GetProperty(nameof(FireDepartmentAccess.UserId))!
			.SetValue(access, userId);

		await dataContext.SaveChangesAsync(cancellationToken);
		return NoContent();
	}

	[HttpDelete("{id:guid}", Name = "DeleteFireDepartmentAccess")]
	[ProducesResponseType<string>(StatusCodes.Status404NotFound)]
	[ProducesResponseType(StatusCodes.Status204NoContent)]
	public async Task<IActionResult> Delete(Guid id, CancellationToken cancellationToken)
	{
		var accessId = new FireDepartmentAccessId(id);
		var access = await dataContext.FireDepartmentAccesses.FirstOrDefaultAsync(
			a => a.Id == accessId,
			cancellationToken
		);
		if (access is null)
		{
			return NotFound("Access not found");
		}

		dataContext.FireDepartmentAccesses.Remove(access);
		await dataContext.SaveChangesAsync(cancellationToken);
		return NoContent();
	}
}
