/* 
 * Copyright (c)2009-2025 DemiVision, LLC. All Rights Reserved. The information 
 * herein is the CONFIDENTIAL and PROPRIETARY information of DemiVision, LLC. 
 */

using System.Net;
using System.Security.Cryptography;

using System.Text;
using System.Text.Json;
using System.Text.Json.Serialization;

using RestSharp;
using RestSharp.Serializers.Json;

namespace iStatVball3;

/*
 * Implements all MaxPreps REST endpoint functionality including auth, test, and stats upload.
 */
public static class MP2API
{
    /* Constants */
    private const bool TestMode = false;

    // Partner fields
    private const string UserId = "482b336a-46d0-44f8-b88e-eca55ad3177a";
    private const string Password = "aL3p^9zTq8";
    private const string PartnerName = "IStatVBall3";
    private const string EncryptionKey = "fThWmZq4t7w!z%C*F-JaNdRgUkXn2r5u";
    private const string InitializationVector = "TjWnZr4u7x!A%C*F";

    // Required header fields
    private const string ContentType = "application/json";
    private const string TokenType = "Partner";

    // API endpoints
    private const string ProductionHost = "https://production.api.maxpreps.com";
    private const string TestingHost = "https://dev.api.maxpreps.com";

    private const string UriExtension = "gatewayweb/partners";
    private const string UriVersion = "v1";

    /* Methods */

    // Calls testing endpoint to validate userToken and apiKey
    public static async Task<HttpStatusCode> Test()
    {
        // Build request
        RestRequest request = CreateRequest( "testing" );

        // POST
        RestResponse response = await GetClient().ExecutePostAsync( request );

        return response.StatusCode;
    }

    // Calls stats upload endpoint
    public static async Task<RestResponse> SendStats( MP2Match match )
    {
        // Build request with body
        RestRequest request = CreatePostRequest( "contest-stat-imports", match );

        // POST
        return await GetClient().ExecutePostAsync( request );
    }

    /* Utilities */

    // Creates HTTP request to specified URI extension
    private static RestRequest CreateRequest( string uri )
    {
        const string host = TestMode ? TestingHost : ProductionHost;

        // Build base URI
        string absoluteUri = $"{host}/{UriExtension}/{uri}/{UriVersion}";

        RestRequest request = new( absoluteUri );

        // All requests include UserId
        request.AddQueryParameter( "userid", UserId, true );

        // Generated token/key
        string userToken = GenerateUserToken();
        string apiKey = GenerateApiKey();

        // Populate POST header fields
        request.AddHeader( "Content-Type", ContentType );
        request.AddHeader( "X-MP-TokenType", TokenType );
        request.AddHeader( "X-MP-UserToken", userToken );
        request.AddHeader( "apiKey", apiKey );

        return request;
    }

    // Creates HTTP request for POSTing specified JSON body
    private static RestRequest CreatePostRequest( string uri, object body )
    {
        RestRequest request = CreateRequest( uri );

        // Serialize JSON
        request.AddJsonBody( body );

        return request;
    }

    // Returns AES128 encrypted auth token based on user ID and date
    private static string GenerateUserToken()
    {
        // Add 16 minutes to current UTC time
        DateTime date = DateTime.UtcNow.Date.AddMinutes( 16 );

        // Serialize object
        object tokenObject = new
        {
            userId = UserId,
            createdOn = date.ToString( "g" ),
            tokenSource = PartnerName
        };

        string tokenString = JsonSerializer.Serialize( tokenObject );

        // Encrypt using AES128
        using Aes aes128 = Aes.Create();

        // Encryption settings
        aes128.Padding = PaddingMode.PKCS7;
        aes128.Mode = CipherMode.CBC;
        aes128.BlockSize = 128;
        aes128.KeySize = 256;
        aes128.Key = Encoding.ASCII.GetBytes( EncryptionKey );
        aes128.IV = Encoding.ASCII.GetBytes( InitializationVector );

        // Create cryptoTransform from Encryptor with key/iv, get encrypted text
        using ICryptoTransform cryptoTransform = aes128.CreateEncryptor( aes128.Key, aes128.IV );

        byte[] textBytes = Encoding.ASCII.GetBytes( tokenString );
        byte[] encryptedBytes = cryptoTransform.TransformFinalBlock( textBytes, 0, textBytes.Length );
        string encryptedString = Convert.ToBase64String( encryptedBytes );

        return encryptedString;
    }

    // Returns SHA256 hashed API key based on password and date
    private static string GenerateApiKey()
    {
        // Current UTC time
        DateTime now = DateTime.UtcNow;

        // Create key as SHA256 hash of "PASSWORD-YYYY-MM-DD-HH-MM" with UTC time
        string expectedKey = $"{Password}-{now:yyyy-MM-dd-HH}-{now.Minute / 5}";

        // Get bytes from string
        byte[] bytes = Encoding.ASCII.GetBytes( expectedKey );

        // Get SHA256 hash from bytes
        byte[] hash = SHA256.HashData( bytes );

        // Convert hash to hex string
        StringBuilder apiKey = new();

        foreach ( byte b in hash )
        {
            apiKey.Append( b.ToString( "x2" ) );
        }

        // Replace double quote with empty string
        return apiKey.ToString().Replace( "\"", string.Empty );
    }

    // Returns HTTP client used for all requests
    private static RestClient GetClient()
    {
        RestClient client = new( configureSerialization: s => s.UseSystemTextJson( new JsonSerializerOptions
        {
            DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
            PropertyNameCaseInsensitive = true,
            PropertyNamingPolicy = JsonNamingPolicy.CamelCase
        }));
        
        return client;
    }
}

//
