modules/Instance.psm1

using module '.\Enums.psm1'
using module '.\Module.psm1'

class Instance {
    [OPERATOR] $operator
    [string] $brand
    [ENVIRONMENT] $environment
    [string] $domain
    [string] $key
    [string] $secret
    [MODULE[]] $modules
    [PSCustomObject] $extension
    [int] $tokenIatCorrectionMilliseconds

    [hashtable] $versions
    [hashtable] $versionSubModules

    Instance([OPERATOR]$operator, [string]$brand, [ENVIRONMENT]$environment, [string] $domain, [string] $key, [string] $secret, `
        [MODULE[]]$modules, [PSCustomObject] $extension, [int]$tokenIatCorrectionMilliseconds) {

        $this.operator = $operator
        $this.brand = $brand
        $this.environment = $environment
        $this.domain = $domain
        $this.key = $key
        $this.secret = $secret
        $this.modules = $modules
        $this.extension = $extension
        $this.tokenIatCorrectionMilliseconds = $tokenIatCorrectionMilliseconds

        $this.versions = @{}
        $this.versionSubModules = @{}
    }

    [void] GetVersions([MODULE[]]$module, [hashtable] $appModule){
        foreach ($m in $this.modules) {
            if ($m -in $module -and -not ($this.versions.ContainsKey($m))) {
                $retVersions = $appModule[$m].GetVersion($this.extension, $this.domain, $this.getHeaders())

                if ($retVersions.Count -eq 1){
                    $this.versions += $retVersions
                }
                else {
                    $this.versions.Add($m, $null)
                    $this.versionSubModules.Add($m, @{})
                    foreach ($item in $retVersions.GetEnumerator()) {
                        if ($item.Key -eq $m){
                            $this.versions[$m] = $item.Value
                        }
                        else {
                            $this.versionSubModules[$m].Add($item.key, $item.Value)
                        }
                    }
                }
            }
        }
    }

    [PSCustomObject] ToOutput([MODULE[]]$module){
        $retArray = $null

        foreach ($m in $module) {
            $version = $this.versions[$m]

            $retValue = $null
            if ($version){
                $retValue = [PSCustomObject]@{PSTypeName='DisplayVersion'; operator=$this.operator; brand=$this.brand; env=$this.environment; module=$m; version=$version.ToString() }
                if ($this.versionSubModules[$m]){
                    foreach ($item in $this.versionSubModules[$m].GetEnumerator()) {
                        $retValue | Add-Member $item.key $item.value.ToString()
                    }
                }
                $retArray += $retValue
            }
        }

        return $retArray
    }

    [string[]] GetSubmodules([MODULE[]]$module){
        $retArray = @()

        foreach ($m in $module) {
            $version = $this.versions[$m]

            if ($version -and $this.versionSubModules[$m]){
                foreach ($item in $this.versionSubModules[$m].GetEnumerator()) {
                    if (-not ($item.key -in $retArray)) {
                        $retArray += $item.key
                    }
                }
            }
        }

        return $retArray
    }

    hidden [hashtable] getHeaders(){

        $token = $this.jsonWebToken("HS256", 'JWT', $this.key, $this.secret, $this.tokenIatCorrectionMilliseconds, 'bookmakers')
        return @{'Authorization' = 'Bearer ' + $token}
    }

    hidden [string] jsonWebToken([string]$algorithm, [string]$type, [string]$issuer, [string]$secretKey, [int]$correctionMilliseconds, [string]$sub){

        $iat = [int][double]::parse((Get-Date -Date $((Get-Date).AddMilliseconds($correctionMilliseconds).ToUniversalTime()) -UFormat %s)) # Grab Unix Epoch Timestamp of the creation
        #$exp = [int][double]::parse((Get-Date -Date $((Get-Date).AddSeconds($validForSeconds).ToUniversalTime()) -UFormat %s)) # Grab Unix Epoch Timestamp and add desired expiration.

        [hashtable]$header = @{alg = $Algorithm; typ = $type}
        [hashtable]$payload = @{sub=$sub; iss = $Issuer; iat = $iat}

        $headerjson = $header | ConvertTo-Json -Compress
        $payloadjson = $payload | ConvertTo-Json -Compress

        $headerjsonbase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($headerjson)).Split('=')[0] #.Replace('+', '-').Replace('/', '_')
        $payloadjsonbase64 = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes($payloadjson)).Split('=')[0] #.Replace('+', '-').Replace('/', '_')

        $ToBeSigned = $headerjsonbase64 + "." + $payloadjsonbase64

        $SigningAlgorithm = switch ($Algorithm) {
            "HS256" {New-Object System.Security.Cryptography.HMACSHA256}
            "HS384" {New-Object System.Security.Cryptography.HMACSHA384}
            "HS512" {New-Object System.Security.Cryptography.HMACSHA512}
        }

        $SigningAlgorithm.Key = [System.Text.Encoding]::UTF8.GetBytes($SecretKey)
        $Signature = [Convert]::ToBase64String($SigningAlgorithm.ComputeHash([System.Text.Encoding]::UTF8.GetBytes($ToBeSigned))).Split('=')[0] #.Replace('+', '-').Replace('/', '_')

        $token = "$headerjsonbase64.$payloadjsonbase64.$Signature"

        return $token
    }
}