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;
  }
}