using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;

namespace JobTrackerApi.Services;

public sealed record GoogleTokenPrincipal(string Subject, string? Email, bool EmailVerified, string? GivenName, string? FamilyName, string? Name);

public interface IGoogleTokenValidator
{
    Task<GoogleTokenPrincipal> ValidateAsync(string idToken, CancellationToken cancellationToken = default);
}

public sealed class GoogleTokenValidator : IGoogleTokenValidator
{
    private readonly IConfiguration _cfg;
    private readonly IConfigurationManager<OpenIdConnectConfiguration> _configManager;

    public GoogleTokenValidator(IConfiguration cfg)
    {
        _cfg = cfg;
        _configManager = new ConfigurationManager<OpenIdConnectConfiguration>(
            "https://accounts.google.com/.well-known/openid-configuration",
            new OpenIdConnectConfigurationRetriever());
    }

    public GoogleTokenValidator(IConfiguration cfg, IConfigurationManager<OpenIdConnectConfiguration> configManager)
    {
        _cfg = cfg;
        _configManager = configManager;
    }

    public async Task<GoogleTokenPrincipal> ValidateAsync(string idToken, CancellationToken cancellationToken = default)
    {
        var audience = (_cfg["Auth:GoogleClientId"] ?? "").Trim();
        if (string.IsNullOrWhiteSpace(audience))
        {
            throw new InvalidOperationException("Google sign-in is not configured.");
        }

        var config = await _configManager.GetConfigurationAsync(cancellationToken);
        var handler = new JwtSecurityTokenHandler();
        var principal = handler.ValidateToken(idToken, new TokenValidationParameters
        {
            ValidateIssuer = true,
            ValidIssuers = new[] { "accounts.google.com", "https://accounts.google.com" },
            ValidateAudience = true,
            ValidAudience = audience,
            ValidateLifetime = true,
            ValidateIssuerSigningKey = true,
            IssuerSigningKeys = config.SigningKeys,
            ClockSkew = TimeSpan.FromMinutes(2),
        }, out _);

        var subject = principal.FindFirst("sub")?.Value?.Trim()
            ?? principal.FindFirst(JwtRegisteredClaimNames.Sub)?.Value?.Trim()
            ?? principal.FindFirst(ClaimTypes.NameIdentifier)?.Value?.Trim();
        if (string.IsNullOrWhiteSpace(subject))
        {
            throw new InvalidOperationException("Google token is missing a subject.");
        }

        return new GoogleTokenPrincipal(
            Subject: subject,
            Email: principal.FindFirst("email")?.Value?.Trim() ?? principal.FindFirst(ClaimTypes.Email)?.Value?.Trim(),
            EmailVerified: IsEmailVerified(principal),
            GivenName: principal.FindFirst("given_name")?.Value?.Trim() ?? principal.FindFirst(ClaimTypes.GivenName)?.Value?.Trim(),
            FamilyName: principal.FindFirst("family_name")?.Value?.Trim() ?? principal.FindFirst(ClaimTypes.Surname)?.Value?.Trim(),
            Name: principal.FindFirst("name")?.Value?.Trim() ?? principal.Identity?.Name?.Trim()
        );
    }

    private static bool IsEmailVerified(System.Security.Claims.ClaimsPrincipal principal)
    {
        var raw = principal.FindFirst("email_verified")?.Value?.Trim();
        if (string.IsNullOrWhiteSpace(raw)) return false;
        return string.Equals(raw, "true", StringComparison.OrdinalIgnoreCase) || raw == "1";
    }
}
