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

$ErrorActionPreference = 'Stop'

function Write-Utf8File {
  param([string]$FilePath, [string]$Content)
  $encoding = New-Object System.Text.UTF8Encoding $false
  [System.IO.Directory]::CreateDirectory([System.IO.Path]::GetDirectoryName($FilePath)) | Out-Null
  [System.IO.File]::WriteAllText($FilePath, $Content, $encoding)
}

function New-ExpertFixtureProject {
  param(
    [string]$ProjectsRoot,
    [string]$ProjectId,
    [string]$ProjectName,
    [string]$Platform,
    [string]$Description,
    [string]$ReadmeBody,
    [string]$UpdatedAt
  )

  $projectRoot = Join-Path $ProjectsRoot $ProjectId
  New-Item -ItemType Directory -Force -Path $projectRoot, (Join-Path $projectRoot 'memory') | Out-Null

  $projectPayload = [ordered]@{
    id = $ProjectId
    name = $ProjectName
    description = $Description
    platform = $Platform
    ready = $true
    updatedAt = $UpdatedAt
    boundSkillIds = @()
    workspaceEntryEnabled = $true
  }
  Write-Utf8File (Join-Path $projectRoot 'project.json') ($projectPayload | ConvertTo-Json -Depth 8)
  Write-Utf8File (Join-Path $projectRoot 'README.md') ("# $ProjectName`n`n$ReadmeBody")
  Write-Utf8File (Join-Path $projectRoot 'AGENTS.md') "# $ProjectName`n`nPassive expert fixture for desktop home intent suggestion smoke."
}

function Initialize-SmokeUserData {
  param(
    [string]$UserDataPath
  )

  $projectsRoot = Join-Path $UserDataPath 'projects'
  $manifestsRoot = Join-Path $UserDataPath 'manifests'
  if (Test-Path $UserDataPath) {
    Remove-Item $UserDataPath -Recurse -Force -ErrorAction SilentlyContinue
  }
  New-Item -ItemType Directory -Force -Path $UserDataPath, $projectsRoot, $manifestsRoot | Out-Null

  New-ExpertFixtureProject -ProjectsRoot $projectsRoot -ProjectId 'xhs' -ProjectName 'Xiaohongshu Expert Workspace' -Platform 'xiaohongshu' -Description '小红书运营专家，负责小红书文案、笔记、发布时间和发布流程。' -ReadmeBody '小红书专家夹具。处理小红书护肤笔记、文案、发布时间建议和发布任务。' -UpdatedAt '2026-04-16T00:00:00.000Z'
  New-ExpertFixtureProject -ProjectsRoot $projectsRoot -ProjectId 'douyin-expert-smoke' -ProjectName 'Douyin Expert Workspace' -Platform 'douyin' -Description '抖音运营专家，负责抖音脚本、镜头节奏和发布建议。' -ReadmeBody '抖音专家夹具。处理抖音脚本、镜头节奏和口播任务。' -UpdatedAt '2026-04-16T00:01:00.000Z'

  Write-Utf8File (Join-Path $manifestsRoot 'active-project.json') (@{ projectId = 'douyin-expert-smoke' } | ConvertTo-Json -Depth 3)
}

