using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Data.ProcessMaps;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;

namespace AsbCloudWebApi.Controllers.ProcessMaps;

/// <summary>
/// Конструкция скважины - план
/// </summary>
[ApiController]
[Route("api/well/{idWell:int}/[controller]")]
[Authorize]
public class WellSectionPlanController : ControllerBase
{
	private readonly IWellService wellService;
	private readonly IWellSectionPlanRepository wellSectionPlanRepository;
	private readonly ICrudRepository<WellSectionTypeDto> wellSectionRepository;

	public WellSectionPlanController(IWellService wellService,
		IWellSectionPlanRepository wellSectionPlanRepository,
		ICrudRepository<WellSectionTypeDto> wellSectionRepository)
	{
		this.wellService = wellService;
		this.wellSectionPlanRepository = wellSectionPlanRepository;
		this.wellSectionRepository = wellSectionRepository;
	}

	//TODO: так же следует вынести в базовый контроллер
	private int IdUser
	{
		get
		{
			var idUser = User.GetUserId();

			if (!idUser.HasValue)
				throw new ForbidException("Неизвестный пользователь");

			return idUser.Value;
		}
	}

	/// <summary>
	/// Добавить секцию
	/// </summary>
	/// <param name="idWell">Идентификатор скважины</param>
	/// <param name="wellSection">Секция скважины - план</param>
	/// <param name="cancellationToken"></param>
	/// <returns></returns>
	[HttpPost]
	[Permission]
	[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
	[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
	[ProducesResponseType(StatusCodes.Status403Forbidden)]
	public async Task<IActionResult> InsertAsync(int idWell, WellSectionPlanDto wellSection, CancellationToken cancellationToken)
	{
		wellSection.IdWell = idWell;
		wellSection.IdUser = IdUser;
		
		await CheckIsExistsWellSectionTypeAsync(wellSection.IdSectionType, cancellationToken);
		
		await AssertUserAccessToWell(idWell, cancellationToken);

		var wellSectionId = await wellSectionPlanRepository.InsertAsync(wellSection, cancellationToken);

		return Ok(wellSectionId);
	}
	
	/// <summary>
	/// Обновить секцию
	/// </summary>
	/// <param name="idWell">Идентификатор скважины</param>
	/// <param name="wellSection">Секция скважины - план</param>
	/// <param name="cancellationToken"></param>
	/// <returns></returns>
	[HttpPut]
	[Permission]
	[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
	[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
	[ProducesResponseType(StatusCodes.Status403Forbidden)]
	public async Task<IActionResult> UpdateAsync(int idWell, WellSectionPlanDto wellSection, CancellationToken cancellationToken)
	{
		wellSection.IdWell = idWell;
		wellSection.IdUser = IdUser;
		wellSection.LastUpdateDate = DateTimeOffset.UtcNow;

		await CheckIsExistsWellSectionTypeAsync(wellSection.IdSectionType, cancellationToken);
		
		await AssertUserAccessToWell(idWell, cancellationToken);

		var wellSectionId = await wellSectionPlanRepository.UpdateAsync(wellSection, cancellationToken);

		if (wellSectionId == ICrudRepository<WellSectionPlanDto>.ErrorIdNotFound)
			return this.ValidationBadRequest(nameof(wellSection.Id), $"Секции скважины с Id: {wellSection.Id} не существует");

		return Ok(wellSectionId);
	}

	/// <summary>
	/// Получить типы секций
	/// </summary>
	/// <param name="idWell">Идентификатор скважины</param>
	/// <param name="cancellationToken"></param>
	/// <returns></returns>
	[HttpGet("wellSectionTypes")]
	[ProducesResponseType(typeof(IEnumerable<WellSectionTypeDto>), StatusCodes.Status200OK)]
	[ProducesResponseType(StatusCodes.Status204NoContent)]
	[ProducesResponseType(StatusCodes.Status403Forbidden)]
	public async Task<IActionResult> GetWellSectionTypesAsync(int idWell, CancellationToken cancellationToken)
	{
		await AssertUserAccessToWell(idWell, cancellationToken);

		var wellSectionTypes = await wellSectionPlanRepository.GetWellSectionTypesAsync(idWell, cancellationToken);

		if (!wellSectionTypes.Any())
			return NoContent();

		return Ok(wellSectionTypes);
	}

	/// <summary>
	/// Получить список секций
	/// </summary>
	/// <param name="idWell">Идентификатор скважины</param>
	/// <param name="cancellationToken"></param>
	/// <returns></returns>
	[HttpGet]
	[ProducesResponseType(typeof(IEnumerable<WellSectionPlanDto>), StatusCodes.Status200OK)]
	[ProducesResponseType(StatusCodes.Status204NoContent)]
	[ProducesResponseType(StatusCodes.Status403Forbidden)]
	public async Task<IActionResult> GetPlanWellSectionsAsync(int idWell, CancellationToken cancellationToken)
	{
		await AssertUserAccessToWell(idWell, cancellationToken);

		var planWellSections = await wellSectionPlanRepository.GetByIdWellAsync(idWell, cancellationToken);
		
		if (!planWellSections.Any())
			return NoContent();

		return Ok(planWellSections);
	}

	/// <summary>
	/// Удалить секцию
	/// </summary>
	/// <param name="idWell">Идентификатор скважины</param>
	/// <param name="id">Идентификатор плановой секции</param>
	/// <param name="cancellationToken"></param>
	/// <returns></returns>
	[HttpDelete]
	[Permission]
	[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
	[ProducesResponseType(StatusCodes.Status403Forbidden)]
	public async Task<IActionResult> DeleteAsync(int idWell, int id, CancellationToken cancellationToken)
	{
		await AssertUserAccessToWell(idWell, cancellationToken);

		var deletedWellSectionPlanCount = await wellSectionPlanRepository.DeleteAsync(id, cancellationToken);

		return Ok(deletedWellSectionPlanCount);
	}

	//TODO: нужно создать базовый контроллер связанный со скважиной и вынести этот метод туда. Данный метод много где дублируется
	private async Task AssertUserAccessToWell(int idWell, CancellationToken cancellationToken)
	{
		var idCompany = User.GetCompanyId();

		if (!idCompany.HasValue || !await wellService.IsCompanyInvolvedInWellAsync(idCompany.Value, idWell, cancellationToken))
			throw new ForbidException("Нет доступа к скважине");
	}

	//TODO: тоже нужно вынести в базовый контроллер
	private async Task CheckIsExistsWellSectionTypeAsync(int idWellSectionType, CancellationToken cancellationToken)
	{
		_ = await wellSectionRepository.GetOrDefaultAsync(idWellSectionType, cancellationToken)
			?? throw new ArgumentInvalidException(nameof(ProcessMapPlanWellDrillingDto.IdWellSectionType),
				$"Тип секции с Id: {idWellSectionType} не найден");
	}
}