using System;
using AsbCloudApp.Data.DetectedOperation;
using AsbCloudApp.Requests;
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Data.WellOperation;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudInfrastructure.Services.DetectOperations;
using Microsoft.AspNetCore.Http;

namespace AsbCloudWebApi.Controllers.SAUB
{
	/// <summary>
	/// Операции определенные по телеметрии САУБ
	/// </summary>
	[Route("api/well/{idWell}/[controller]")]
	[ApiController]
	[Authorize]
	public class DetectedOperationController : ControllerBase
	{
		private readonly IDetectedOperationRepository detectedOperationRepository;
		private readonly IDetectedOperationService detectedOperationService;
		private readonly IWellService wellService;
		private readonly DetectedOperationExportService detectedOperationExportService;

		public DetectedOperationController(IDetectedOperationService detectedOperationService,
			IWellService wellService,
			DetectedOperationExportService detectedOperationExportService,
			IDetectedOperationRepository detectedOperationRepository)
		{
			this.detectedOperationService = detectedOperationService;
			this.wellService = wellService;
			this.detectedOperationExportService = detectedOperationExportService;
			this.detectedOperationRepository = detectedOperationRepository;
		}

		/// <summary>
		/// Добавить операции
		/// </summary>
		/// <param name="idWell"></param>
		/// <param name="dtos"></param>
		/// <param name="token"></param>
		/// <returns></returns>
		[HttpPost]
		[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
		public async Task<IActionResult> InsertRangeAsync(int idWell, IEnumerable<DetectedOperationDto> dtos, CancellationToken token)
		{
			var idUser = await AssertUserHasAccessToWellAsync(idWell, token);

			var result = await detectedOperationService.InsertRangeManualAsync(idUser, idWell, dtos, token);

			return Ok(result);
		}

		/// <summary>
		/// Обновить операции
		/// </summary>
		/// <param name="idWell"></param>
		/// <param name="dtos"></param>
		/// <param name="token"></param>
		/// <returns></returns>
		[HttpPut]
		[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
		public async Task<IActionResult> UpdateRangeAsync(int idWell, IEnumerable<DetectedOperationDto> dtos, CancellationToken token)
		{
			var idUser = await AssertUserHasAccessToWellAsync(idWell, token);

			var result = await detectedOperationService.UpdateRangeManualAsync(idUser, idWell, dtos, token);

			return Ok(result);
		}

		/// <summary>
		/// Удалить операции
		/// </summary>
		/// <param name="idWell"></param>
		/// <param name="ids"></param>
		/// <param name="token"></param>
		/// <returns></returns>
		[HttpDelete]
		[ProducesResponseType(typeof(int), StatusCodes.Status200OK)]
		public async Task<IActionResult> DeleteRangeAsync(int idWell, IEnumerable<int> ids, CancellationToken token)
		{
			await AssertUserHasAccessToWellAsync(idWell, token);

			var result = await detectedOperationRepository.DeleteRangeAsync(ids, token);

			return Ok(result);
		}

		/// <summary>
		/// получить справочник операций. Отличается от операций заводимых вручную.
		/// При задании id скважины вернет только те операции, которые определились в телеметрии этой скважины.
		/// </summary>
		/// <param name="idWell">[опционально] id скважины</param>
		/// <param name="token"></param>
		/// <returns></returns>
		[HttpGet]
		[Route("/api/well/[controller]/categories")]
		[ProducesResponseType(typeof(IEnumerable<WellOperationCategoryDto>), StatusCodes.Status200OK)]
		public async Task<IActionResult> GetCategoriesAsync([FromQuery] int? idWell, CancellationToken token)
		{
			var result = await detectedOperationService.GetCategoriesAsync(idWell, token);
			return Ok(result);
		}

		/// <summary>
		/// Получить список автоопределенных операций для редактирования
		/// </summary>
		/// <param name="idWell"></param>
		/// <param name="request"></param>
		/// <param name="token"></param>
		/// <returns></returns>
		[HttpGet]
		[ProducesResponseType(typeof(PaginationContainer<DetectedOperationDto>), StatusCodes.Status200OK)]
		public async Task<IActionResult> GetPageAsync(int idWell, [FromQuery] DetectedOperationRequest request,
			CancellationToken token)
		{
			await AssertUserHasAccessToWellAsync(idWell, token);

			var well = await wellService.GetOrDefaultAsync(idWell, token);

			if (well?.IdTelemetry is null)
				return NoContent();

			var requestToService = new DetectedOperationByTelemetryRequest(well.IdTelemetry.Value, request);

			var result = await detectedOperationRepository.GetPageAsync(requestToService, token);
			return Ok(result);
		}
		
		/// <summary>
		/// Получить статистику по автоопределенным операциям
		/// </summary>
		/// <param name="idWell"></param>
		/// <param name="request"></param>
		/// <param name="token"></param>
		/// <returns></returns>
		[HttpGet("stat")]
		[ProducesResponseType(typeof(DetectedOperationListDto), StatusCodes.Status200OK)]
		public async Task<IActionResult> GetAsync(int idWell, [FromQuery] DetectedOperationRequest request, CancellationToken token)
		{
			await AssertUserHasAccessToWellAsync(idWell, token);
			
			var requestToService = new DetectedOperationByWellRequest(idWell, request);

			var result = await detectedOperationService.GetAsync(requestToService, token);
			return Ok(result);
		}

		/// <summary>
		/// Создает excel файл с операциями по скважине
		/// </summary>
		/// <param name="idWell">id скважины</param>
		/// <param name="token"></param>
		[HttpGet("export")]
		[Permission]
		[ProducesResponseType(typeof(PhysicalFileResult), StatusCodes.Status200OK, "application/octet-stream")]
		[ProducesResponseType(StatusCodes.Status204NoContent)]
		[ProducesResponseType(typeof(ValidationProblemDetails), (int)System.Net.HttpStatusCode.BadRequest)]
		public async Task<IActionResult> ExportAsync(int idWell, CancellationToken token)
		{
			var idCompany = User.GetCompanyId();

			if (idCompany is null)
				return Forbid();

			var host = $"{HttpContext.Request.Scheme}://{HttpContext.Request.Host}";
			var stream = await detectedOperationExportService.ExportAsync(idWell, host, token);

			return File(stream, "application/octet-stream", "operations.xlsx");
		}

		private async Task<int> AssertUserHasAccessToWellAsync(int idWell, CancellationToken token)
		{
			var idUser = User.GetUserId();
			var idCompany = User.GetCompanyId();

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

			if (!idCompany.HasValue)
				throw new ForbidException("Нет доступа к скважине");

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

			return idUser.Value;
		}
	}
}