$ErrorActionPreference = 'Stop'
Set-StrictMode -Version Latest

$campaignId = 'AIBLACK-300-20260830T183052882Z'
$endpoint = 'https://lanxas-ia.145.239.141.177.sslip.io/black-chat-json'
$timeoutSeconds = 1500
$runnerRoot = Split-Path -Parent $MyInvocation.MyCommand.Path
$outRoot = Join-Path $runnerRoot 'RUNNER_OUT'
$responsesRoot = Join-Path $outRoot 'reponses'
$csvPath = Join-Path $outRoot 'campagne_log.csv'
$hashPath = Join-Path $outRoot 'SHA256_TEST_BLANC.txt'
$lockPath = Join-Path $runnerRoot 'TEST_BLANC_EXECUTED.lock'
$utf8NoBom = [System.Text.UTF8Encoding]::new($false)
$invariant = [System.Globalization.CultureInfo]::InvariantCulture

if (Test-Path -LiteralPath $lockPath) {
    throw 'ANTI_REPLAY_LOCK_PRESENT'
}
if (Test-Path -LiteralPath $outRoot) {
    throw 'RUNNER_OUT_ALREADY_EXISTS'
}

$lockStream = [System.IO.File]::Open(
    $lockPath,
    [System.IO.FileMode]::CreateNew,
    [System.IO.FileAccess]::Write,
    [System.IO.FileShare]::None
)
try {
    $lockText = "campaign_id=$campaignId`ncreated_utc=$((Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ', $invariant))`nmax_attempts=1`n"
    $lockBytes = $utf8NoBom.GetBytes($lockText)
    $lockStream.Write($lockBytes, 0, $lockBytes.Length)
}
finally {
    $lockStream.Dispose()
}

[System.IO.Directory]::CreateDirectory($responsesRoot) | Out-Null
$csvHeader = "id;timestamp_utc_envoi;http_status;latence_s;taille_octets;sha256_reponse`n"
[System.IO.File]::WriteAllText($csvPath, $csvHeader, $utf8NoBom)

$tbR1 = 'Test blanc Runner neutre TB_R1, distinct de toute épreuve. Répondez sur une seule ligne avec exactement ALPHA|RUNNER|OMEGA, sans espace, sans ponctuation ajoutée et sans autre texte.'
$tbR2 = @'
Test blanc Runner neutre TB_R2, distinct de toute épreuve. Répondez exactement avec les trois lignes suivantes, en JSON valide, sans indentation, sans bloc de code et sans texte avant ou après :
{
"test":"TB_R2","value":"A|B|C"
}
'@

$tests = @(
    [pscustomobject]@{ Id = 'TB_R1'; Question = $tbR1 },
    [pscustomobject]@{ Id = 'TB_R2'; Question = $tbR2 }
)

$handler = [System.Net.Http.HttpClientHandler]::new()
$client = [System.Net.Http.HttpClient]::new($handler)
$client.Timeout = [System.TimeSpan]::FromSeconds($timeoutSeconds)

try {
    foreach ($test in $tests) {
        $responsePath = Join-Path $responsesRoot ($test.Id + '.json')
        if (Test-Path -LiteralPath $responsePath) {
            throw "ANTI_REPLAY_RESPONSE_EXISTS:$($test.Id)"
        }

        $requestObject = [ordered]@{
            messages = @([ordered]@{ role = 'user'; content = $test.Question })
            mode = 'black'
            power = 3
        }
        $requestJson = $requestObject | ConvertTo-Json -Compress -Depth 5
        $requestBytes = $utf8NoBom.GetBytes($requestJson)
        $content = [System.Net.Http.ByteArrayContent]::new($requestBytes)
        $content.Headers.ContentType = [System.Net.Http.Headers.MediaTypeHeaderValue]::new('application/json')
        $content.Headers.ContentType.CharSet = 'utf-8'

        $timestampUtc = (Get-Date).ToUniversalTime().ToString('yyyy-MM-ddTHH:mm:ss.fffZ', $invariant)
        $stopwatch = [System.Diagnostics.Stopwatch]::StartNew()
        $status = 'CLIENT_ERROR'
        $rawResponse = [byte[]]::new(0)

        try {
            $response = $client.PostAsync($endpoint, $content).GetAwaiter().GetResult()
            try {
                $status = ([int]$response.StatusCode).ToString($invariant)
                $rawResponse = $response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult()
            }
            finally {
                $response.Dispose()
            }
        }
        catch [System.Threading.Tasks.TaskCanceledException] {
            $status = 'TIMEOUT'
        }
        catch {
            $status = 'CLIENT_ERROR'
        }
        finally {
            $stopwatch.Stop()
            $content.Dispose()
        }

        [System.IO.File]::WriteAllBytes($responsePath, $rawResponse)
        $sha256 = [Convert]::ToHexString(
            [System.Security.Cryptography.SHA256]::HashData($rawResponse)
        ).ToLowerInvariant()
        $latency = $stopwatch.Elapsed.TotalSeconds.ToString('F6', $invariant)
        $csvLine = "$($test.Id);$timestampUtc;$status;$latency;$($rawResponse.Length);$sha256`n"
        [System.IO.File]::AppendAllText($csvPath, $csvLine, $utf8NoBom)
    }
}
finally {
    $client.Dispose()
    $handler.Dispose()
}

$hashLines = foreach ($test in $tests) {
    $responsePath = Join-Path $responsesRoot ($test.Id + '.json')
    $hash = (Get-FileHash -LiteralPath $responsePath -Algorithm SHA256).Hash.ToLowerInvariant()
    "$hash  reponses/$($test.Id).json"
}
[System.IO.File]::WriteAllLines($hashPath, $hashLines, $utf8NoBom)

Write-Output 'TEST_BLANC_RUNNER_DONE'
