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 expert-entry 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 'content-account-planning-smoke' -ProjectName 'Content Account Planning Expert Workspace' -Platform 'content-account-planning' -Description 'Standalone expert fixture for account planning.' -ReadmeBody 'Used to validate standalone expert entry routing and session isolation.' -UpdatedAt '2026-04-16T00:00:00.000Z'
  New-ExpertFixtureProject -ProjectsRoot $projectsRoot -ProjectId 'zhihu-smoke' -ProjectName 'Zhihu Expert Workspace' -Platform 'zhihu' -Description 'Standalone expert fixture for Zhihu.' -ReadmeBody 'Used to validate standalone expert entry routing and session isolation.' -UpdatedAt '2026-04-16T00:01:00.000Z'
  New-ExpertFixtureProject -ProjectsRoot $projectsRoot -ProjectId 'douyin-expert-smoke' -ProjectName 'Douyin Expert Workspace' -Platform 'douyin' -Description 'Non-standalone expert fixture to verify experts page filtering.' -ReadmeBody 'Ensures workspace still contains other normal expert-like projects.' -UpdatedAt '2026-04-16T00:02:00.000Z'

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

function Invoke-ExpertEntryScenario {
  param(
    [string]$ScenarioName,
    [string]$SmokeExpertEntryId,
    [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

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

  $summaryScript = @"
const fs = require('fs');
const [scenarioName, smokeOutput, expectedExpertEntryId] = 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 finalState = result.finalState || {};
const finalWorkspace = finalState.workspaceSummary || {};
const expertEntry = sendResult.expertEntry || {};
const experts = finalState.experts || {};
const finalSessions = Array.isArray(finalState.sessions) ? finalState.sessions : [];
const standaloneIds = Array.isArray(experts.standaloneIds) ? experts.standaloneIds.map((value) => String(value || '')).sort() : [];
const homeShortcutIds = Array.isArray(experts.homeShortcutIds) ? experts.homeShortcutIds.map((value) => String(value || '')).sort() : [];
const standalonePromptAvailableIds = Array.isArray(experts.standalonePromptAvailableIds) ? experts.standalonePromptAvailableIds.map((value) => String(value || '')).sort() : [];
const expectedStandaloneIds = ['content-account-planning', 'zhihu'];
const expectedHomeShortcutIds = ['geo', 'poster', 'precision-leads', 'sales-champion', 'tiktok', 'wechat-official-account', 'x-platform'];
if (String(sendResult.smokeExpertEntryId || '') !== expectedExpertEntryId) {
  throw new Error('Smoke did not report the expected expert entry id: ' + String(sendResult.smokeExpertEntryId || ''));
}
if (String(expertEntry.expertId || '') !== expectedExpertEntryId) {
  throw new Error('Expert action result expertId mismatch: ' + String(expertEntry.expertId || ''));
}
if (standaloneIds.length !== 2) {
  throw new Error('Unexpected standalone expert count: ' + standaloneIds.length);
}
if (homeShortcutIds.length !== 7) {
  throw new Error('Unexpected home shortcut count: ' + homeShortcutIds.length);
}
if (standalonePromptAvailableIds.length !== 2) {
  throw new Error('Unexpected standalone prompt-available count: ' + standalonePromptAvailableIds.length);
}
if (JSON.stringify(standaloneIds) !== JSON.stringify(expectedStandaloneIds)) {
  throw new Error('Standalone expert ids mismatch: ' + JSON.stringify(standaloneIds));
}
if (JSON.stringify(homeShortcutIds) !== JSON.stringify(expectedHomeShortcutIds)) {
  throw new Error('Home shortcut ids mismatch: ' + JSON.stringify(homeShortcutIds));
}
if (JSON.stringify(standalonePromptAvailableIds) !== JSON.stringify(expectedStandaloneIds)) {
  throw new Error('Standalone prompt availability mismatch: ' + JSON.stringify(standalonePromptAvailableIds));
}
if (expectedStandaloneIds.includes(expectedExpertEntryId)) {
  if (String(finalState.viewMode || '') !== 'experts') {
    throw new Error('Standalone expert did not land on experts view.');
  }
  if (String(expertEntry.entryMode || '') !== 'standalone') {
    throw new Error('Standalone expert entryMode mismatch: ' + String(expertEntry.entryMode || ''));
  }
  if (!String(expertEntry.currentProjectId || '').includes(expectedExpertEntryId)) {
    throw new Error('Standalone expert did not resolve expected project: ' + String(expertEntry.currentProjectId || ''));
  }
  if (String(finalWorkspace.currentProjectId || '') !== String(expertEntry.currentProjectId || '')) {
    throw new Error('Standalone expert final project mismatch: ' + String(finalWorkspace.currentProjectId || ''));
  }
  if (String(finalState.ui && finalState.ui.sessionScopeProjectId || '') !== String(finalWorkspace.currentProjectId || '')) {
    throw new Error('Standalone expert session scope mismatch: ' + String(finalState.ui && finalState.ui.sessionScopeProjectId || ''));
  }
  if (finalSessions.length < 1) {
    throw new Error('Standalone expert did not expose any sessions for project: ' + String(finalWorkspace.currentProjectId || ''));
  }
  const foreignSession = finalSessions.find((session) => String(session && session.projectId || '') !== String(finalWorkspace.currentProjectId || ''));
  if (foreignSession) {
    throw new Error('Standalone expert exposed a session from another project: ' + JSON.stringify(foreignSession));
  }
} else if (expectedHomeShortcutIds.includes(expectedExpertEntryId)) {
  if (String(finalState.viewMode || '') !== 'chat') {
    throw new Error('Home shortcut did not land on chat view.');
  }
  if (String(expertEntry.entryMode || '') !== 'home-chat-shortcut') {
    throw new Error('Home shortcut entryMode mismatch: ' + String(expertEntry.entryMode || ''));
  }
  if (String(finalState.ui && finalState.ui.sessionScopeProjectId || '') !== 'home-chat') {
    throw new Error('Home shortcut changed the session scope: ' + String(finalState.ui && finalState.ui.sessionScopeProjectId || ''));
  }
  if (String(expertEntry.sessionScopeProjectId || '') !== 'home-chat') {
    throw new Error('Home shortcut post-action session scope mismatch: ' + String(expertEntry.sessionScopeProjectId || ''));
  }
  if (Number(sendResult.messageCount || 0) !== 0) {
    throw new Error('Home shortcut unexpectedly created chat messages: ' + Number(sendResult.messageCount || 0));
  }
  if (String(sendResult.pendingHomeIntentPrompt || '')) {
    throw new Error('Home shortcut unexpectedly left a pending home intent prompt.');
  }
  if (!String(expertEntry.prompt || '').trim()) {
    throw new Error('Home shortcut did not surface a starter prompt.');
  }
} else {
  throw new Error('Unsupported scenario expectation: ' + expectedExpertEntryId);
}
console.log(JSON.stringify({
  ok: true,
  scenarioName,
  smokeOutput,
  expertEntryId: expectedExpertEntryId,
  entryMode: expertEntry.entryMode || null,
  finalViewMode: finalState.viewMode || null,
  currentProjectId: finalWorkspace.currentProjectId || null,
  sessionScopeProjectId: finalState.ui && finalState.ui.sessionScopeProjectId || null,
  sessionCount: finalSessions.length,
  standaloneIds,
  homeShortcutIds,
  standalonePromptAvailableIds
}, null, 2));
"@

  $summary = & node -e $summaryScript $ScenarioName $smokeOutput $SmokeExpertEntryId
  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-expert-entry-smoke'
}
$BaseOutputDir = [System.IO.Path]::GetFullPath($BaseOutputDir)
$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 | 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-ExpertEntryScenario -ScenarioName 'standalone-content-account-planning' -SmokeExpertEntryId 'content-account-planning' -BaseOutputDir $BaseOutputDir -ElectronSmokeScript $electronSmokeScript -SmokePort $SmokePort -SmokeToken $SmokeToken -TimeoutSeconds $TimeoutSeconds
Invoke-ExpertEntryScenario -ScenarioName 'standalone-zhihu' -SmokeExpertEntryId 'zhihu' -BaseOutputDir $BaseOutputDir -ElectronSmokeScript $electronSmokeScript -SmokePort $SmokePort -SmokeToken $SmokeToken -TimeoutSeconds $TimeoutSeconds
Invoke-ExpertEntryScenario -ScenarioName 'home-shortcut-wechat-official-account' -SmokeExpertEntryId 'wechat-official-account' -BaseOutputDir $BaseOutputDir -ElectronSmokeScript $electronSmokeScript -SmokePort $SmokePort -SmokeToken $SmokeToken -TimeoutSeconds $TimeoutSeconds
Invoke-ExpertEntryScenario -ScenarioName 'home-shortcut-x-platform' -SmokeExpertEntryId 'x-platform' -BaseOutputDir $BaseOutputDir -ElectronSmokeScript $electronSmokeScript -SmokePort $SmokePort -SmokeToken $SmokeToken -TimeoutSeconds $TimeoutSeconds
Invoke-ExpertEntryScenario -ScenarioName 'home-shortcut-tiktok' -SmokeExpertEntryId 'tiktok' -BaseOutputDir $BaseOutputDir -ElectronSmokeScript $electronSmokeScript -SmokePort $SmokePort -SmokeToken $SmokeToken -TimeoutSeconds $TimeoutSeconds
Invoke-ExpertEntryScenario -ScenarioName 'home-shortcut-poster' -SmokeExpertEntryId 'poster' -BaseOutputDir $BaseOutputDir -ElectronSmokeScript $electronSmokeScript -SmokePort $SmokePort -SmokeToken $SmokeToken -TimeoutSeconds $TimeoutSeconds
Invoke-ExpertEntryScenario -ScenarioName 'home-shortcut-geo' -SmokeExpertEntryId 'geo' -BaseOutputDir $BaseOutputDir -ElectronSmokeScript $electronSmokeScript -SmokePort $SmokePort -SmokeToken $SmokeToken -TimeoutSeconds $TimeoutSeconds
Invoke-ExpertEntryScenario -ScenarioName 'home-shortcut-precision-leads' -SmokeExpertEntryId 'precision-leads' -BaseOutputDir $BaseOutputDir -ElectronSmokeScript $electronSmokeScript -SmokePort $SmokePort -SmokeToken $SmokeToken -TimeoutSeconds $TimeoutSeconds
Invoke-ExpertEntryScenario -ScenarioName 'home-shortcut-sales-champion' -SmokeExpertEntryId 'sales-champion' -BaseOutputDir $BaseOutputDir -ElectronSmokeScript $electronSmokeScript -SmokePort $SmokePort -SmokeToken $SmokeToken -TimeoutSeconds $TimeoutSeconds
