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 = @()

$testPlanTemplatePath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot "docs\testing\test-plan-template.md"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\testing\test-plan-template.md")
)
$failureTriagePath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot "docs\testing\failure-triage.md"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\testing\failure-triage.md")
)
$e2ePlanPath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot "docs\testing\e2e-plan.md"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\testing\e2e-plan.md")
)
$testingRulePath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot ".codex\rules\testing.md"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\config\rules\testing.md")
)
$testPlannerAgentPath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot ".codex\agents\test-planner.toml"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\config\agents\test-planner.toml")
)
$testPlannerPromptPath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot ".codex\prompts\test-planner.md"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\prompts\test-planner.md")
)
$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")
)
$failureTriagePromptPath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot ".codex\prompts\failure-triage.md"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\prompts\failure-triage.md")
)
$featureTaskTemplatePath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot "tools\harness\templates\feature-task-reference.json"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\runtime\feature-task-reference.json")
)
$traceSchemaPath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot "docs\harness\trace.schema.json"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\trace\trace.schema.json")
)

$testPlanTemplate = Get-FileContentOrNull -Path $testPlanTemplatePath
if ($null -eq $testPlanTemplate -or -not (Test-ContainsAll -Content $testPlanTemplate -Terms @("acceptance-to-test mapping", "positive cases", "negative cases", "boundary cases", "regression cases", "permission / security cases", "affected tests", "Fresh Evidence"))) {
  $issues += New-Issue -Severity "error" -File "docs/codex-harness-engineering/templates/testing/test-plan-template.md" -Section "template" -Message "缺少或未完整定义测试计划模板。"
}

if ($null -eq (Get-FileContentOrNull -Path $failureTriagePath)) {
  $issues += New-Issue -Severity "error" -File "docs/codex-harness-engineering/templates/testing/failure-triage.md" -Section "template" -Message "缺少 failure-triage testing truth source。"
}

if ($null -eq (Get-FileContentOrNull -Path $e2ePlanPath)) {
  $issues += New-Issue -Severity "error" -File "docs/codex-harness-engineering/templates/testing/e2e-plan.md" -Section "template" -Message "缺少 e2e-plan 模板。"
}

$testingRule = Get-FileContentOrNull -Path $testingRulePath
if ($null -eq $testingRule -or -not (Test-ContainsAll -Content $testingRule -Terms @("test-plan-template.md", "TDD", "acceptance", "permission", "security"))) {
  $issues += New-Issue -Severity "error" -File "docs/codex-harness-engineering/templates/config/rules/testing.md" -Section "rules" -Message "testing 规则模板没有完整覆盖测试策略前置、TDD 或权限/安全 case。"
}

$testPlannerAgent = Get-FileContentOrNull -Path $testPlannerAgentPath
if ($null -eq $testPlannerAgent -or -not (Test-ContainsAll -Content $testPlannerAgent -Terms @("acceptance-to-test mapping", "positive/negative/boundary/regression/permission/security", "test-plan-template.md", "e2e-plan.md"))) {
  $issues += New-Issue -Severity "error" -File "docs/codex-harness-engineering/templates/config/agents/test-planner.toml" -Section "agent" -Message "test-planner agent 模板没有完整覆盖测试策略输出。"
}

$testPlannerPrompt = Get-FileContentOrNull -Path $testPlannerPromptPath
if ($null -eq $testPlannerPrompt -or -not (Test-ContainsAll -Content $testPlannerPrompt -Terms @("acceptance-to-test mapping", "permission / security cases", "BLOCKED"))) {
  $issues += New-Issue -Severity "error" -File "docs/codex-harness-engineering/templates/prompts/test-planner.md" -Section "prompt" -Message "缺少或未完整定义 test-planner prompt 模板。"
}

$implementPrompt = Get-FileContentOrNull -Path $implementPromptPath
if ($null -eq $implementPrompt -or -not (Test-ContainsAll -Content $implementPrompt -Terms @("### Phase 5: Test Plan", "permission_cases", "security_cases", "test_plan_summary"))) {
  $issues += New-Issue -Severity "error" -File "docs/codex-harness-engineering/templates/prompts/implement-one-task.md" -Section "prompt" -Message "implement prompt 没有把测试计划阶段做成执行链要求。"
}

$stage2Prompt = Get-FileContentOrNull -Path $stage2PromptPath
if ($null -eq $stage2Prompt -or -not (Test-ContainsAll -Content $stage2Prompt -Terms @("acceptance 到验证动作", "permission_cases", "security_cases", "test_plan_summary"))) {
  $issues += New-Issue -Severity "error" -File "docs/codex-harness-engineering/templates/prompts/review-stage2-quality.md" -Section "prompt" -Message "Stage 2 prompt 缺少测试策略、permission/security cases 或 test_plan_summary 审查。"
}

$failureTriagePrompt = Get-FileContentOrNull -Path $failureTriagePromptPath
if ($null -eq $failureTriagePrompt -or -not (Test-ContainsAll -Content $failureTriagePrompt -Terms @("TEST_CODE_ISSUE", "PRODUCT_BUG", "ENV_OR_DATA_ISSUE", "FLAKY", "先做失败分类"))) {
  $issues += New-Issue -Severity "error" -File "docs/codex-harness-engineering/templates/prompts/failure-triage.md" -Section "prompt" -Message "failure-triage prompt 没有先分类再修复的硬要求。"
}

$featureTaskTemplate = Get-FileContentOrNull -Path $featureTaskTemplatePath
if ($null -eq $featureTaskTemplate -or -not (Test-ContainsAll -Content $featureTaskTemplate -Terms @('"test_command"', '"qa_contract"', '"permission_cases"', '"security_cases"', '"test_strategy_required"', '"test_plan_summary"'))) {
  $issues += New-Issue -Severity "error" -File "docs/codex-harness-engineering/templates/runtime/feature-task-reference.json" -Section "task-template" -Message "feature task 模板缺少测试策略字段或 permission/security cases。"
}

$traceSchema = Get-FileContentOrNull -Path $traceSchemaPath
if ($null -eq $traceSchema -or -not (Test-ContainsAll -Content $traceSchema -Terms @('"test_plan_summary"', '"test_result_summary"', '"test_planner_used"', '"test_command"'))) {
  $issues += New-Issue -Severity "warning" -File "docs/codex-harness-engineering/templates/trace/trace.schema.json" -Section "trace" -Message "trace schema 尚未完整记录测试策略和测试结果摘要。"
}

$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 "Test strategy lint passed."
  exit 0
}

if ($errors.Count -gt 0) {
  Write-Output "Test strategy 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 "Test strategy lint passed with warnings."
  }
  foreach ($issue in $warnings) {
    Write-Output ("- [WARNING] {0} :: {1} :: {2}" -f $issue.file, $issue.section, $issue.message)
  }
}

exit $exitCode
