using AsbCloudApp.Data;
using AsbCloudApp.Exceptions;
using AsbCloudApp.Services;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Threading;
using System.Threading.Tasks;

namespace AsbCloudWebApi.Controllers
{
    /// <summary>
    /// Формирование программы бурения
    /// </summary>
    [Route("api/well/{idWell}/drillingProgram")]
    [ApiController]
    [Authorize]
    public class DrillingProgramController : ControllerBase
    {
        private readonly IDrillingProgramService drillingProgramService;
        private readonly IWellService wellService;

        public DrillingProgramController(IDrillingProgramService drillingProgramService, IWellService wellService)
        {
            this.drillingProgramService = drillingProgramService;
            this.wellService = wellService;
        }

        /// <summary>
        /// Список категорий файлов из которых состоит программа бурения
        /// </summary>
        /// <param name="token"></param>
        /// <returns></returns>
        [HttpGet("categories")]
        [ProducesResponseType(typeof(FileCategoryDto[]), (int)System.Net.HttpStatusCode.OK)]
        public async Task<IActionResult> GetCategories(CancellationToken token)
        {
            var result = await drillingProgramService.GetCategoriesAsync(token);
            return Ok(result);
        }

        /// <summary>
        /// Список пользователей 
        /// </summary>
        /// <param name="idWell">id скважины</param>
        /// <param name="token"></param>
        /// <returns></returns>
        [HttpGet("availableUsers")]
        [ProducesResponseType(typeof(FileCategoryDto[]), (int)System.Net.HttpStatusCode.OK)]
        public async Task<IActionResult> GetAvailableUsers(int idWell, CancellationToken token)
        {
            var result = await drillingProgramService.GetAvailableUsers(idWell, token);
            return Ok(result);
        }

        /// <summary>
        /// Состояние процесса согласования программы бурения
        /// </summary>
        /// <param name="idWell"> id скважины </param>
        /// <param name="token"> Токен отмены задачи </param>
        /// <returns> Возвращает файл программы бурения </returns>
        [HttpGet]
        [ProducesResponseType(typeof(DrillingProgramStateDto), (int)System.Net.HttpStatusCode.OK)]
        public async Task<IActionResult> GetStateAsync(int idWell, CancellationToken token)
        {
            var idCompany = User.GetCompanyId();

            var idUser = User.GetUserId();

            if (idCompany is null || idUser is null ||
                !await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
                    idWell, token).ConfigureAwait(false))
                return Forbid();

            var result = await drillingProgramService.GetStateAsync(idWell, (int)idUser, token);
            return Ok(result);
        }

        /// <summary>
        /// Сброс ошибки формирования ПБ
        /// </summary>
        /// <param name="idWell"></param>
        /// <returns></returns>
        [HttpDelete("errors")]
        [Permission("DrillingProgram.get")]
        public IActionResult ClearError(int idWell)
        {
            drillingProgramService.ClearError(idWell);
            return Ok();
        }

        /// <summary>
        /// Загрузка файла для части программы бурения
        /// </summary>
        /// <param name="idWell"></param>
        /// <param name="idFileCategory">ID части программы. Не путать с категорией файла</param>
        /// <param name="files"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        [HttpPost("part/{idFileCategory}")]
        [Permission("DrillingProgram.get")]
        [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
        public async Task<IActionResult> AddFile(
            int idWell,
            int idFileCategory,
            [FromForm] IFormFileCollection files,
            CancellationToken token)
        {
            int? idCompany = User.GetCompanyId();
            int? idUser = User.GetUserId();

            if (idCompany is null || idUser is null)
                return Forbid();

            if (!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
                idWell, token).ConfigureAwait(false))
                return Forbid();

            if (files.Count > 1)
                return this.ValidationBadRequest(nameof(files), "only 1 file can be uploaded");

            if (files.Count == 0)
                return this.ValidationBadRequest(nameof(files), "at list 1 file should be uploaded");

            var fileName = files[0].FileName;
            
            var fileStream = files[0].OpenReadStream();
            var result = await drillingProgramService.AddFile(idWell, idFileCategory, (int)idUser, fileName, fileStream, token);

            return Ok(result);
        }

        /// <summary>
        /// Добавляет разделы программы бурения
        /// </summary>
        /// <param name="idWell"></param>
        /// <param name="idFileCategories">Список категорий файлов, по которым будут добавлены части ПБ</param>
        /// <param name="token"></param>
        /// <returns></returns>
        [HttpPost("part")]
        [Permission]
        [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
        public async Task<IActionResult> AddPartsAsync(int idWell, [Required] IEnumerable<int> idFileCategories, CancellationToken token)
        {
            int? idCompany = User.GetCompanyId();
            int? idUser = User.GetUserId();

            if (idCompany is null || idUser is null)
                return Forbid();

            if (!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
                idWell, token).ConfigureAwait(false))
                return Forbid();

            var result = await drillingProgramService.AddPartsAsync(idWell, idFileCategories, token);
            return Ok(result);
        }

