using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;

using Microsoft.IdentityModel.Tokens;

using Monitoring.Core.Domain;

namespace Monitoring.Services;

public class AuthenticationService(
	string issuer,
	string audience,
	string key
)
{
	private const int AccessTokenTimeoutMinutes = 30;
	private const int RefreshTokenTimeoutDays = 7;

	public record TokenResult(string AccessToken, string RefreshToken, DateTime AccessTokenExpires, DateTime RefreshTokenExpires);

	public TokenResult CreateTokens(User user)
	{
		// Создаем access token
		var accessTokenExpires = DateTime.UtcNow.AddMinutes(AccessTokenTimeoutMinutes);
		var accessToken = CreateAccessToken(user, accessTokenExpires);

		// Создаем refresh token
		var refreshTokenExpires = DateTime.UtcNow.AddDays(RefreshTokenTimeoutDays);
		var refreshToken = CreateRefreshToken();

		return new TokenResult(accessToken, refreshToken, accessTokenExpires, refreshTokenExpires);
	}

	private string CreateAccessToken(User user, DateTime expirationTime)
	{
		IEnumerable<Claim> claims =
			[
				new Claim(ClaimTypes.Name, user.Username.Value),
				new Claim(ClaimTypes.NameIdentifier, user.Id.Value.ToString()),
				new Claim(ClaimTypes.Role, user.Role.Name.Value),
			];

		SymmetricSecurityKey securityKey = new(Encoding.UTF8.GetBytes(key));
		SigningCredentials signingCredentials = new(securityKey, SecurityAlgorithms.HmacSha512Signature);

		JwtSecurityToken securityToken = new(
			claims: claims,
			expires: expirationTime,
			issuer: issuer,
			audience: audience,
			signingCredentials: signingCredentials
		);

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

	private static string CreateRefreshToken()
	{
		var randomNumber = new byte[32];
		using var rng = RandomNumberGenerator.Create();
		rng.GetBytes(randomNumber);
		return Convert.ToBase64String(randomNumber);
	}

	public ClaimsPrincipal? GetPrincipalFromExpiredToken(string token)
	{
		var tokenValidationParameters = new TokenValidationParameters
		{
			ValidateAudience = true,
			ValidateIssuer = true,
			ValidateIssuerSigningKey = true,
			IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(key)),
			ValidateLifetime = false, // важно для проверки истекших токенов
			ValidIssuer = issuer,
			ValidAudience = audience
		};

		var tokenHandler = new JwtSecurityTokenHandler();
		var principal = tokenHandler.ValidateToken(token, tokenValidationParameters, out SecurityToken securityToken);

		if (securityToken is not JwtSecurityToken jwtSecurityToken ||
			!jwtSecurityToken.Header.Alg.Equals(SecurityAlgorithms.HmacSha512Signature, StringComparison.InvariantCultureIgnoreCase))
		{
			return null;
		}

		return principal;
	}
}
