using AutoMapper;
using FluentValidation;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using PARR.API.Contracts.V1;
using PARR.API.Contracts.V1.Requests;
using PARR.API.Contracts.V1.Requests.Queries;
using PARR.API.Contracts.V1.Responses;
using PARR.API.Contracts.V1.Responses.Base;
using PARR.API.Controllers.V1.Base;
using PARR.API.Extensions;
using PARR.API.Services.Interfaces;
using PARR.Core.Repositories.Interfaces;
using PARR.Domain.Common.Pagination;
using PARR.Domain.Common.Roles;
using PARR.Domain.Entities;

namespace PARR.API.Controllers.V1
{
    /// <summary>
    /// Управление подпроцессами
    /// </summary>
    [Authorize(Roles = ParrRoles.Administrator.Role)]
    public class SubprocessController : BaseApiController
    {
        private readonly ILogger<SubprocessController> logger;
        private readonly IMapper mapper;
        private readonly ISubprocessRepository subprocessService;
        //private readonly IValidator<SubprocessRequest> validator;
        private readonly IUriService uriService;

        public SubprocessController(
            ILogger<SubprocessController> logger,
            IMapper mapper,
            ISubprocessRepository subprocessService,
            //IValidator<SubprocessRequest> validator,
            IUriService uriService
            )
        {
            this.logger = logger;
            this.mapper = mapper;
            this.subprocessService = subprocessService;
            //this.validator = validator;
            this.uriService = uriService;
        }


        /// <summary>
        /// Список подпроцессов постранично
        /// </summary>
        /// <param name="paginationQuery"></param>
        /// <returns></returns>
        [HttpGet(ApiRoutes.Subprocess.GetAll)]
        public async Task<IActionResult> GetAll([FromQuery] PaginationQuery paginationQuery, [FromQuery] SubprocessQuery filter)
        {
            var paginationFilter = mapper.Map<PaginationFilter>(paginationQuery);

            IQueryable<Subprocess> query = subprocessService.Get()
                                        .OrderBy(t => t.Name);

            if (filter.ProcessId.HasValue)
                query = query.Where(t => t.ProcessId == filter.ProcessId.Value);

            var subprocesses = await subprocessService.GetPage(query, paginationFilter).ToListAsync();

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

            var subprocessesResponse = mapper.Map<List<SubprocessResponse>>(subprocesses);
            var paginationResponse = new PagedResponse<SubprocessResponse>(subprocessesResponse, true).GetPaginatedProps(paginationFilter, query);

            return Ok(paginationResponse);
        }


        /// <summary>
        /// Получить подпроцесс по id
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet(ApiRoutes.Subprocess.Get)]
        public async Task<IActionResult> GetById([FromRoute] Guid id)
        {
            var subprocess = await subprocessService.Get()
                                .FirstOrDefaultAsync(t => t.Id == id);

            if (subprocess == null)
                return NotFound();

            var response = mapper.Map<SubprocessResponse>(subprocess);

            return Ok(new Response<SubprocessResponse>(response, true));
        }


