param(
  [string]$ProjectRoot = "",
  [string[]]$Commands = @("git diff --check"),
  [string[]]$HookConfigPaths = @()
)

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

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

function Invoke-VerificationCommand {
  param(
    [string]$Command,
    [string]$Root
  )

  Push-Location $Root
  try {
    Write-Output "Running: $Command"
    powershell -NoProfile -Command $Command
    if ($LASTEXITCODE -ne 0) {
      throw "验证失败，退出码 $($LASTEXITCODE): $Command"
    }
  }
  finally {
    Pop-Location
  }
}

function Invoke-TaskStructureValidation {
  param([string]$Root)

  $scriptPath = Join-Path $Root "tools\harness\task-structure-lint.ps1"
  if (-not (Test-Path -LiteralPath $scriptPath)) {
    throw "缺少 task structure lint: tools\\harness\\task-structure-lint.ps1"
  }

  & powershell -NoProfile -ExecutionPolicy Bypass -File $scriptPath -ProjectRoot $Root | Out-Host
  if ($LASTEXITCODE -ne 0) {
    throw "task structure 校验失败。"
  }
}

function Invoke-TaskProtocolValidation {
  param([string]$Root)

  $scriptPath = Join-Path $Root "tools\harness\task-protocol-lint.ps1"
  if (-not (Test-Path -LiteralPath $scriptPath)) {
    throw "缺少 task protocol lint: tools\\harness\\task-protocol-lint.ps1"
  }

  & powershell -NoProfile -ExecutionPolicy Bypass -File $scriptPath -ProjectRoot $Root | Out-Host
  if ($LASTEXITCODE -ne 0) {
    throw "task protocol 校验失败。"
  }
}

function Invoke-BusinessGoalValidation {
  param([string]$Root)

  $scriptPath = Join-Path $Root "tools\harness\business-goal-lint.ps1"
  if (-not (Test-Path -LiteralPath $scriptPath)) {
    throw "缺少 business goal lint: tools\\harness\\business-goal-lint.ps1"
  }

  & powershell -NoProfile -ExecutionPolicy Bypass -File $scriptPath -ProjectRoot $Root | Out-Host
  if ($LASTEXITCODE -ne 0) {
    throw "business goal 校验失败。"
  }
}

function Invoke-ProductSpecValidation {
  param([string]$Root)

  $scriptPath = Join-Path $Root "tools\harness\product-spec-lint.ps1"
  if (-not (Test-Path -LiteralPath $scriptPath)) {
    throw "缺少 product spec lint: tools\\harness\\product-spec-lint.ps1"
  }

  & powershell -NoProfile -ExecutionPolicy Bypass -File $scriptPath -ProjectRoot $Root | Out-Host
  if ($LASTEXITCODE -ne 0) {
    throw "product spec 校验失败。"
  }
}

function Invoke-AcceptanceContractValidation {
  param([string]$Root)

  $scriptPath = Join-Path $Root "tools\harness\acceptance-contract-lint.ps1"
  if (-not (Test-Path -LiteralPath $scriptPath)) {
    throw "缺少 acceptance contract lint: tools\\harness\\acceptance-contract-lint.ps1"
  }

  & powershell -NoProfile -ExecutionPolicy Bypass -File $scriptPath -ProjectRoot $Root | Out-Host
  if ($LASTEXITCODE -ne 0) {
    throw "acceptance contract 校验失败。"
  }
}

function Invoke-ContractValidation {
  param([string]$Root)

  $scriptPath = Join-Path $Root "tools\harness\contract-lint.ps1"
  if (-not (Test-Path -LiteralPath $scriptPath)) {
    throw "缺少 contract lint: tools\\harness\\contract-lint.ps1"
  }

  & powershell -NoProfile -ExecutionPolicy Bypass -File $scriptPath -ProjectRoot $Root | Out-Host
  if ($LASTEXITCODE -ne 0) {
    throw "contract 校验失败。"
  }
}

function Invoke-DomainModelValidation {
  param([string]$Root)

  $scriptPath = Join-Path $Root "tools\harness\domain-model-lint.ps1"
  if (-not (Test-Path -LiteralPath $scriptPath)) {
    throw "缺少 domain model lint: tools\\harness\\domain-model-lint.ps1"
  }

  & powershell -NoProfile -ExecutionPolicy Bypass -File $scriptPath -ProjectRoot $Root | Out-Host
  if ($LASTEXITCODE -ne 0) {
    throw "domain model 校验失败。"
  }
}

