using System.Security.Cryptography;

namespace DysonNetwork.Pass.Auth;

public class CompactTokenService(IConfiguration config)
{
    private readonly string _privateKeyPath = config["AuthToken:PrivateKeyPath"] 
        ?? throw new InvalidOperationException("AuthToken:PrivateKeyPath configuration is missing");
    
    public string CreateToken(AuthSession session)
    {
        // Load the private key for signing
        var privateKeyPem = File.ReadAllText(_privateKeyPath);
        using var rsa = RSA.Create();
        rsa.ImportFromPem(privateKeyPem);
        
        // Create and return a single token
        return CreateCompactToken(session.Id, rsa);
    }

    private string CreateCompactToken(Guid sessionId, RSA rsa)
    {
        // Create the payload: just the session ID
        var payloadBytes = sessionId.ToByteArray();
        
        // Base64Url encode the payload
        var payloadBase64 = Base64UrlEncode(payloadBytes);
        
        // Sign the payload with RSA-SHA256
        var signature = rsa.SignData(payloadBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        
        // Base64Url encode the signature
        var signatureBase64 = Base64UrlEncode(signature);
        
        // Combine payload and signature with a dot
        return $"{payloadBase64}.{signatureBase64}";
    }
    
    public bool ValidateToken(string token, out Guid sessionId)
    {
        sessionId = Guid.Empty;
        
        try
        {
            // Split the token
            var parts = token.Split('.');
            if (parts.Length != 2)
                return false;
            
            // Decode the payload
            var payloadBytes = Base64UrlDecode(parts[0]);
            
            // Extract session ID
            sessionId = new Guid(payloadBytes);
            
            // Load public key for verification
            var publicKeyPem = File.ReadAllText(config["AuthToken:PublicKeyPath"]!);
            using var rsa = RSA.Create();
            rsa.ImportFromPem(publicKeyPem);
            
            // Verify signature
            var signature = Base64UrlDecode(parts[1]);
            return rsa.VerifyData(payloadBytes, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
        }
        catch
        {
            return false;
        }
    }
    
    // Helper methods for Base64Url encoding/decoding
    private static string Base64UrlEncode(byte[] data)
    {
        return Convert.ToBase64String(data)
            .TrimEnd('=')
            .Replace('+', '-')
            .Replace('/', '_');
    }
    
    private static byte[] Base64UrlDecode(string base64Url)
    {
        string padded = base64Url
            .Replace('-', '+')
            .Replace('_', '/');
            
        switch (padded.Length % 4)
        {
            case 2: padded += "=="; break;
            case 3: padded += "="; break;
        }
        
        return Convert.FromBase64String(padded);
    }
}