param(
  [int]$GatewayPort = 18889,
  [string]$GatewayToken = 'qjc-bundled-runtime-token',
  [int]$SmokePort = 4321,
  [string]$SmokeToken = 'smoke-token',
  [string]$BaseOutputDir,
  [int]$TimeoutSeconds = 180,
  [switch]$SkipMaterializeRuntime
)

$ErrorActionPreference = 'Stop'

$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
if (-not $BaseOutputDir) {
  $BaseOutputDir = Join-Path $repoRoot '.tmp\desktop-session-switch-stream-smoke'
}
$BaseOutputDir = [System.IO.Path]::GetFullPath($BaseOutputDir)
$userDataPath = Join-Path $BaseOutputDir 'user-data'
$logsPath = Join-Path $BaseOutputDir 'logs'
$smokeOutput = Join-Path $BaseOutputDir 'result.json'
$electronSmokeScript = Join-Path $repoRoot 'build\scripts\electron-smoke.ps1'

if (Test-Path $BaseOutputDir) {
  Remove-Item $BaseOutputDir -Recurse -Force -ErrorAction SilentlyContinue
}
New-Item -ItemType Directory -Force -Path $BaseOutputDir, $userDataPath, $logsPath | Out-Null

if (-not $SkipMaterializeRuntime) {
  Write-Host "Materializing bundled runtime payload on port $GatewayPort"
  powershell -ExecutionPolicy Bypass -File (Join-Path $repoRoot 'build\scripts\materialize-runtime-payload.ps1') -GatewayPort $GatewayPort -GatewayToken $GatewayToken
  if ($LASTEXITCODE -ne 0) {
    exit $LASTEXITCODE
  }
}

$copywritingApiKey = if ($env:QJCLAW_SMOKE_COPYWRITING_API_KEY) { $env:QJCLAW_SMOKE_COPYWRITING_API_KEY } else { 'runtime-provider-token' }
$smokeSettingsConfig = [ordered]@{
  copywriting = [ordered]@{
    baseUrl = "http://127.0.0.1:$SmokePort/openai/v1"
    apiKey = $copywritingApiKey
    modelId = 'gpt-5.4-mini'
  }
} | ConvertTo-Json -Depth 6 -Compress

$env:QJCLAW_SMOKE_SETTINGS_CONFIG_JSON = $smokeSettingsConfig
$env:QJCLAW_SMOKE_SCENARIO = 'session-switch-stream'

powershell -ExecutionPolicy Bypass -File $electronSmokeScript @(
  '-SmokeOutput', $smokeOutput,
  '-SmokePort', $SmokePort,
  '-SmokeToken', $SmokeToken,
  '-UserDataPath', $userDataPath,
  '-LogsPath', $logsPath,
  '-RuntimeMode', 'bundled-runtime',
  '-ExpectBundledRuntime',
  '-SmokeViewMode', 'chat',
  '-SmokePrompt', 'Smoke session switching while assistant is still responding.',
  '-TimeoutSeconds', $TimeoutSeconds
)
if ($LASTEXITCODE -ne 0) {
  exit $LASTEXITCODE
}

$summaryScript = @"
const fs = require('fs');
const [smokeOutput] = process.argv.slice(1);
const result = JSON.parse(fs.readFileSync(smokeOutput, 'utf8'));
if (!result.ok) {
  throw new Error(result.error || 'Desktop session-switch-stream smoke failed.');
}
const sendResult = result.sendResult || {};
const streamSmoke = sendResult.streamSmoke || {};
const scenario = sendResult.scenarioResult || {};
const visibleMessages = Array.isArray(scenario.visibleMessagesAfterReturn) ? scenario.visibleMessagesAfterReturn : [];
const cachedMessages = Array.isArray(scenario.cachedMessagesAfterReturn) ? scenario.cachedMessagesAfterReturn : [];
const userMessage = visibleMessages.find((message) => String(message.role || '') === 'user') || null;
const assistantMessage = [...visibleMessages].reverse().find((message) => String(message.role || '') === 'assistant') || null;
if (String(sendResult.smokeScenario || '') !== 'session-switch-stream') {
  throw new Error('Smoke did not report session-switch-stream scenario.');
}
if (Number(streamSmoke.startedEventCount || 0) < 1) {
  throw new Error('Stream did not emit a started event.');
}
if (Number(streamSmoke.errorEventCount || 0) !== 0) {
  throw new Error('Stream emitted unexpected error events: ' + Number(streamSmoke.errorEventCount || 0));
}
if (String(streamSmoke.phase || '') === 'error') {
  throw new Error('Session-switch stream ended in error: ' + String(streamSmoke.lastError || ''));
}
if (!String(scenario.streamSessionId || '').startsWith('project:home-chat:')) {
  throw new Error('Primary session was not a home-chat session: ' + String(scenario.streamSessionId || ''));
}
if (!String(scenario.alternateSessionId || '').startsWith('project:home-chat:')) {
  throw new Error('Alternate session was not a home-chat session: ' + String(scenario.alternateSessionId || ''));
}
if (String(scenario.switchedAwayActiveSessionId || '') !== String(scenario.alternateSessionId || '')) {
  throw new Error('Smoke did not remain on the alternate session after switching away.');
}
if (String(scenario.returnedActiveSessionId || '') !== String(scenario.streamSessionId || '')) {
  throw new Error('Smoke did not return to the streaming session.');
}
if (['completed', 'fallback', 'error'].includes(String(scenario.phaseBeforeSwitch || ''))) {
  throw new Error('Stream was already terminal before switching sessions: ' + String(scenario.phaseBeforeSwitch || ''));
}
if (['completed', 'fallback', 'error'].includes(String(scenario.phaseAtReturn || ''))) {
  throw new Error('Stream was already terminal when the smoke returned to the original session: ' + String(scenario.phaseAtReturn || ''));
}
if (Number(scenario.completedEventCountAtReturn || 0) !== 0) {
  throw new Error('Stream had already completed by the time the smoke returned to the original session.');
}
if (visibleMessages.length < 2) {
  throw new Error('Visible messages after returning were incomplete: ' + visibleMessages.length);
}
if (cachedMessages.length < 2) {
  throw new Error('Cached messages after returning were incomplete: ' + cachedMessages.length);
}
if (!userMessage || !String(userMessage.content || '').includes('Smoke session switching while assistant is still responding.')) {
  throw new Error('Returned user message was missing or incorrect.');
}
if (!assistantMessage) {
  throw new Error('Returned assistant placeholder was missing.');
}
console.log(JSON.stringify({
  ok: true,
  smokeOutput,
  streamSessionId: scenario.streamSessionId || null,
  alternateSessionId: scenario.alternateSessionId || null,
  returnedVisibleMessageCount: visibleMessages.length,
  returnedCachedMessageCount: cachedMessages.length,
  phaseBeforeSwitch: scenario.phaseBeforeSwitch || null,
  phaseAtReturn: scenario.phaseAtReturn || null,
  streamPhase: streamSmoke.phase || null,
  startedEventCount: Number(streamSmoke.startedEventCount || 0),
  deltaEventCount: Number(streamSmoke.deltaEventCount || 0),
  completedEventCount: Number(streamSmoke.completedEventCount || 0)
}, null, 2));
"@

$summary = & node -e $summaryScript $smokeOutput
if ($LASTEXITCODE -ne 0) {
  exit $LASTEXITCODE
}

Write-Output $summary
