using AutoMapper;
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.Responses;
using PARR.API.Contracts.V1.Responses.Base;
using PARR.API.Controllers.V1.Base;
using PARR.API.Services.Interfaces;
using PARR.Core.Repositories.Interfaces;
using PARR.Domain.Common.Roles;
using PARR.Domain.Entities;

namespace PARR.API.Controllers.V1
{
    /// <summary>
    /// Управление ролями пользователя
    /// </summary>
    [Authorize(Roles = ParrRoles.Administrator.Role)]
    public class UserRoleController : BaseApiController
    {
        private readonly IUserRepository userService;
        private readonly IMapper mapper;
        private readonly ILogger<UserRoleController> logger;
        private readonly IRoleRepository roleService;
        private readonly IAuthService authService;
        private readonly IUriService uriService;

        public UserRoleController(
            IUserRepository userService,
            IMapper mapper,
            ILogger<UserRoleController> logger,
            IRoleRepository roleService,
            IAuthService authService,
            IUriService uriService
            )
        {
            this.userService = userService;
            this.mapper = mapper;
            this.logger = logger;
            this.roleService = roleService;
            this.authService = authService;
            this.uriService = uriService;
        }


        /// <summary>
        /// Роли пользователя
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        [HttpGet(ApiRoutes.UserRoles.Get)]
        public async Task<IActionResult> Get([FromRoute] Guid userId)
        {
            var user = await userService.Get()
                                .Include(t => t.Roles).ThenInclude(t => t.Role)
                                .FirstOrDefaultAsync(t => t.Id == userId);

            if (user == null)
                return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Не найден пользователь с id: {userId}" } }));

            var response = mapper.Map<List<RoleResponse>>(user.Roles.Select(t => t.Role)).OrderBy(t => t.Description).ToList();

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


        /// <summary>
        /// Добавить роль пользователю
        /// </summary>
        /// <param name="userId"></param>
        /// <returns></returns>
        [HttpPost(ApiRoutes.UserRoles.AddRole)]
        public async Task<IActionResult> AddRole([FromRoute] Guid userId, [FromBody] UserAddRoleRequest request)
        {
            var user = await userService.Get()
                                .Include(t => t.Roles)
                                .FirstOrDefaultAsync(t => t.Id == userId);

            if (user == null)
                return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Не найден пользователь с id: {userId}" } }));

            if (user.Roles.FirstOrDefault(t => t.RoleId == request.RoleId) != null)
                return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"У пользователя {userId} уже есть роль {request.RoleId}" } }));

            var newRole = await roleService.GetAsync(request.RoleId);
            if (newRole == null)
                return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Не найдена роль с id: {request.RoleId}" } }));

            // добавляем роль
            user.Roles.Add(new UsersInRole { RoleId = newRole.Id });
            if (!await userService.CommitAsync())
                return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Ошибка при добавлении роли {request.RoleId} пользователю {userId}" } }));

            logger.LogInformation($"Пользователь {User.Identity?.Name} добвавил роль {newRole.Name} пользователю {userId}, {user.Ip}, {user.Name}");

            // удалить из кэша
            await authService.RemoveCacheUserAsync(user.Ip);


            var updatedUser = await userService.Get()
                                .Include(t => t.Roles).ThenInclude(t => t.Role)
                                .FirstAsync(t => t.Id == userId);

            var response = mapper.Map<List<RoleResponse>>(updatedUser.Roles.Select(t => t.Role)).OrderBy(t => t.Description).ToList();
            var locationUri = uriService.GetUri(ApiRoutes.UserRoles.Get, ApiRoutes.UserRoles.userParam, userId);

            return Created(locationUri, new Response<List<RoleResponse>>(response, true));
        }


        /// <summary>
        /// Удалить роль у пользователя
        /// </summary>
        /// <param name="userId"></param>
        /// <param name="roleId"></param>
        /// <returns></returns>
        [HttpDelete(ApiRoutes.UserRoles.DeleteRole)]
        public async Task<IActionResult> DeleteRole([FromRoute] Guid userId, [FromRoute] Guid roleId)
        {
            var user = await userService.Get()
                              .Include(t => t.Roles).ThenInclude(t => t.Role)
                              .FirstOrDefaultAsync(t => t.Id == userId);

            if (user == null)
                return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Не найден пользователь с id: {userId}" } }));

            var userRole = user.Roles.FirstOrDefault(t => t.RoleId == roleId);

            if (userRole == null)
                return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"У пользователя {userId} нет роли с id {roleId}" } }));

            user.Roles.Remove(userRole);

            if (!await userService.CommitAsync())
                return BadRequest(new Response(false, new List<ErrorModel> { new ErrorModel { Message = $"Ошибка при удалении роли {roleId} у пользователя {userId}" } }));

            logger.LogInformation($"Пользователь {User.Identity?.Name} удалил роль {userRole.Role.Name} у пользователя {userId}, {user.Ip}, {user.Name}");

            // удалить из кэша
            await authService.RemoveCacheUserAsync(user.Ip);

            return NoContent();
        }

    }
}