function Invoke-BackendValidation {
  param([string]$Root)

  $scriptPath = Join-Path $Root "tools\harness\backend-lint.ps1"
  if (-not (Test-Path -LiteralPath $scriptPath)) {
    throw "缺少 backend lint: tools\\harness\\backend-lint.ps1"
  }

  & powershell -NoProfile -ExecutionPolicy Bypass -File $scriptPath -ProjectRoot $Root -AllowWarningsOnly | Out-Host
  if ($LASTEXITCODE -ne 0) {
    throw "backend 校验失败。"
  }
}

function Invoke-ArchitectureBoundaryValidation {
  param([string]$Root)

  $scriptPath = Join-Path $Root "tools\harness\architecture-boundary-lint.ps1"
  if (-not (Test-Path -LiteralPath $scriptPath)) {
    throw "缺少 architecture boundary lint: tools\\harness\\architecture-boundary-lint.ps1"
  }

  & powershell -NoProfile -ExecutionPolicy Bypass -File $scriptPath -ProjectRoot $Root | Out-Host
  if ($LASTEXITCODE -ne 0) {
    throw "architecture boundary 校验失败。"
  }
}

function Invoke-SecurityValidation {
  param([string]$Root)

  $scriptPath = Join-Path $Root "tools\harness\security-lint.ps1"
  if (-not (Test-Path -LiteralPath $scriptPath)) {
    throw "缺少 security lint: tools\\harness\\security-lint.ps1"
  }

  & powershell -NoProfile -ExecutionPolicy Bypass -File $scriptPath -ProjectRoot $Root -AllowWarningsOnly | Out-Host
  if ($LASTEXITCODE -ne 0) {
    throw "security 校验失败。"
  }
}

function Invoke-ImpactValidation {
  param([string]$Root)

  $scriptPath = Join-Path $Root "tools\harness\impact-lint.ps1"
  if (-not (Test-Path -LiteralPath $scriptPath)) {
    throw "缺少 impact lint: tools\\harness\\impact-lint.ps1"
  }

  & powershell -NoProfile -ExecutionPolicy Bypass -File $scriptPath -ProjectRoot $Root -AllowWarningsOnly | Out-Host
  if ($LASTEXITCODE -ne 0) {
    throw "impact analysis 校验失败。"
  }
}

function Invoke-TestStrategyValidation {
  param([string]$Root)

  $scriptPath = Join-Path $Root "tools\harness\test-strategy-lint.ps1"
  if (-not (Test-Path -LiteralPath $scriptPath)) {
    throw "缺少 test strategy lint: tools\\harness\\test-strategy-lint.ps1"
  }

  & powershell -NoProfile -ExecutionPolicy Bypass -File $scriptPath -ProjectRoot $Root | Out-Host
  if ($LASTEXITCODE -ne 0) {
    throw "test strategy 校验失败。"
  }
}

function Invoke-ImplementationPlanValidation {
  param([string]$Root)

  $scriptPath = Join-Path $Root "tools\harness\implementation-plan-lint.ps1"
  if (-not (Test-Path -LiteralPath $scriptPath)) {
    throw "缺少 implementation plan lint: tools\\harness\\implementation-plan-lint.ps1"
  }

  & powershell -NoProfile -ExecutionPolicy Bypass -File $scriptPath -ProjectRoot $Root | Out-Host
  if ($LASTEXITCODE -ne 0) {
    throw "implementation plan 校验失败。"
  }
}

function Invoke-ExecutionBoundaryValidation {
  param([string]$Root)

  $scriptPath = Join-Path $Root "tools\harness\execution-boundary-lint.ps1"
  if (-not (Test-Path -LiteralPath $scriptPath)) {
    throw "缺少 execution boundary lint: tools\\harness\\execution-boundary-lint.ps1"
  }

  & powershell -NoProfile -ExecutionPolicy Bypass -File $scriptPath -ProjectRoot $Root | Out-Host
  if ($LASTEXITCODE -ne 0) {
    throw "execution boundary 校验失败。"
  }
}

