﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
using SnapMark.Objects.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http.Authentication;
using SnapMark.Web.Models.Api.V1.AuthorizeViewModels;
using System.Security.Claims;
using System.Text;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using SnapMark.Objects.Repos;
using SnapMark.Objects;
using SnapMark.Web.Models.Api.V1.AuthorizeViewModels.Response;
using SnapMark.Web.Models.Api.V1.AuthorizeViewModels.Request;

namespace SnapMark.Web.Controllers.Api.V1
{
    [Produces("application/json")]
    [Route("/api/v1/authorize")]
    public class AuthorizeController : BaseController
    {
        private readonly ApplicationDbContext applicationDbContext;
        private readonly ApplicationRepo applicationRepo;
        private readonly ApplicationUserRepo applicationUserRepo;
        private readonly ApplicationUserTokenRepo applicationUserTokenRepo;

        public AuthorizeController(IServiceProvider serviceProvider) : base(serviceProvider)
        {
            applicationDbContext = serviceProvider.GetService<ApplicationDbContext>();
            applicationRepo = serviceProvider.GetService<ApplicationRepo>();
            applicationUserRepo = serviceProvider.GetService<ApplicationUserRepo>();
            applicationUserTokenRepo = serviceProvider.GetService<ApplicationUserTokenRepo>();
        }

        [AllowAnonymous]
        [Route(""), HttpPost]
        public async Task<IActionResult> RequestToken([FromBody] TokenRequest request)
        {
            var application = await applicationRepo.FindByClientIdentifier(request.ClientId);

            if (application == null)
            {
                return BadRequest(new
                {
                    ErrorDescription = "Not authorized for application."
                });
            }

            var user = await applicationUserRepo.FindByUserNameAsync(request.UserName);

            if (user == null)
            {
                return BadRequest(new
                {
                    ErrorDescription = "Invalid user name or password"
                });
            }

            if (!BCrypt.Net.BCrypt.Verify(request.Password, user.Password))
            {
                return BadRequest(new
                {
                    ErrorDescription = "Invalid user name or password"
                });
            }

            var claims = new[]
            {
                new Claim(ClaimTypes.Name, request.UserName)
            };

            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(application.ClientSecret));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var expiresUtc = DateTime.UtcNow.AddMinutes(30);

            var securityToken = new JwtSecurityToken(
                issuer: Request.Host.Host,
                audience: Request.Host.Host,
                claims: claims,
                expires: expiresUtc,
                signingCredentials: creds);

            var token = new JwtSecurityTokenHandler().WriteToken(securityToken);

            var applicationUserToken = new ApplicationUserToken
            {
                Token = token,
                ExpiresUtc = expiresUtc,
                Type = "login",
                ApplicationUser = user,
                Application = application
            };

            applicationUserTokenRepo.Write(applicationUserToken);

            await applicationDbContext.SaveChangesAsync();

            return Ok(new RequestTokenRepsonse
            {
                Token = token,
                UserName = user.UserName,
            });
        }
    }
}