using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Monitoring.Common;
using Monitoring.Controllers;
using Monitoring.Core.Domain.FireDepartmentManagement;
using Monitoring.Extensions;
using Monitoring.Features.FireDepartments.CreateFireDepartment;
using Monitoring.Features.FireDepartments.GetFireDepartmentList;
using Monitoring.Features.FireDepartments.UpdateFireDepartment;
using Monitoring.Infrastructure.Data;
using Monitoring.Models;

namespace Monitoring.Features.FireDepartments;

[Route("api/fire-departments")]
[Authorize(Roles = $"{Roles.Manager},{Roles.Watcher}")]
public class FireDepartmentsController(DataContext dataContext) : BaseController
{
	[HttpGet(Name = "GetFireDepartmentList")]
	[ProducesResponseType<TableDataResponseModel<FireDepartmentModel>>(StatusCodes.Status200OK)]
	public async Task<IActionResult> GetList(
		[FromQuery] GetFireDepartmentListRequest request,
		CancellationToken cancellationToken
	)
	{
		var query = dataContext.FireDepartments.AsQueryable();

		if (Role == Roles.Watcher)
		{
			// Watcher видит только те ДЧС, к которым у него есть доступ
			query = query.Where(fd =>
				dataContext.FireDepartmentAccesses.Any(a =>
					a.DepartmentId == fd.Id && a.UserId == UserId
				)
			);
		}

		if (!string.IsNullOrWhiteSpace(request.Search))
		{
			var search = request.Search.Trim().ToLowerInvariant();
			query = query.Where(fd =>
				fd.Name.Value.ToLower().Contains(search)
				|| fd.Address.Value.ToLower().Contains(search)
			);
		}

		var list = await query
			.AsNoTracking()
			.OrderBy(fd => fd.Name.Value)
			.Select(FireDepartmentModel.FromEntity)
			.GetTable(request, cancellationToken);

		return Ok(list);
	}

	[HttpGet("{id:guid}", Name = "GetFireDepartment")]
	[ProducesResponseType<string>(StatusCodes.Status404NotFound)]
	[ProducesResponseType<FireDepartmentModel>(StatusCodes.Status200OK)]
	public async Task<IActionResult> Get(Guid id, CancellationToken cancellationToken)
	{
		var deptId = new FireDepartmentId(id);

		if (Role == Roles.Watcher)
		{
			var hasAccess = await dataContext.FireDepartmentAccesses.AnyAsync(
				a => a.DepartmentId == deptId && a.UserId == UserId,
				cancellationToken
			);
			if (!hasAccess)
			{
				return NotFound("Fire department not found");
			}
		}

		var model = await dataContext
			.FireDepartments.Where(fd => fd.Id == deptId)
			.Select(FireDepartmentModel.FromEntity)
			.FirstOrDefaultAsync(cancellationToken);

		if (model is null)
		{
			return NotFound("Fire department not found");
		}

		return Ok(model);
	}

	[HttpPost(Name = "CreateFireDepartment")]
	[Authorize(Roles = Roles.Manager)]
	[ProducesResponseType<FireDepartmentModel>(StatusCodes.Status200OK)]
	public async Task<IActionResult> Create(
		CreateFireDepartmentRequest request,
		CancellationToken cancellationToken
	)
	{
		var name = FireDepartmentName.Create(request.Name);
		var address = FireDepartmentAddress.Create(request.Address);

		var exists = await dataContext.FireDepartments.AnyAsync(
			fd => fd.Name == name,
			cancellationToken
		);
		if (exists)
		{
			return Conflict("Fire department with the same name already exists");
		}

		var dept = FireDepartment.Create(name, address);
		await dataContext.FireDepartments.AddAsync(dept, cancellationToken);
		await dataContext.SaveChangesAsync(cancellationToken);

		var model = await dataContext
			.FireDepartments.Where(fd => fd.Id == dept.Id)
			.Select(FireDepartmentModel.FromEntity)
			.FirstAsync(cancellationToken);

		return Ok(model);
	}

	[HttpPut("{id:guid}", Name = "UpdateFireDepartment")]
	[Authorize(Roles = Roles.Manager)]
	[ProducesResponseType<string>(StatusCodes.Status404NotFound)]
	[ProducesResponseType<string>(StatusCodes.Status409Conflict)]
	[ProducesResponseType(StatusCodes.Status204NoContent)]
	public async Task<IActionResult> Update(
		Guid id,
		UpdateFireDepartmentRequest request,
		CancellationToken cancellationToken
	)
	{
		var deptId = new FireDepartmentId(id);
		var dept = await dataContext.FireDepartments.FirstOrDefaultAsync(
			fd => fd.Id == deptId,
			cancellationToken
		);
		if (dept is null)
		{
			return NotFound("Fire department not found");
		}

		var newName = FireDepartmentName.Create(request.Name);
		var nameConflict = await dataContext.FireDepartments.AnyAsync(
			fd => fd.Name == newName && fd.Id != deptId,
			cancellationToken
		);
		if (nameConflict)
		{
			return Conflict("Fire department with the same name already exists");
		}

		// Присваиваем value-объекты
		typeof(FireDepartment).GetProperty(nameof(FireDepartment.Name))!.SetValue(dept, newName);
		var newAddress = FireDepartmentAddress.Create(request.Address);
		typeof(FireDepartment)
			.GetProperty(nameof(FireDepartment.Address))!
			.SetValue(dept, newAddress);

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

	[HttpDelete("{id:guid}", Name = "DeleteFireDepartment")]
	[Authorize(Roles = Roles.Manager)]
	[ProducesResponseType<string>(StatusCodes.Status404NotFound)]
	[ProducesResponseType(StatusCodes.Status204NoContent)]
	public async Task<IActionResult> Delete(Guid id, CancellationToken cancellationToken)
	{
		var deptId = new FireDepartmentId(id);
		var dept = await dataContext.FireDepartments.FirstOrDefaultAsync(
			fd => fd.Id == deptId,
			cancellationToken
		);
		if (dept is null)
		{
			return NotFound("Fire department not found");
		}

		dataContext.FireDepartments.Remove(dept);
		await dataContext.SaveChangesAsync(cancellationToken);
		return NoContent();
	}
}
