using System;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Threading.Tasks;
using Infrastructure.Identity;
using Infrastructure.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;

namespace Api.Controllers
{
  [Route("api/accounts")]
  [ApiController]
  [AllowAnonymous]
  public class AccountsController : ControllerBase
  {
    private readonly UserManager<User> _userManager;
    private readonly SignInManager<User> _signinManager;
    private readonly TokenOptions _options;
    private readonly IUserClaimsPrincipalFactory<User> _claimsFactory;

    public AccountsController(UserManager<User> userManager, SignInManager<User> signinManager, TokenOptions options, IUserClaimsPrincipalFactory<User> claimsFactory)
    {
      _userManager = userManager;
      _signinManager = signinManager;
      _options = options;
      _claimsFactory = claimsFactory;
    }

    [HttpGet("me")]
    [ProducesResponseType(StatusCodes.Status200OK)]
    public IActionResult Me()
    {
      return Ok(new
      {
        Id = User.FindFirstValue(ClaimTypes.NameIdentifier),
        Name = User.Identity.Name,
        Authenticated = User.Identity.IsAuthenticated,
      });
    }

    [HttpPost]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(StatusCodes.Status204NoContent)]
    public async Task<IActionResult> Register(RegisterViewModel cmd)
    {
      var result = await _userManager.CreateAsync(
        new Infrastructure.Identity.User { UserName = cmd.Username }, cmd.Password);

      if (!result.Succeeded)
      {
        return BadRequest();
      }

      return NoContent();
    }

    [HttpPost("token")]
    [ProducesResponseType(StatusCodes.Status400BadRequest)]
    [ProducesResponseType(typeof(string), StatusCodes.Status200OK)]
    public async Task<IActionResult> Login(LoginViewModel cmd)
    {
      var user = await _userManager.FindByNameAsync(cmd.Username);

      if (user == null)
      {
        return BadRequest();
      }

      var result = await _signinManager.CheckPasswordSignInAsync(user, cmd.Password, false);

      if (!result.Succeeded)
      {
        return BadRequest();
      }

      var principal = await _claimsFactory.CreateAsync(user);
      var descriptor = new SecurityTokenDescriptor
      {
        Subject = (ClaimsIdentity)principal.Identity,
        Expires = DateTime.UtcNow.AddDays(7),
        Issuer = _options.Issuer,
        Audience = _options.Audience,
        SigningCredentials = new SigningCredentials(_options.Key, SecurityAlgorithms.HmacSha256Signature)
      };
      var token = new JwtSecurityTokenHandler().CreateEncodedJwt(descriptor);

      return Ok(token);
    }
  }
}