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

namespace SatelliteProvider.TestSupport;

public static class JwtTokenFactory
{
    public const string DefaultSubject = "test-user";

    public static string Create(
        string secret,
        string subject = DefaultSubject,
        TimeSpan? lifetime = null,
        IEnumerable<Claim>? extraClaims = null,
        string algorithm = SecurityAlgorithms.HmacSha256,
        string? issuer = null,
        string? audience = null)
    {
        ArgumentNullException.ThrowIfNull(secret);

        var keyBytes = Encoding.UTF8.GetBytes(secret);
        var signingKey = new SymmetricSecurityKey(keyBytes);
        var credentials = new SigningCredentials(signingKey, algorithm);

        var now = DateTime.UtcNow;
        var expires = now.Add(lifetime ?? TimeSpan.FromHours(1));
        // JwtSecurityToken rejects Expires <= NotBefore. For negative
        // lifetimes (expired-token test fixture) shift NotBefore behind
        // Expires so the constructor accepts the token and lifetime
        // validation can fire downstream. DO NOT remove this branch —
        // see cycle-2 commits f64d0d7 + 11b7074 (LESSONS.md L-testing).
        var notBefore = expires <= now ? expires.AddMinutes(-5) : now;

        var claims = new List<Claim>
        {
            new(JwtRegisteredClaimNames.Sub, subject),
            new(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString("N"))
        };

        if (extraClaims is not null)
        {
            claims.AddRange(extraClaims);
        }

        var token = new JwtSecurityToken(
            issuer: issuer,
            audience: audience,
            claims: claims,
            notBefore: notBefore,
            expires: expires,
            signingCredentials: credentials);

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

    public static string CreateExpired(string secret, string subject = DefaultSubject, string? issuer = null, string? audience = null)
    {
        return Create(secret, subject, lifetime: TimeSpan.FromMinutes(-5), issuer: issuer, audience: audience);
    }

    public static string TamperSignature(string token)
    {
        ArgumentException.ThrowIfNullOrEmpty(token);

        var parts = token.Split('.');
        if (parts.Length != 3)
        {
            throw new ArgumentException("JWT must have three dot-separated parts.", nameof(token));
        }

        var signature = parts[2];
        if (signature.Length == 0)
        {
            throw new ArgumentException("JWT signature segment is empty.", nameof(token));
        }

        var firstChar = signature[0];
        var replacement = firstChar == 'A' ? 'B' : 'A';
        parts[2] = replacement + signature[1..];
        return string.Join('.', parts);
    }
}