        /// <summary>
        /// Получить связанные с подпроцессом ТНК
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpGet(ApiRoutes.Subprocess.GetTnks)]
        public async Task<IActionResult> Get([FromRoute] Guid id)
        {
            var subprocess = await subprocessService.Get()
                                .Include(t => t.Tnks)
                                .FirstOrDefaultAsync(t => t.Id == id);

            if (subprocess == null)
                return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Не найден подпроцесс с id: {id}" } }));

            var response = mapper.Map<List<TnkResponse>>(subprocess.Tnks.OrderBy(t => t.Name).ToList());

            return Ok(new Response<List<TnkResponse>>(response, true));
        }



        /// <summary>
        /// Создать подпроцесс
        /// </summary>
        /// <param name="request"></param>
        /// <returns></returns>
        [HttpPost(ApiRoutes.Subprocess.Create)]
        public async Task<IActionResult> Create([FromBody] SubprocessRequest request)
        {
            //var resultValidate = await validator.ValidateAsync(request);
            //if (!resultValidate.IsValid)
            //    return BadRequest(new Response(resultValidate.Errors));

            var existName = await subprocessService.Get().FirstOrDefaultAsync(t => t.Name.ToLower() == request.Name.Trim().ToLower());
            if (existName != null)
                return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { FieldName = nameof(request.Name), Message = $"Подпроцесс с именем \"{request.Name}\" уже существует." } }));

            var existEsppId = await subprocessService.Get().FirstOrDefaultAsync(t => t.EsppId == request.EsppId);
            if (existEsppId != null)
                return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { FieldName = nameof(request.EsppId), Message = $"Подпроцесс с EsppId: {request.EsppId} уже существует." } }));

            var subprocess = new Subprocess
            {
                Id = Guid.NewGuid(),
                Name = request.Name.Trim(),
                EsppId = request.EsppId,
                ProcessId = request.ProcessId,
            };

            if (!await subprocessService.CreateAsync(subprocess) || !await subprocessService.CommitAsync())
                return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = "Ошибка при добавлении подпроцесса." } }));

            logger.LogInformation($"Пользователь {User.Identity?.Name} добавил подпроцесс: {subprocess.Name}, {subprocess.EsppId}, {subprocess.ProcessId}");

            var locationUri = uriService.GetUri(ApiRoutes.Subprocess.Get, ApiRoutes.Subprocess.getParam, subprocess.Id);

            return Created(locationUri, new Response<SubprocessResponse>(mapper.Map<SubprocessResponse>(subprocess), true));
        }


        /// <summary>
        /// Обновить подпроцесс
        /// </summary>
        /// <param name="id"></param>
        /// <param name="request"></param>
        /// <returns></returns>
        [HttpPut(ApiRoutes.Subprocess.Update)]
        public async Task<IActionResult> Update([FromRoute] Guid id, [FromBody] SubprocessRequest request)
        {
            //var resultValidate = await validator.ValidateAsync(request);
            //if (!resultValidate.IsValid)
            //    return BadRequest(new Response(resultValidate.Errors));

            //проверка на уникальность esppId
            var existEsppId = await subprocessService.Get().FirstOrDefaultAsync(t => t.Id != id && t.EsppId == request.EsppId);
            if (existEsppId != null)
                return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { FieldName = nameof(request.EsppId), Message = $"Уже есть подпроцесс с EsppId: {request.EsppId}" } }));

            var orig = await subprocessService.Get().FirstOrDefaultAsync(t => t.Id == id);

            if (orig == null)
                return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Ошибка при изменении подпроцесса. Не найден подпроцесс Id: {id}" } }));

            orig.Name = request.Name.Trim();
            orig.EsppId = request.EsppId;
            orig.ProcessId = request.ProcessId;
            orig.DateModified = DateTimeOffset.UtcNow;

            if (!await subprocessService.CommitAsync())
                return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = "Ошибка записи в базу данных изменений подпроцесса." } }));

            logger.LogInformation($"Пользователь {User.Identity?.Name} обновил подпроцесс: {orig.Id}, {orig.Name}, {orig.EsppId}, {orig.ProcessId}");

            return Ok(new Response<SubprocessResponse>(mapper.Map<SubprocessResponse>(orig), true));
        }


        /// <summary>
        /// Удалить подпроцесс (если у него нет ТНК)
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [HttpDelete(ApiRoutes.Subprocess.Delete)]
        public async Task<IActionResult> Delete([FromRoute] Guid id)
        {
            var subprocess = await subprocessService.Get().Include(t=>t.Tnks).FirstOrDefaultAsync(t=>t.Id==id);

            if (subprocess == null)
                return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Ошибка при удалении подпроцесса." } }));

            if (subprocess.Tnks.Any())
                return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Ошибка при удалении подпроцесса. У данного подпроцесса есть ТНК ({subprocess.Tnks.Count()} шт.)" } }));

            if (!subprocessService.Delete(subprocess) || !await subprocessService.CommitAsync())
                return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Ошибка при удалении подпроцесса." } }));

            logger.LogInformation($"Пользователь {User.Identity?.Name} удалил подпроцесс: {subprocess.Id}, {subprocess.Name}, {subprocess.EsppId}, {subprocess.ProcessId}");

            return NoContent();
        }
    }
}