        /// <summary>
        /// Удаляет разделы программы бурения
        /// </summary>
        /// <param name="idWell"></param>
        /// <param name="idFileCategories">Список категорий файлов, по которым будут удалены части ПБ</param>
        /// <param name="token"></param>
        /// <returns></returns>
        [HttpDelete("part")]
        [Permission]
        [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
        public async Task<IActionResult> RemovePartsAsync(int idWell, [Required] IEnumerable<int> idFileCategories, CancellationToken token)
        {
            int? idCompany = User.GetCompanyId();
            int? idUser = User.GetUserId();

            if (idCompany is null || idUser is null)
                return Forbid();

            if (!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
                idWell, token).ConfigureAwait(false))
                return Forbid();

            var result = await drillingProgramService.RemovePartsAsync(idWell, idFileCategories, token);
            return Ok(result);
        }

        /// <summary>
        /// Добавить пользователя в список publishers или approvers части программы бурения
        /// </summary>
        /// <param name="idWell"></param>
        /// <param name="idUser"></param>
        /// <param name="idFileCategory"></param>
        /// <param name="idUserRole"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        [HttpPost("part/{idFileCategory}/user")]
        [Permission]
        [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
        public async Task<IActionResult> AddUserAsync(int idWell, [Required] int idUser, int idFileCategory, [Required] int idUserRole, CancellationToken token)
        {
            int? idCompany = User.GetCompanyId();
            int? idUserEditor = User.GetUserId();

            if (idCompany is null || idUserEditor is null)
                return Forbid();

            if (!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
                idWell, token).ConfigureAwait(false))
                return Forbid();

            var result = await drillingProgramService.AddUserAsync(idWell, idFileCategory, idUser, idUserRole, token);
            return Ok(result);
        }

        /// <summary>
        /// Удалить пользователя из списка publishers или approvers части программы бурения
        /// </summary>
        /// <param name="idWell"></param>
        /// <param name="idUser"></param>
        /// <param name="idFileCategory"></param>
        /// <param name="idUserRole"></param>
        /// <param name="token"></param>
        /// <returns></returns>
        [HttpDelete("part/{idFileCategory}/user/{idUser}")]
        [Permission]
        [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
        public async Task<IActionResult> RemoveUserAsync(int idWell, int idUser, int idFileCategory, [Required] int idUserRole, CancellationToken token)
        {
            int? idCompany = User.GetCompanyId();
            int? idUserEditor = User.GetUserId();

            if (idCompany is null || idUserEditor is null)
                return Forbid();

            if (!await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
                idWell, token).ConfigureAwait(false))
                return Forbid();

            var result = await drillingProgramService.RemoveUserAsync(idWell, idFileCategory, idUser, idUserRole, token);
            return Ok(result);
        }

        /// <summary>
        /// Создает метку для файла входящего в программу бурения, заменить существующую
        /// </summary>
        /// <param name="idWell"> id скважины </param>
        /// <param name="markDto">метка файла</param>
        /// <param name="token"> Токен отмены задачи </param>
        /// <returns></returns>
        [HttpPost("fileMark")]
        [Permission("DrillingProgram.get")]
        public async Task<IActionResult> AddOrReplaceFileMarkAsync(int idWell, FileMarkDto markDto, CancellationToken token)
        {
            var idCompany = User.GetCompanyId();

            var idUser = User.GetUserId();

            if (idCompany is null || idUser is null ||
                !await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
                    idWell, token).ConfigureAwait(false))
                return Forbid();

            var result = await drillingProgramService.AddOrReplaceFileMarkAsync(markDto, (int)idUser, token)
                .ConfigureAwait(false);

            return Ok(result);
        }

        /// <summary>
        /// Помечает метку у файла входящего, в программу бурения, как удаленную
        /// </summary>
        /// <param name="idWell"> id скважины </param>
        /// <param name="idMark"> id метки </param>
        /// <param name="token"> Токен отмены задачи </param>
        /// <returns></returns>
        [HttpDelete("fileMark/{idMark}")]
        [Permission("DrillingProgram.get")]
        [ProducesResponseType(typeof(int), (int)System.Net.HttpStatusCode.OK)]
        public async Task<IActionResult> MarkAsDeletedFileMarkAsync(int idWell, int idMark,
            CancellationToken token)
        {
            var idCompany = User.GetCompanyId();

            if (idCompany is null || !await wellService.IsCompanyInvolvedInWellAsync((int)idCompany,
                idWell, token).ConfigureAwait(false))
                return Forbid();

            var result = await drillingProgramService.MarkAsDeletedFileMarkAsync(idMark, token)
                .ConfigureAwait(false);

            return Ok(result);
        }
    }
}