param(
  [string]$ProjectRoot = "",
  [switch]$AsJson
)

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

if ([string]::IsNullOrWhiteSpace($ProjectRoot)) {
  $ProjectRoot = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot "..\..\..\.."))
}

function New-Issue {
  param(
    [string]$Severity,
    [string]$File,
    [string]$Section,
    [string]$Message
  )

  [PSCustomObject]@{
    severity = $Severity
    file = $File
    section = $Section
    message = $Message
  }
}

function Get-FileContentOrNull {
  param([string]$Path)

  if (-not (Test-Path -LiteralPath $Path)) {
    return $null
  }

  Get-Content -LiteralPath $Path -Raw
}

function Resolve-FirstExistingPath {
  param([string[]]$Candidates)

  foreach ($candidate in $Candidates) {
    if (Test-Path -LiteralPath $candidate) {
      return $candidate
    }
  }

  return $null
}

function Test-ContainsAll {
  param(
    [string]$Content,
    [string[]]$Terms
  )

  foreach ($term in $Terms) {
    if ($Content -notmatch [regex]::Escape($term)) {
      return $false
    }
  }

  return $true
}

$issues = @()

$implementPromptPath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot ".codex\prompts\implement-one-task.md"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\prompts\implement-one-task.md")
)
$stage2PromptPath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot ".codex\prompts\review-stage2-quality.md"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\prompts\review-stage2-quality.md")
)
$architectureRulePath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot ".codex\rules\architecture.md"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\config\rules\architecture.md")
)
$codingRulePath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot ".codex\rules\coding-style.md"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\config\rules\coding-style.md")
)
$constitutionPath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot ".codex\rules\constitution.md"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\config\rules\constitution.md")
)
$verifyPath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot "tools\harness\verify.ps1"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\runtime\verify.ps1")
)
$traceSchemaPath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot "docs\harness\trace.schema.json"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\trace\trace.schema.json")
)
$bootstrapPath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot "tools\install\bootstrap-codex-harness.ps1"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\bootstrap-codex-harness.ps1")
)
$installSmokePath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot "tools\harness\tests\test-install-modes.ps1")
)

$implementPrompt = Get-FileContentOrNull -Path $implementPromptPath
if ($null -eq $implementPrompt -or -not (Test-ContainsAll -Content $implementPrompt -Terms @('只处理 Driver Context 中指定的一个任务', '不要处理 `task.json` 中其他任务', '不要修改与当前任务无关的文件', '不要修改公共 API', '不要静默吞错', '不要在日志、trace、截图、文档或测试样本中记录 token'))) {
  $issues += New-Issue -Severity "error" -File ".codex/prompts/implement-one-task.md" -Section "prompt" -Message "implement prompt 缺少单任务、公共 API、异常处理或敏感信息执行边界。"
}

$stage2Prompt = Get-FileContentOrNull -Path $stage2PromptPath
if ($null -eq $stage2Prompt -or -not (Test-ContainsAll -Content $stage2Prompt -Terms @('无关 diff', '未批准依赖变更', '公共 API', '静默吞错', '敏感信息'))) {
  $issues += New-Issue -Severity "error" -File ".codex/prompts/review-stage2-quality.md" -Section "prompt" -Message "Stage 2 prompt 缺少执行边界、高风险公共契约、异常处理或敏感信息检查。"
}

$architectureRule = Get-FileContentOrNull -Path $architectureRulePath
if ($null -eq $architectureRule -or -not (Test-ContainsAll -Content $architectureRule -Terms @('owned_paths', 'forbidden_paths', 'allowed_dependencies', '公共 API', 'package API', '文件格式'))) {
  $issues += New-Issue -Severity "error" -File ".codex/rules/architecture.md" -Section "rules" -Message "architecture 规则缺少公共 API / 契约边界硬约束。"
}

