using System.ComponentModel.DataAnnotations;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using AsbCloudApp.Data;
using AsbCloudApp.Data.ProcessMaps;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Repositories;
using AsbCloudApp.Services;
using AsbCloudApp.Services.ProcessMaps;
using AsbCloudApp.Services.ProcessMaps.WellDrillingProcessMap;
using AsbCloudWebApi.SignalR;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR;

namespace AsbCloudWebApi.Controllers.ProcessMaps;

public class ProcessMapWellDrillingController : ProcessMapBaseController<ProcessMapPlanWellDrillingDto>
{
    private readonly IHubContext<TelemetryHub> telemetryHubContext;

    private readonly IProcessMapReportWellDrillingService wellDrillingProcessMapReportService;
    private readonly IProcessMapReportExportService wellDrillingProcessMapReportExportService;
    private readonly IProcessMapImportService wellDrillingProcessMapImportService;

    public ProcessMapWellDrillingController(IWellService wellService,
        IProcessMapPlanRepository<ProcessMapPlanWellDrillingDto> repository,
        IUserRepository userRepository,
        IHubContext<TelemetryHub> telemetryHubContext,
        IProcessMapReportExportService wellDrillingProcessMapReportExportService,
        IProcessMapImportService wellDrillingProcessMapImportService,
        IProcessMapReportWellDrillingService wellDrillingProcessMapReportService, 
        ICrudRepository<WellSectionTypeDto> wellSectionRepository)
        : base(wellService, repository, userRepository, wellSectionRepository)
    {
        this.telemetryHubContext = telemetryHubContext;
        this.wellDrillingProcessMapReportExportService = wellDrillingProcessMapReportExportService;
        this.wellDrillingProcessMapImportService = wellDrillingProcessMapImportService;
        this.wellDrillingProcessMapReportService = wellDrillingProcessMapReportService;
    }

    /// <summary>
    /// Создание РТК
    /// </summary>
    /// <param name="processMap">Тело запроса</param>
    /// <param name="idWell">Id скважины</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public override async Task<IActionResult> InsertAsync(ProcessMapPlanWellDrillingDto processMap, int idWell, CancellationToken cancellationToken)
    {
        var result = await base.InsertAsync(processMap, idWell, cancellationToken);

        await NotifyUsersBySignalR(idWell, cancellationToken);

        return result;
    }

    /// <summary>
    /// Обновление РТК
    /// </summary>
    /// <param name="processMap">Тело запроса</param>
    /// <param name="idWell">Id скважины</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public override async Task<IActionResult> UpdateAsync(ProcessMapPlanWellDrillingDto processMap, int idWell, CancellationToken cancellationToken)
    {
        var result = await base.UpdateAsync(processMap, idWell, cancellationToken);

        await NotifyUsersBySignalR(idWell, cancellationToken);

        return result;
    }

    /// <summary>
    /// Удаление РТК
    /// </summary>
    /// <param name="id">Id удаляемой РТК</param>
    /// <param name="idWell">Id скважины</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    public override async Task<IActionResult> DeleteAsync(int id, int idWell, CancellationToken cancellationToken)
    {
	   var result = await base.DeleteAsync(id, idWell, cancellationToken);

        await NotifyUsersBySignalR(idWell, cancellationToken);

        return result;
    }

    /// <summary>
    /// Получение отчета РТК бурение
    /// </summary>
    /// <param name="idWell">Id</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    [HttpGet("report")]
    [ProducesResponseType(typeof(ProcessMapPlanWellDrillingDto), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetReportAsync(int idWell, CancellationToken cancellationToken)
    {
        var report = await wellDrillingProcessMapReportService.GetAsync(idWell, cancellationToken);

        return Ok(report);
    }

    /// <summary>
    /// Экспорт отчета РТК бурение
    /// </summary>
    /// <param name="idWell">Id скважины</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    [HttpGet("report/export")]
    [ProducesResponseType(typeof(PhysicalFileResult), StatusCodes.Status200OK, "application/octet-stream")]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    public async Task<IActionResult> ExportReportAsync(int idWell, CancellationToken cancellationToken)
    {
        var report = await wellDrillingProcessMapReportExportService.ExportAsync(idWell, cancellationToken);

        if (report is null)
            return NoContent();

        return File(report.Value.File, "application/octet-stream", report.Value.Name);
    }

    /// <summary>
    /// Импорт РТК бурение
    /// </summary>
    /// <param name="idWell">Id скважины</param>
    /// <param name="options"></param>
    /// <param name="file"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    [HttpPost("import/{options}")]
    [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
    public async Task<IActionResult> ImportAsync(int idWell, 
        int options, 
        [Required] IFormFile file, 
        CancellationToken cancellationToken)
    {
        await AssertUserHasAccessToEditProcessMapAsync(idWell, cancellationToken);

        if (Path.GetExtension(file.FileName).ToLower() != ".xlsx")
            return this.ValidationBadRequest(nameof(file), "Требуется xlsx файл.");

        using Stream stream = file.OpenReadStream();

        try
        {
            await wellDrillingProcessMapImportService.ImportAsync(idWell,
                IdUser,
                (options & 1) > 0,
                stream,
                cancellationToken);

		  await NotifyUsersBySignalR(idWell, cancellationToken);
	   }
        catch (FileFormatException ex)
        {
            return this.ValidationBadRequest(nameof(file), ex.Message);
        }

        return Ok();
    }

    /// <summary>
    /// Экспорт РТК бурение
    /// </summary>
    /// <param name="idWell">Id скважины</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    [HttpGet("export")]
    [ProducesResponseType(typeof(PhysicalFileResult), StatusCodes.Status200OK, "application/octet-stream")]
    [ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest)]
    public async Task<IActionResult> ExportAsync(int idWell, CancellationToken cancellationToken)
    {
        var processMapsFile = await wellDrillingProcessMapImportService.ExportAsync(idWell, cancellationToken);

        return File(processMapsFile.File, "application/octet-stream", processMapsFile.Name);
    }

    /// <summary>
    /// Возвращает шаблон файла для импорта
    /// </summary>
    /// <returns>Запрашиваемый файл</returns>
    [HttpGet("template")]
    [ProducesResponseType(typeof(PhysicalFileResult), StatusCodes.Status200OK, "application/octet-stream")]
    public async Task<IActionResult> GetTemplateAsync(CancellationToken cancellationToken)
    {
        var template = await wellDrillingProcessMapImportService.GetExcelTemplateStreamAsync(cancellationToken);
        return File(template.File, "application/octet-stream", template.Name);
    }

    private async Task NotifyUsersBySignalR(int idWell, CancellationToken cancellationToken)
    {
        var wellDrillingProcessMaps = await repository.GetByIdWellAsync(idWell, cancellationToken);

        await telemetryHubContext.Clients
            .Group($"well_{idWell}")
            .SendAsync("UpdateProcessMap", wellDrillingProcessMaps, cancellationToken);
    }
}