using FluentValidation;
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.CallManagement;
using Monitoring.Core.Domain.EventManagement;
using Monitoring.Extensions;
using Monitoring.Features.Calls.CreateCall;
using Monitoring.Features.Calls.GetCallList;
using Monitoring.Features.Calls.UpdateCall;
using Monitoring.Infrastructure.Data;
using Monitoring.Models;

namespace Monitoring.Features.Calls;

[Route("api/calls")]
[Authorize(Roles = $"{Roles.Manager},{Roles.Watcher}")]
public class CallsController(DataContext dataContext) : BaseController
{
	[HttpGet(Name = "GetCallList")]
	[ProducesResponseType<TableDataResponseModel<CallModel>>(StatusCodes.Status200OK)]
	public async Task<IActionResult> GetList(
		[FromQuery] GetCallListRequest request,
		CancellationToken cancellationToken
	)
	{
		var query = dataContext.Calls.Where(c =>
			c.AlarmScope.Asset!.Organization.Members.Any(m => m.UserId == UserId)
		);

		if (Role == Roles.Watcher)
		{
			query = query.Where(c =>
				c.AlarmScope.Asset!.Groups.Any(g => g.Accesses.Any(a => a.Member.UserId == UserId))
			);
		}

		if (request.AssetId.HasValue)
		{
			var assetId = new AssetId(request.AssetId.Value);
			query = query.Where(c => c.AlarmScope.AssetId == assetId);
		}

		if (request.AlarmScopeId.HasValue)
		{
			var alarmScopeIdValue = new AlarmScopeId(request.AlarmScopeId.Value);
			query = query.Where(c => c.AlarmScopeId == alarmScopeIdValue);
		}

		if (!string.IsNullOrWhiteSpace(request.Search))
		{
			var search = request.Search.Trim().ToLowerInvariant();
			query = query.Where(c => c.Commentary.Value.ToLower().Contains(search));
		}

		var list = await query
			.AsNoTracking()
			.OrderByDescending(c => c.StartedAt)
			.Select(CallModel.FromEntity)
			.GetTable(request, cancellationToken);

		return Ok(list);
	}

	[HttpGet("{id:guid}", Name = "GetCall")]
	[ProducesResponseType<string>(StatusCodes.Status404NotFound)]
	[ProducesResponseType<CallModel>(StatusCodes.Status200OK)]
	public async Task<IActionResult> Get(Guid id, CancellationToken cancellationToken)
	{
		var callId = new CallId(id);
		var exists = await dataContext.Calls.AnyAsync(
			c =>
				c.Id == callId
				&& c.AlarmScope.Asset!.Organization.Members.Any(m => m.UserId == UserId),
			cancellationToken
		);
		if (!exists)
		{
			return NotFound("Call not found");
		}
		if (Role == Roles.Watcher)
		{
			var hasAccess = await dataContext
				.Calls.Where(c => c.Id == callId)
				.AnyAsync(
					c =>
						c.AlarmScope.Asset!.Groups.Any(g =>
							g.Accesses.Any(a => a.Member.UserId == UserId)
						),
					cancellationToken
				);
			if (!hasAccess)
			{
				return NotFound("Call not found");
			}
		}

		var model = await dataContext
			.Calls.Where(c => c.Id == callId)
			.Select(CallModel.FromEntity)
			.FirstAsync(cancellationToken);
		return Ok(model);
	}

	[HttpPost(Name = "CreateCall")]
	[Authorize(Roles = Roles.Watcher)]
	[ProducesResponseType<string>(StatusCodes.Status404NotFound)]
	[ProducesResponseType<CallModel>(StatusCodes.Status200OK)]
	public async Task<IActionResult> Create(
		CreateCallRequest request,
		CancellationToken cancellationToken
	)
	{
		// Проверяем доступ к тревоге
		var alarmScopeIdValue = new AlarmScopeId(request.AlarmScopeId);
		var scope = await dataContext.AlarmScopes.FirstOrDefaultAsync(
			a =>
				a.Id == alarmScopeIdValue
				&& a.Asset!.Organization.Members.Any(m => m.UserId == UserId),
			cancellationToken
		);
		if (scope is null)
		{
			return NotFound("Alarm not found");
		}
		if (Role == Roles.Watcher)
		{
			var hasAccess = await dataContext
				.AlarmScopes.Where(a => a.Id == alarmScopeIdValue)
				.AnyAsync(
					a => a.Asset!.Groups.Any(g => g.Accesses.Any(ac => ac.Member.UserId == UserId)),
					cancellationToken
				);
			if (!hasAccess)
			{
				return NotFound("Alarm not found");
			}
		}

		// Проверяем ответственное лицо принадлежит тому же объекту
		var rp = await dataContext.ResponsiblePersons.FirstOrDefaultAsync(
			x => x.Id == request.ResponsiblePersonId && x.AssetId == scope.AssetId,
			cancellationToken
		);
		if (rp is null)
		{
			return NotFound("Responsible person not found");
		}

		var commentary = CallCommentary.Create(request.Commentary);
		var call = Call.Create(
			scope.Id,
			UserId,
			request.ResponsiblePersonId,
			commentary,
			request.IsConfirmedRealAlarm,
			request.Result
		);
		await dataContext.Calls.AddAsync(call, cancellationToken);
		await dataContext.SaveChangesAsync(cancellationToken);

		var model = await dataContext
			.Calls.Where(c => c.Id == call.Id)
			.Select(CallModel.FromEntity)
			.FirstAsync(cancellationToken);
		return Ok(model);
	}

	[HttpPut("{id:guid}", Name = "UpdateCall")]
	[Authorize(Roles = Roles.Manager)]
	[ProducesResponseType<string>(StatusCodes.Status404NotFound)]
	[ProducesResponseType(StatusCodes.Status204NoContent)]
	public async Task<IActionResult> Update(
		Guid id,
		UpdateCallRequest request,
		CancellationToken cancellationToken
	)
	{
		var callId = new CallId(id);
		var call = await dataContext.Calls.FirstOrDefaultAsync(
			c =>
				c.Id == callId
				&& c.AlarmScope.Asset!.Organization.Members.Any(m => m.UserId == UserId),
			cancellationToken
		);
		if (call is null)
		{
			return NotFound("Call not found");
		}

		call.IsConfirmedRealAlarm = request.IsConfirmedRealAlarm;
		call.Commentary = CallCommentary.Create(request.Commentary);
		call.Result = request.Result;

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