$codingRule = Get-FileContentOrNull -Path $codingRulePath
if ($null -eq $codingRule -or -not (Test-ContainsAll -Content $codingRule -Terms @('不吞异常', '空 `catch`', '伪成功值', '敏感信息输出'))) {
  $issues += New-Issue -Severity "error" -File ".codex/rules/coding-style.md" -Section "rules" -Message "coding-style 规则缺少静默吞错或敏感信息输出禁令。"
}

$constitution = Get-FileContentOrNull -Path $constitutionPath
if ($null -eq $constitution -or -not (Test-ContainsAll -Content $constitution -Terms @('日志不得包含 token', '不能静默吞错'))) {
  $issues += New-Issue -Severity "error" -File ".codex/rules/constitution.md" -Section "rules" -Message "constitution 缺少敏感信息或错误处理治理条款。"
}

$verify = Get-FileContentOrNull -Path $verifyPath
if ($null -eq $verify -or -not (Test-ContainsAll -Content $verify -Terms @('Invoke-ContractValidation', 'Invoke-BackendValidation', 'Invoke-SecurityValidation', 'Invoke-ExecutionBoundaryValidation', 'execution-boundary-lint.ps1'))) {
  $issues += New-Issue -Severity "error" -File "tools/harness/verify.ps1" -Section "verify" -Message "verify 没有接入 contract/backend/security/execution-boundary 门禁。"
}

$traceSchema = Get-FileContentOrNull -Path $traceSchemaPath
if ($null -eq $traceSchema -or -not (Test-ContainsAll -Content $traceSchema -Terms @('"files_changed"', '"stage2_review"', '"risk_review_summary"', '"public_api_changes"', '"sensitive_data_findings"', '"error_handling_findings"'))) {
  $issues += New-Issue -Severity "error" -File "docs/harness/trace.schema.json" -Section "trace" -Message "trace schema 没有完整记录 changed files 或执行风险审查结果。"
}

$bootstrap = Get-FileContentOrNull -Path $bootstrapPath
if ($null -eq $bootstrap -or -not (Test-ContainsAll -Content $bootstrap -Terms @('execution-boundary-lint.ps1'))) {
  $issues += New-Issue -Severity "error" -File "tools/install/bootstrap-codex-harness.ps1" -Section "install" -Message "bootstrap 没有安装 execution-boundary-lint。"
}

$installSmoke = Get-FileContentOrNull -Path $installSmokePath
if ($null -eq $installSmoke -or -not (Test-ContainsAll -Content $installSmoke -Terms @('tools\harness\execution-boundary-lint.ps1'))) {
  $issues += New-Issue -Severity "error" -File "tools/harness/tests/test-install-modes.ps1" -Section "smoke" -Message "安装 smoke 没有覆盖 execution-boundary-lint。"
}

$errors = @($issues | Where-Object { $_.severity -eq "error" })
$warnings = @($issues | Where-Object { $_.severity -eq "warning" })
$exitCode = if ($errors.Count -gt 0) { 1 } elseif ($warnings.Count -gt 0) { 2 } else { 0 }

if ($AsJson) {
  [PSCustomObject]@{
    ok = ($exitCode -eq 0)
    errors = $errors
    warnings = $warnings
  } | ConvertTo-Json -Depth 6
  exit $exitCode
}

if ($errors.Count -eq 0 -and $warnings.Count -eq 0) {
  Write-Output "Execution boundary lint passed."
  exit 0
}

if ($errors.Count -gt 0) {
  Write-Output "Execution boundary lint failed."
  foreach ($issue in $errors) {
    Write-Output ("- [ERROR] {0} :: {1} :: {2}" -f $issue.file, $issue.section, $issue.message)
  }
}

if ($warnings.Count -gt 0) {
  if ($errors.Count -eq 0) {
    Write-Output "Execution boundary lint passed with warnings."
  }
  foreach ($issue in $warnings) {
    Write-Output ("- [WARNING] {0} :: {1} :: {2}" -f $issue.file, $issue.section, $issue.message)
  }
}

exit $exitCode
