using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using SharpRSS.API.Auth;
using SharpRSS.API.Contracts.DTOs.Groups;
using SharpRSS.API.Contracts.DTOs.Sessions;
using SharpRSS.API.Contracts.DTOs.Users;
using SharpRSS.API.Contracts.Models;
using SharpRSS.API.Data;
using SharpRSS.API.Models;
using ToolQit;
using ToolQit.Logging;

namespace SharpRSS.API.Controllers
{
    [ApiController]
    [Route("api/[controller]")]
    public class AuthController : ControllerBase
    {
        public AuthController(AuthService authService)
        {
            _authService = authService;
            _log = LogManager.CreateLogger(typeof(AuthController));
        }
        private readonly ILog _log;

        private readonly AuthService _authService;

        [HttpPost("authenticate")]
        [AllowAnonymous]
        [Produces("application/json")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
        public async Task<ActionResult<ResultOr<Session>>> Authenticate(AuthenticateUser auth)
        {
            var sessionResult = await _authService.Authenticate(auth);
            return sessionResult.Success ? Ok(sessionResult) :
                sessionResult.Status == ResultStatus.Failed ? BadRequest(sessionResult) :
                StatusCode(StatusCodes.Status500InternalServerError, sessionResult);
        }

        // To update only fill the values that need to be updated.
        [HttpPost("user")]
        [SessionAuthorize(true)]
        [Produces("application/json")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
        [ProducesResponseType(StatusCodes.Status401Unauthorized)]
        public async Task<ActionResult<ResultOr<User>>> InsertUser(InsertUser payload)
        {
            object? authSetObj = RouteData.Values[nameof(AuthorizationSet)];
            if (authSetObj is AuthorizationSet authSet)
            {
                if (!authSet.Group.Administrator)
                {
                    if (payload.Uid == authSet.User.Uid) // User can self change own information, but not the group property!
                        payload.GroupId = string.Empty;
                    else
                        return new UnauthorizedObjectResult(new Result(
                            $"User '{authSet.User.Uid}' in group '{authSet.Group.DisplayName}' does not has the right permission to change user '{payload.Uid}'!"));
                }
            }
            else
                return StatusCode(StatusCodes.Status500InternalServerError, new Result("Failed to get the authorization data!", ResultStatus.InternalFail));

            var createdUserResult = await _authService.InsertUserAsync(payload);
            return createdUserResult.Success ? Created("", createdUserResult) :
                createdUserResult.Status == ResultStatus.Failed ? BadRequest(createdUserResult) :
                StatusCode(StatusCodes.Status500InternalServerError, createdUserResult);
        }
        
        [HttpDelete("user")]
        [SessionAuthorize(true)]
        [Produces("application/json")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
        public async Task<ActionResult<Result>> DeleteUser(string userId)
        {
            var removedUserResult = await _authService.RemoveUserAsync(userId);
            return removedUserResult.Success ? Ok(removedUserResult) :
                removedUserResult.Status == ResultStatus.Failed ? BadRequest(removedUserResult) :
                StatusCode(StatusCodes.Status500InternalServerError, removedUserResult);
        }

        [HttpGet("user")]
        [SessionAuthorize(true)]
        [Produces("application/json")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        public async Task<ActionResult<ResultOr<User>>> GetUser(string userId)
        {
            var userResult = await _authService.GetUserAsync(userId);
            return userResult.Success ? Ok(userResult) : BadRequest(userResult);
        }

        [HttpGet("users")]
        [SessionAuthorize(true)]
        [Produces("application/json")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
        public async Task<ActionResult<ListResult<UserItem>>> GetUsers(string search = "", int results = 20, int skip = 0)
        {
            var authSet = HttpContext.Items["auth"] as AuthorizationSet;
            var usersResult = await _authService.GetUsersAsync(results, skip, search);
            return usersResult.Success ? Ok(usersResult) :
                usersResult.Status == ResultStatus.Failed ? BadRequest(usersResult) :
                StatusCode(StatusCodes.Status500InternalServerError, usersResult);
        }

        [HttpPost("group")]
        [SessionAuthorize(true)]
        [Produces("application/json")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
        public async Task<ActionResult<ResultOr<Group>>> InsertGroup(InsertGroup @group)
        {
            var groupInsertResult = await _authService.InsertGroupAsync(group);
            return groupInsertResult.Success ? Ok(groupInsertResult) :
                groupInsertResult.Status == ResultStatus.Failed ? BadRequest(groupInsertResult) :
                StatusCode(StatusCodes.Status500InternalServerError, groupInsertResult);
        }

        [HttpDelete("group")]
        [SessionAuthorize(true)]
        [Produces("application/json")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
        public async Task<ActionResult<Result>> RemoveGroup(string groupId)
        {
            var removeResult = await _authService.RemoveGroup(groupId);
            return removeResult.Success ? Ok(removeResult) :
                removeResult.Status == ResultStatus.Failed ? BadRequest(removeResult) :
                StatusCode(StatusCodes.Status500InternalServerError, removeResult);
        }

        [HttpGet("groups")]
        [SessionAuthorize(false)]
        [Produces("application/json")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
        public async Task<ActionResult<ListResult<GroupItem>>> GetGroups(string search = "", int results = 20, int skip = 0)
        {
            var groupsResult = await _authService.GetGroupsAsync(results, skip, search);
            return groupsResult.Success ? Ok(groupsResult) :
                groupsResult.Status == ResultStatus.Failed ? BadRequest(groupsResult) :
                StatusCode(StatusCodes.Status500InternalServerError, groupsResult);
        }

        [HttpGet("group")]
        [SessionAuthorize(false)]
        [Produces("application/json")]
        [ProducesResponseType(StatusCodes.Status200OK)]
        [ProducesResponseType(StatusCodes.Status400BadRequest)]
        [ProducesResponseType(StatusCodes.Status500InternalServerError)]
        public async Task<ActionResult<ResultOr<Group>>> GetGroup(string groupId)
        {
            var groupResult = await _authService.GetGroupAsync(groupId);
            return groupResult.Success ? Ok(groupResult) :
                groupResult.Status == ResultStatus.Failed ? BadRequest(groupResult) :
                StatusCode(StatusCodes.Status500InternalServerError, groupResult);
        }
    }
}