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 ProcessController : BaseApiController
    {
        private readonly ILogger<ProcessController> logger;
        private readonly IMapper mapper;
        private readonly IProcessRepository processService;
        //private readonly IValidator<ProcessRequest> validator;
        private readonly IUriService uriService;

        public ProcessController(
            ILogger<ProcessController> logger,
            IMapper mapper,
            IProcessRepository processService,
            //IValidator<ProcessRequest> validator,
            IUriService uriService
            )
        {
            this.logger = logger;
            this.mapper = mapper;
            this.processService = processService;
            //this.validator = validator;
            this.uriService = uriService;
        }


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

            IQueryable<Process> query = processService.Get()
                                        .OrderBy(t => t.Name);

            var processes = await processService.GetPage(query, paginationFilter).ToListAsync();

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

            var processesResponse = mapper.Map<List<ProcessResponse>>(processes);
            var paginationResponse = new PagedResponse<ProcessResponse>(processesResponse, true).GetPaginatedProps(paginationFilter, query);

            return Ok(paginationResponse);
        }


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

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

            var response = mapper.Map<ProcessResponse>(process);

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


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

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

            var response = mapper.Map<List<SubprocessResponse>>(process.Subprocesses.OrderBy(t => t.Name).ToList());

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


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

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

            var existEsppId = await processService.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 process = new Process
            {
                Id = Guid.NewGuid(),
                Name = request.Name.Trim(),
                EsppId = request.EsppId
            };

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

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

            var locationUri = uriService.GetUri(ApiRoutes.Process.Get, ApiRoutes.Process.getParam, process.Id);

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


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

            //проверка на уникальность esppId
            var existEsppId = await processService.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 processService.Get().FirstOrDefaultAsync(t => t.Id == id);

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

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

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

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

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


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

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

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

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

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

            return NoContent();
        }
    }
}