function Invoke-ReviewEvidenceValidation {
  param([string]$Root)

  $scriptPath = Join-Path $Root "tools\harness\review-evidence-lint.ps1"
  if (-not (Test-Path -LiteralPath $scriptPath)) {
    throw "缺少 review evidence lint: tools\\harness\\review-evidence-lint.ps1"
  }

  & powershell -NoProfile -ExecutionPolicy Bypass -File $scriptPath -ProjectRoot $Root | Out-Host
  if ($LASTEXITCODE -ne 0) {
    throw "review evidence 校验失败。"
  }
}

function Invoke-TruthSourceValidation {
  param([string]$Root)

  $scriptPath = Join-Path $Root "tools\harness\truth-source-lint.ps1"
  if (-not (Test-Path -LiteralPath $scriptPath)) {
    throw "缺少 truth source lint: tools\\harness\\truth-source-lint.ps1"
  }

  & powershell -NoProfile -ExecutionPolicy Bypass -File $scriptPath -ProjectRoot $Root | Out-Host
  if ($LASTEXITCODE -ne 0) {
    throw "truth source 校验失败。"
  }
}

function Get-CodexHookCommand {
  param(
    [object]$Node,
    [string]$Path = "$"
  )

  if ($null -eq $Node -or $Node -is [string]) {
    return
  }

  if ($Node -is [System.Collections.IEnumerable] -and $Node -isnot [System.Collections.IDictionary]) {
    $index = 0
    foreach ($item in $Node) {
      Get-CodexHookCommand -Node $item -Path "${Path}[$index]"
      $index += 1
    }
    return
  }

  foreach ($property in $Node.PSObject.Properties) {
    $childPath = "$Path.$($property.Name)"
    if ($property.Name -eq "command" -and $property.Value -is [string]) {
      [pscustomobject]@{
        Path = $childPath
        Command = $property.Value
      }
    }

    Get-CodexHookCommand -Node $property.Value -Path $childPath
  }
}

function Get-CandidateHookConfigPaths {
  param(
    [string]$Root,
    [string[]]$AdditionalPaths
  )

  return @(
    (Join-Path $HOME ".codex\hooks.json"),
    (Join-Path $Root ".codex\hooks.json"),
    (Join-Path $Root "docs\codex-harness-engineering\templates\hooks\hooks.json")
  ) + $AdditionalPaths
}