function Invoke-HomeIntentScenario {
  param(
    [string]$ScenarioName,
    [string]$SuggestionAction,
    [string]$Prompt,
    [string]$BaseOutputDir,
    [string]$ElectronSmokeScript,
    [int]$SmokePort,
    [string]$SmokeToken,
    [int]$TimeoutSeconds
  )

  $scenarioRoot = Join-Path $BaseOutputDir $ScenarioName
  $userDataPath = Join-Path $scenarioRoot 'user-data'
  $logsPath = Join-Path $scenarioRoot 'logs'
  $smokeOutput = Join-Path $scenarioRoot 'result.json'

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

  $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

  powershell -ExecutionPolicy Bypass -File $ElectronSmokeScript @(
    '-SmokeOutput', $smokeOutput,
    '-SmokePort', $SmokePort,
    '-SmokeToken', $SmokeToken,
    '-UserDataPath', $userDataPath,
    '-LogsPath', $logsPath,
    '-RuntimeMode', 'bundled-runtime',
    '-ExpectBundledRuntime',
    '-PreserveUserData',
    '-SmokePrompt', $Prompt,
    '-SmokeViewMode', 'chat',
    '-SmokeSuggestionAction', $SuggestionAction,
    '-TimeoutSeconds', $TimeoutSeconds
  )
  if ($LASTEXITCODE -ne 0) {
    exit $LASTEXITCODE
  }

  $summary = & node -e @"
const fs = require('fs');
const path = require('path');
const [scenarioName, smokeOutput, expectedAction, expectedPrompt] = process.argv.slice(1);
const result = JSON.parse(fs.readFileSync(smokeOutput, 'utf8'));
if (!result.ok) {
  throw new Error(result.error || (scenarioName + ' smoke failed.'));
}
const sendResult = result.sendResult || {};
const streamSmoke = sendResult.streamSmoke || {};
const suggestion = sendResult.homeIntentSuggestion || {};
const actionResult = sendResult.homeIntentActionResult || {};
const finalState = result.finalState || {};
const finalWorkspace = finalState.workspaceSummary || {};
const finalSessionId = String(sendResult.sessionId || '');
const streamSessionId = String(streamSmoke.sessionId || '');
if (String(sendResult.homeIntentAction || '') !== expectedAction) {
  throw new Error('Unexpected home intent action: ' + String(sendResult.homeIntentAction || ''));
}
if (String(sendResult.smokeScenario || '') !== 'home-intent-suggestion') {
  throw new Error('Smoke did not use the home-intent suggestion scenario exit: ' + String(sendResult.smokeScenario || ''));
}
if (String(suggestion.projectId || '') !== 'xhs') {
  throw new Error('Suggestion did not target xhs: ' + String(suggestion.projectId || ''));
}
if (String(suggestion.pendingPrompt || '') !== expectedPrompt) {
  throw new Error('Suggestion pending prompt mismatch.');
}
if (String(sendResult.prompt || '') !== expectedPrompt) {
  throw new Error('Final send prompt mismatch.');
}
if (String(sendResult.smokeViewMode || '') !== 'chat') {
  throw new Error('Smoke did not run in chat view.');
}
if (String(sendResult.smokeProjectId || '')) {
  throw new Error('Smoke unexpectedly targeted a fixed project: ' + String(sendResult.smokeProjectId || ''));
}
if (!['requested', 'started', 'streaming', 'completed'].includes(String(streamSmoke.phase || ''))) {
  throw new Error('Stream did not start after home intent decision: ' + String(streamSmoke.phase || ''));
}
if (!streamSessionId) {
  throw new Error('Home intent decision did not publish a stream session id.');
}
if (sendResult.homeIntentDismissed) {
  throw new Error('Suggestion was unexpectedly marked dismissed.');
}
if (sendResult.homeIntentSuggestionVisible) {
  throw new Error('Suggestion remained visible after decision.');
}
if (sendResult.pendingHomeIntentPrompt) {
  throw new Error('Pending home intent prompt was not cleared.');
}
if (expectedAction === 'continue-home') {
  if (!actionResult.continued) {
    throw new Error('Continue-home action did not report continued=true.');
  }
  if (!streamSessionId.startsWith('project:home-chat:')) {
    throw new Error('Continue-home did not send inside home-chat: ' + streamSessionId);
  }
} else if (expectedAction === 'switch-expert') {
  if (!actionResult.switched || String(actionResult.projectId || '') !== 'xhs') {
    throw new Error('Switch-expert action did not report switched xhs.');
  }
  if (!streamSessionId.startsWith('project:xhs:')) {
    throw new Error('Switch-expert did not send inside xhs: ' + streamSessionId);
  }
  if (String(sendResult.currentProjectId || '') !== 'xhs') {
    throw new Error('Switch-expert post-stream currentProjectId mismatch: ' + String(sendResult.currentProjectId || ''));
  }
  if (String(finalWorkspace.currentProjectId || '') !== 'xhs') {
    throw new Error('Switch-expert final workspace project mismatch: ' + String(finalWorkspace.currentProjectId || ''));
  }
} else {
  throw new Error('Unsupported expected action: ' + expectedAction);
}
console.log(JSON.stringify({
  ok: true,
  scenarioName,
  smokeOutput,
  action: sendResult.homeIntentAction || null,
  suggestedProjectId: suggestion.projectId || null,
  suggestedProjectName: suggestion.projectName || null,
  sessionId: streamSessionId || finalSessionId || null,
  currentProjectId: finalWorkspace.currentProjectId || null,
  streamPhase: streamSmoke.phase || null,
  messageCount: sendResult.messageCount || null
}, null, 2));
"@ $ScenarioName $smokeOutput $SuggestionAction $Prompt
  if ($LASTEXITCODE -ne 0) {
    exit $LASTEXITCODE
  }
  Write-Output $summary
}

$repoRoot = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
if (-not $BaseOutputDir) {
  $BaseOutputDir = Join-Path $repoRoot '.tmp\desktop-home-intent-suggestion-smoke'
}
$BaseOutputDir = [System.IO.Path]::GetFullPath($BaseOutputDir)
$electronSmokeScript = Join-Path $repoRoot 'build\scripts\electron-smoke.ps1'
$homeIntentPrompt = 'Help me write an xhs skincare note and suggest the publishing time.'

if (Test-Path $BaseOutputDir) {
  Remove-Item $BaseOutputDir -Recurse -Force -ErrorAction SilentlyContinue
}
New-Item -ItemType Directory -Force -Path $BaseOutputDir | 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
  }
}

Invoke-HomeIntentScenario -ScenarioName 'continue-home' -SuggestionAction 'continue-home' -Prompt $homeIntentPrompt -BaseOutputDir $BaseOutputDir -ElectronSmokeScript $electronSmokeScript -SmokePort $SmokePort -SmokeToken $SmokeToken -TimeoutSeconds $TimeoutSeconds
Invoke-HomeIntentScenario -ScenarioName 'switch-expert' -SuggestionAction 'switch-expert' -Prompt $homeIntentPrompt -BaseOutputDir $BaseOutputDir -ElectronSmokeScript $electronSmokeScript -SmokePort $SmokePort -SmokeToken $SmokeToken -TimeoutSeconds $TimeoutSeconds
