using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using UserMicroservice.Models;
using System;
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore;

namespace UserMicroservice.Services
{
    public class AuthService : IAuthService
    {
        private readonly IConfiguration _configuration;
        private readonly ApplicationDbContext _context;

        public AuthService(IConfiguration configuration, ApplicationDbContext applicationDbContext)
        {
            _configuration = configuration;
            _context = applicationDbContext;
        }

        public AuthTokenPair AuthenticateUser(int userId)
        {
            string accessToken = GenerateAccessToken(userId);
            string refreshToken = GenerateRefreshToken(userId).Token;

            return new AuthTokenPair(accessToken, refreshToken);
        }

        private string GenerateAccessToken(int userId)
        {
            string? configuredKey = _configuration["Jwt:Key"];
            string? configuredIssuer = _configuration["Jwt:Issuer"];
            string? configuredAudience = _configuration["Jwt:Audience"];

            throwIfNull(configuredKey, configuredIssuer, configuredAudience);

            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuredKey));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            var claims = new List<Claim>
            {
                new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
                new Claim(ClaimTypes.NameIdentifier, userId.ToString()),
            };

            var token = new JwtSecurityToken(
                issuer: configuredIssuer,
                audience: configuredAudience,
                claims: claims,
                expires: DateTime.UtcNow.AddMinutes(30),
                signingCredentials: creds);

            return new JwtSecurityTokenHandler().WriteToken(token);
        }

        private RefreshToken GenerateRefreshToken(int userId)
        {
            RefreshToken refreshToken = new(Guid.NewGuid().ToString(), DateTime.UtcNow.AddDays(2), userId);
            _context.RefreshTokens.Add(refreshToken);
            _context.SaveChanges();

            return refreshToken;
        }

        public bool ValidateRefreshToken(string token)
        {

            RefreshToken? refreshToken = _context.RefreshTokens.SingleOrDefault(t => t.Token == token);
            if (refreshToken != null)
            {
                if (DateTime.UtcNow <= refreshToken.ExpirationDate)
                {
                    return true;
                }
                
                // delete expired token
                RevokeRefreshToken(token);
            }
            return false;
        }

        public void RevokeRefreshToken(string token)
        {
            _context.RefreshTokens.Where(t => t.Token == token).ExecuteDelete();
            _context.SaveChanges();
        }

        private void throwIfNull(string? key, string? issuer, string? audience)
        {
            if (string.IsNullOrWhiteSpace(key) || string.IsNullOrWhiteSpace(issuer) || string.IsNullOrEmpty(audience))
            {
                throw new ArgumentNullException("JWT configuration is missing.");
            }
        }
    }
}