function Resolve-HookScriptReference {
  param(
    [string]$Root,
    [string]$Command
  )

  $expanded = $Command.Replace('$(git rev-parse --show-toplevel)', $Root.Replace('\', '/'))
  $scriptPath = $null

  if ($expanded -match "Join-Path[\s\S]*?['""]([^'""]*hook-stop-verify\.ps1)['""]") {
    $scriptPath = Join-Path $Root $Matches[1]
  }
  elseif ($expanded -match '"([^"]*hook-stop-verify\.ps1)"') {
    $scriptPath = $Matches[1]
  }
  elseif ($expanded -match '(\S*hook-stop-verify\.ps1)') {
    $scriptPath = $Matches[1]
  }

  if ([string]::IsNullOrWhiteSpace($scriptPath)) {
    return $null
  }

  $scriptPath = $scriptPath.Replace('/', '\')
  if ([System.IO.Path]::IsPathRooted($scriptPath)) {
    return $scriptPath
  }

  return (Join-Path $Root $scriptPath)
}

function Test-HookScriptInstallMapping {
  param(
    [string]$Root,
    [string]$ResolvedScriptPath
  )

  if (Test-Path -LiteralPath $ResolvedScriptPath) {
    return $true
  }

  $relativeExpectedPath = "tools\harness\hook-stop-verify.ps1"
  $normalized = $ResolvedScriptPath.Replace("/", "\")
  if (-not $normalized.EndsWith($relativeExpectedPath, [System.StringComparison]::OrdinalIgnoreCase)) {
    return $false
  }

  $templateScript = Join-Path $Root "docs\codex-harness-engineering\templates\hooks\hook-stop-verify.ps1"
  $bootstrapScript = Join-Path $Root "tools\install\bootstrap-codex-harness.ps1"
  if (-not ((Test-Path -LiteralPath $templateScript) -and (Test-Path -LiteralPath $bootstrapScript))) {
    return $false
  }

  $bootstrapText = Get-Content -LiteralPath $bootstrapScript -Raw
  return $bootstrapText.Contains('hooks\hook-stop-verify.ps1') -and $bootstrapText.Contains('tools\harness\hook-stop-verify.ps1')
}

function Test-CodexHookCommandCompatibility {
  param(
    [string]$Root,
    [string[]]$AdditionalPaths
  )

  $seen = @{}
  foreach ($path in Get-CandidateHookConfigPaths -Root $Root -AdditionalPaths $AdditionalPaths) {
    if ([string]::IsNullOrWhiteSpace($path) -or $seen.ContainsKey($path)) {
      continue
    }
    $seen[$path] = $true

    if (-not (Test-Path -LiteralPath $path)) {
      continue
    }

    $hookConfig = Get-Content -LiteralPath $path -Raw | ConvertFrom-Json
    foreach ($entry in Get-CodexHookCommand -Node $hookConfig) {
      if ($entry.Command -match '^\s*"[^"]*\\PowerShell\\[^"]*\\pwsh(?:\.exe)?"\s+') {
        throw "高风险 Codex hook command: $path $($entry.Path)。带空格的 quoted PowerShell executable 在 PowerShell 外层解释时会被当作字符串。请改用 pwsh、C:\Progra~1\PowerShell\7\pwsh.exe，或在确认外层是 PowerShell 时使用 & 调用。"
      }

      $scriptReference = Resolve-HookScriptReference -Root $Root -Command $entry.Command
      if ($null -ne $scriptReference -and -not (Test-HookScriptInstallMapping -Root $Root -ResolvedScriptPath $scriptReference)) {
        throw "Codex hook command 指向的脚本不可解析或未被 bootstrap 安装: $path $($entry.Path) -> $scriptReference"
      }
    }
  }
}

function Test-PowerShellSyntax {
  param([string]$Root)

  $paths = @(
    "tools\install\bootstrap-codex-harness.ps1",
    "tools\harness\codex-loop.ps1",
    "tools\install\env-check.ps1",
    "tools\install\install-agent.ps1",
    "tools\install\install-agent-here.ps1",
    "tools\harness\task-structure-lint.ps1",
    "tools\harness\task-protocol-lint.ps1",
    "tools\harness\business-goal-lint.ps1",
    "tools\harness\product-spec-lint.ps1",
    "tools\harness\acceptance-contract-lint.ps1",
    "tools\harness\domain-model-lint.ps1",
    "tools\harness\architecture-boundary-lint.ps1",
    "tools\harness\impact-lint.ps1",
    "tools\harness\test-strategy-lint.ps1",
    "tools\harness\truth-source-lint.ps1",
    "tools\harness\verify.ps1",
    "docs\codex-harness-engineering\templates\bootstrap-codex-harness.ps1",
    "docs\codex-harness-engineering\templates\hooks\hook-stop-verify.ps1",
    "docs\codex-harness-engineering\templates\tools\harness\task-structure-lint.ps1",
    "docs\codex-harness-engineering\templates\tools\harness\task-protocol-lint.ps1",
    "docs\codex-harness-engineering\templates\tools\harness\business-goal-lint.ps1",
    "docs\codex-harness-engineering\templates\tools\harness\product-spec-lint.ps1",
    "docs\codex-harness-engineering\templates\tools\harness\acceptance-contract-lint.ps1",
    "docs\codex-harness-engineering\templates\tools\harness\domain-model-lint.ps1",
    "docs\codex-harness-engineering\templates\tools\harness\architecture-boundary-lint.ps1",
    "docs\codex-harness-engineering\templates\tools\harness\impact-lint.ps1",
    "docs\codex-harness-engineering\templates\tools\harness\test-strategy-lint.ps1",
    "docs\codex-harness-engineering\templates\tools\harness\truth-source-lint.ps1"
  )

  foreach ($relativePath in $paths) {
    $path = Join-Path $Root $relativePath
    if (-not (Test-Path -LiteralPath $path)) {
      continue
    }

    $tokens = $null
    $errors = $null
    [System.Management.Automation.Language.Parser]::ParseFile($path, [ref]$tokens, [ref]$errors) | Out-Null
    if ($errors.Count -gt 0) {
      $messages = ($errors | ForEach-Object { $_.Message }) -join "; "
      throw "PowerShell 语法检查失败: $relativePath - $messages"
    }
  }
}

function Get-RelativePathCompat {
  param(
    [string]$Root,
    [string]$Path
  )

  $rootFull = ([System.IO.Path]::GetFullPath($Root)).TrimEnd('\', '/') + '\'
  $pathFull = [System.IO.Path]::GetFullPath($Path)
  if ($pathFull.StartsWith($rootFull, [System.StringComparison]::OrdinalIgnoreCase)) {
    return $pathFull.Substring($rootFull.Length)
  }

  return $pathFull
}

function Test-NoStaleFeedbackMcpReferences {
  param([string]$Root)

  $scanRoots = @(
    "docs\harness",
    "docs\codex-harness-engineering"
  )

  $blockedPatterns = @(
    "interactive_feedback",
    "mcp-feedback-enhanced"
  )

  foreach ($scanRoot in $scanRoots) {
    $fullScanRoot = Join-Path $Root $scanRoot
    if (-not (Test-Path -LiteralPath $fullScanRoot)) {
      continue
    }

    foreach ($file in Get-ChildItem -LiteralPath $fullScanRoot -Recurse -File -Include *.md,*.toml,*.json,*.ps1) {
      $relativePath = Get-RelativePathCompat -Root $Root -Path $file.FullName
      if ($relativePath.EndsWith("templates\runtime\verify.ps1", [System.StringComparison]::OrdinalIgnoreCase)) {
        continue
      }

      $content = Get-Content -LiteralPath $file.FullName -Raw
      $text = if ($null -eq $content) { "" } else { [string]$content }
      foreach ($pattern in $blockedPatterns) {
        if ($text.Contains($pattern)) {
          throw "发现已移除的 feedback MCP 引用: $relativePath contains '$pattern'"
        }
      }
    }
  }
}

function Invoke-AgentPackageFreshnessCheck {
  param([string]$Root)

  $packageRoot = $Root
  $repoCandidate = Split-Path -Parent $packageRoot
  $packageConfig = Join-Path $packageRoot ".ai-sync.yml"
  $refreshScript = Join-Path $repoCandidate "refresh-agent-package.ps1"
  $packageSourceMarker = Join-Path $repoCandidate "docs\codex-harness-engineering\templates\package-assets\root\install-agent.ps1"

  if (-not (Test-Path -LiteralPath $packageConfig -PathType Leaf)) {
    return
  }

  if (-not (Test-Path -LiteralPath $packageSourceMarker -PathType Leaf)) {
    return
  }

  if (-not (Test-Path -LiteralPath $refreshScript -PathType Leaf)) {
    throw "缺少 refresh-agent-package.ps1，无法校验 agent package freshness。"
  }

  & powershell -NoProfile -ExecutionPolicy Bypass -File $refreshScript -RepoRoot $repoCandidate -PackageRoot $packageRoot -CheckOnly | Out-Host
  if ($LASTEXITCODE -ne 0) {
    throw "agent package freshness 检查失败。"
  }
}

$resolvedProjectRoot = (Resolve-Path -LiteralPath $ProjectRoot).Path

foreach ($command in $Commands) {
  Invoke-VerificationCommand -Command $command -Root $resolvedProjectRoot
}

Invoke-TaskStructureValidation -Root $resolvedProjectRoot
Invoke-TaskProtocolValidation -Root $resolvedProjectRoot
Invoke-BusinessGoalValidation -Root $resolvedProjectRoot
Invoke-ProductSpecValidation -Root $resolvedProjectRoot
Invoke-AcceptanceContractValidation -Root $resolvedProjectRoot
Invoke-ContractValidation -Root $resolvedProjectRoot
Invoke-DomainModelValidation -Root $resolvedProjectRoot
Invoke-BackendValidation -Root $resolvedProjectRoot
Invoke-ArchitectureBoundaryValidation -Root $resolvedProjectRoot
Invoke-SecurityValidation -Root $resolvedProjectRoot
Invoke-ImpactValidation -Root $resolvedProjectRoot
Invoke-TestStrategyValidation -Root $resolvedProjectRoot
Invoke-ImplementationPlanValidation -Root $resolvedProjectRoot
Invoke-ExecutionBoundaryValidation -Root $resolvedProjectRoot
Invoke-ReviewEvidenceValidation -Root $resolvedProjectRoot
Invoke-TruthSourceValidation -Root $resolvedProjectRoot
Test-CodexHookCommandCompatibility -Root $resolvedProjectRoot -AdditionalPaths $HookConfigPaths
Test-PowerShellSyntax -Root $resolvedProjectRoot
Test-NoStaleFeedbackMcpReferences -Root $resolvedProjectRoot
Invoke-AgentPackageFreshnessCheck -Root $resolvedProjectRoot

Write-Output "Verification passed."
