using Prospect.Server.Api.Services.CloudScript.Models;
using System.Security.Cryptography;
using System.Text.Json;

namespace Prospect.Server.Api.Services.CloudScript.Functions;

public class TokenGenerator
{
    public static int TokenSequenceId = 0;
    // Header is static - base64url encoded {}
    private static readonly string Header = "e30";

    public static string vxGenerateToken(string key, string issuer, int exp, string vxa, string f, string t)
    {
        TokenSequenceId++;
        var claims = new Claims
        {
            iss = issuer,
            exp = exp,
            vxa = vxa,
            vxi = TokenSequenceId,
            f = f,
            t = t
        };

        List<string> segments = new List<string>();
        segments.Add(Header);

        // Encode payload
        var claimsString = JsonSerializer.Serialize(claims);
        var encodedClaims = Base64URLEncode(claimsString);

        // Join segments to prepare for signing
        segments.Add(encodedClaims);
        string toSign = String.Join(".", segments);

        // Sign token with key and SHA256
        string sig = SHA256Hash(key, toSign);
        segments.Add(sig);

        // Join all 3 parts of token with . and return
        string token = String.Join(".", segments);

        return token;
    }

    private static string Base64URLEncode(string plainText)
    {
        var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(plainText);
        // Remove padding at the end
        var encodedString = Convert.ToBase64String(plainTextBytes).TrimEnd('=');
        // Substitute URL-safe characters
        string urlEncoded = encodedString.Replace("+", "-").Replace("/", "_");

        return urlEncoded;
    }

    private static string SHA256Hash(string secret, string message)
    {
        var encoding = new System.Text.ASCIIEncoding();
        byte[] keyByte = encoding.GetBytes(secret);
        byte[] messageBytes = encoding.GetBytes(message);
        // The instance of HMACSHA256 is constructed and disposed in this method because it is not thread safe
        using var hmacsha256 = new HMACSHA256(keyByte);
        byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
        var hashString = Convert.ToBase64String(hashmessage).TrimEnd('=');
        string urlEncoded = hashString.Replace("+", "-").Replace("/", "_");

        return urlEncoded;
    }

    public class Claims
    {
        public string iss { get; set; }
        public int exp { get; set; }
        public string vxa { get; set; }
        public int vxi { get; set; }
        public string f { get; set; }
        public string t { get; set; }
        public string sub { get; set; }
    }
}

[CloudScriptFunction("VivoxLogin")]
public class VivoxLogin : ICloudScriptFunction<FYVivoxLoginTokenRequest, FYVivoxJoinData>
{
    public async Task<FYVivoxJoinData> ExecuteAsync(FYVivoxLoginTokenRequest request)
    {
        // TODO: Replace with app configuration
        var channel = "sip:confctl-g-<issuer>.testchannel@mtu1xp.vivox.com"; // e - echo channel, g - global, p - positional
        var exp = (int)DateTimeOffset.UtcNow.AddSeconds(90).ToUnixTimeSeconds();
        var token = TokenGenerator.vxGenerateToken("key", "issuer", exp, "login", request.Username, channel);

        return new FYVivoxJoinData
        {
            Request = new Models.Data.FYVivoxJoinTokenRequest{
                Username = request.Username,
                Channel = channel,
                HasAudio = false,
                HasText = true,
                ChannelType = 2,
                ChannelID = "testchannel",
            },
            Token = token
        };
    }
}