using e_suite.API.Common;
using e_suite.API.Common.models;
using e_suite.API.Common.models.@base;
using e_suite.Database.Core.Models;
using e_suite.Utilities.Pagination;
using eSuite.API.Models;
using eSuite.API.security;
using eSuite.API.Utilities;
using eSuite.Core.Miscellaneous;
using eSuite.Core.Security;
using Microsoft.AspNetCore.Mvc;
using Moq;
using static System.Runtime.InteropServices.JavaScript.JSType;
using IRoleManager = e_suite.API.Common.IRoleManager;

namespace eSuite.API.Controllers;

/// <summary>
/// User manage is responsible for managing users within e-suite.
/// </summary>
[Route("api/[controller]")]
[ApiController]
public class UserController : ESuiteControllerBase
{
    private readonly IUserManager _userManager;
    private readonly IRoleManager _roleManager;

    /// <summary>
    /// Constructor for user controller
    /// </summary>
    /// <param name="userManager"></param>
    /// <param name="roleManager"></param>
    public UserController(IUserManager userManager, IRoleManager roleManager)
    {
        _userManager = userManager;
        _roleManager = roleManager;
    }

    /// <summary>
    /// Get a list of the users
    /// </summary>
    /// <param name="paging"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    [Route("users")]
    [AccessKey(SecurityAccess.ViewUser)]
    [HttpGet]
    public async Task<IActionResult> GetUsers([FromQuery] Paging paging, CancellationToken cancellationToken = default!)
    {
        var result = await _userManager.GetUsersAsync(paging, cancellationToken);
        return Ok(result);
    }

    /// <summary>
    /// Get the details of a specific user
    /// </summary>
    /// <param name="generalIdRef"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    [Route("user")]
    [HttpGet]
    [AccessKey(SecurityAccess.ViewUser)]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status404NotFound)]
    public async Task<IActionResult> GetUser(
        [FromQuery] GeneralIdRef generalIdRef,
        CancellationToken cancellationToken = default!
    )
    {
        var user = await _userManager.GetUserAsync(generalIdRef, cancellationToken);
        return Ok(user);
    }

    /// <summary>
    /// Edit a user
    /// </summary>
    /// <param name="user"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    [Route("user")]
    [HttpPut]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [AccessKey(SecurityAccess.EditUser)]
    public async Task<IActionResult> EditUser(EditUser user, CancellationToken cancellationToken = default!)
    {
        await _userManager.EditUser(AuditUserDetails, user, cancellationToken);
        return Ok();
    }

    /// <summary>
    /// Patching is useful when you only want to update a few fields of the user rather than the whole object.
    /// </summary>
    /// <param name="user"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    [Route("user")]
    [HttpPatch]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [AccessKey(SecurityAccess.EditUser)]
    public async Task<IActionResult> PatchUser([FromQuery] IGeneralIdRef userId, [FromBody] PatchUser patchUser, CancellationToken cancellationToken = default!)
    {
    
        await _userManager.PatchUser(AuditUserDetails, userId, patchUser, cancellationToken);
        return Ok();
    }

    /// <summary>
    /// Create a new e-suite user
    /// </summary>
    /// <remarks>Create a new user for e-suite.  The minimum information needed is the email address, which forms the account user name.  Once created the user will get an e-mail asking them to confirm the account.</remarks>
    /// <param name="userRegistration">Contains the details that need to be supplied to create the user.</param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    [Route("user")]
    [HttpPost]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ProblemDetails))]
    [AccessKey(SecurityAccess.AddUser)]
    public async Task<IActionResult> CreateUser(
        [FromBody] UserRegistration userRegistration,
        CancellationToken cancellationToken = default!
    )
    {
        await _roleManager.CheckHasDomainAccess(AuditUserDetails.UserId, userRegistration.DomainId,
            SecurityAccess.AddUser, cancellationToken);

        await _userManager.CreateUser(AuditUserDetails, userRegistration, cancellationToken);
        return Ok();
    }

    /// <summary>
    /// Use this to deactivate a user from being able to access e-suite. This will stop them from being able to log in.
    /// </summary>
    /// <param name="email"></param>
    /// <param name="cancellationToken"></param>
    [Route("userByEmail")]
    [HttpDelete]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(ProblemDetails))]
    [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ProblemDetails))]
    [AccessKey(SecurityAccess.DeleteUser)]
    public async Task<IActionResult> DeactivateUser(
        [FromBody] EmailAddress email,
        CancellationToken cancellationToken = default!
    )
    {
        await _userManager.DeactivateUser(AuditUserDetails, email.Email, cancellationToken);
        return Ok();
    }

    /// <summary>
    /// Use this to deactivate a user from being able to access e-suite. This will stop them from being able to log in.
    /// </summary>
    /// <param name="generalIdRef"></param>
    /// <param name="cancellationToken"></param>
    [Route("user")]
    [HttpDelete]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(ProblemDetails))]
    [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ProblemDetails))]
    [AccessKey(SecurityAccess.DeleteUser)]
    public async Task<IActionResult> DeactivateUser(
        [FromBody] GeneralIdRef generalIdRef,
        CancellationToken cancellationToken = default!
    )
    {
        await _userManager.DeactivateUser(AuditUserDetails, generalIdRef, cancellationToken);
        return Ok();
    }

    /// <summary>
    /// User this to have the API resend the account confirmation e-mail to a given user.
    /// </summary>
    /// <param name="generalIdRef"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    /// <exception cref="ArgumentNullException"></exception>
    [Route("resendConfirmEmail")]
    [HttpPost]
    [AccessKey(SecurityAccess.ResendConfirmMail)]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(ProblemDetails))]
    [ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ProblemDetails))]
    public async Task<IActionResult> ResendConfirmEmail([FromBody] GeneralIdRef generalIdRef, CancellationToken cancellationToken = default!)
    {
        ArgumentNullException.ThrowIfNull(generalIdRef);

        await _userManager.ResendConfirmEmail(AuditUserDetails, generalIdRef, cancellationToken);
        return Ok();
    }

    /// <summary>
    /// Intended for internal use, this allows you to retrieve the current email action url for a given user
    /// </summary>
    /// <param name="emailAddress"></param>
    /// <param name="emailUserActionType"></param>
    /// <param name="cancellationToken"></param>
    /// <returns></returns>
    [Route("currentEmailUserActionUrl")]
    [HttpGet]
    [AccessKey(SecurityAccess.GetCurrentEmailActionUrl)]
    [ProducesResponseType(StatusCodes.Status200OK)]
    [ProducesResponseType(StatusCodes.Status404NotFound, Type = typeof(ProblemDetails))]
    public async Task<IActionResult> CurrentEmailUserActionUrl(
        string emailAddress,
        EmailUserActionType emailUserActionType, CancellationToken cancellationToken = default
    )
    {
        var url = await _userManager.GetCurrentEmailActionUrl(emailAddress, emailUserActionType, cancellationToken);
        return Ok(url);
    }
}