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
}

function Get-FormalTasks {
  param([string]$TaskPath)

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

  try {
    $taskDocument = Get-Content -LiteralPath $TaskPath -Raw | ConvertFrom-Json
  }
  catch {
    return @()
  }

  return @($taskDocument.tasks | Where-Object {
      @("feature_research", "feature_spec", "feature_design", "feature_plan", "testcase_design", "feature_impl", "release") -contains [string]$_.task_kind
    })
}

$issues = @()
$taskPath = Join-Path $ProjectRoot "task.json"
$formalTasks = @(Get-FormalTasks -TaskPath $taskPath)

$prdPath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot "docs\product\prd-lite.md"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\product\prd-lite.md")
)
$acceptancePath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot "docs\product\acceptance-criteria.md"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\product\acceptance-criteria.md")
)
$matrixPath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot "docs\product\requirement-interface-matrix.md"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\product\requirement-interface-matrix.md")
)
$stateMatrixPath = Resolve-FirstExistingPath -Candidates @(
  (Join-Path $ProjectRoot "docs\product\state-matrix.yaml"),
  (Join-Path $ProjectRoot "docs\codex-harness-engineering\templates\product\state-matrix.yaml")
)
$implementPromptPath = Join-Path $ProjectRoot ".codex\prompts\implement-one-task.md"
$stage1PromptPath = Join-Path $ProjectRoot ".codex\prompts\review-stage1-spec.md"
$projectTaskTemplatePath = Join-Path $ProjectRoot "tools\harness\templates\project-task-template.json"
$featureTaskTemplatePath = Join-Path $ProjectRoot "tools\harness\templates\feature-task-reference.json"

$prd = Get-FileContentOrNull -Path $prdPath
if ($null -eq $prd) {
  $issues += New-Issue -Severity "error" -File "docs/product/prd-lite.md" -Section "file" -Message "缺少 PRD-Lite，无法承载产品规格层。"
}
elseif (-not (Test-ContainsAll -Content $prd -Terms @("## 4. 用户与角色", "## 6. 业务流程", "### 主流程", "### 异常流程", "### 与现有行为关系", "### 关键输出 / 副作用", "### 非目标"))) {
  $issues += New-Issue -Severity "error" -File "docs/product/prd-lite.md" -Section "structure" -Message "PRD-Lite 缺少用户角色、流程、非目标、输出或与现有行为关系段落。"
}

$acceptance = Get-FileContentOrNull -Path $acceptancePath
if ($null -eq $acceptance) {
  $issues += New-Issue -Severity "error" -File "docs/product/acceptance-criteria.md" -Section "file" -Message "缺少 acceptance-criteria，无法校验异常与边界验收。"
}
elseif (-not (Test-ContainsAll -Content $acceptance -Terms @("## 2. 异常与边界验收", "权限不足", "空数据", "接口失败"))) {
  $issues += New-Issue -Severity "error" -File "docs/product/acceptance-criteria.md" -Section "boundary" -Message "acceptance-criteria 缺少异常、边界或权限验收样式。"
}

$matrix = Get-FileContentOrNull -Path $matrixPath
if ($null -eq $matrix) {
  $issues += New-Issue -Severity "error" -File "docs/product/requirement-interface-matrix.md" -Section "file" -Message "缺少 requirement-interface-matrix，无法把规格追溯到页面、状态和接口。"
}
elseif (-not (Test-ContainsAll -Content $matrix -Terms @("用户角色", "页面状态", "UI 元素 / 操作", "依赖接口", "状态到需求映射"))) {
  $issues += New-Issue -Severity "error" -File "docs/product/requirement-interface-matrix.md" -Section "traceability" -Message "requirement-interface-matrix 缺少角色、状态、操作或接口映射。"
}

$stateMatrix = Get-FileContentOrNull -Path $stateMatrixPath
if ($null -eq $stateMatrix) {
  $issues += New-Issue -Severity "error" -File "docs/product/state-matrix.yaml" -Section "file" -Message "缺少 state-matrix，无法表达状态变化和权限态。"
}
elseif (-not (Test-ContainsAll -Content $stateMatrix -Terms @("actor:", "states:", "permission_denied:", "trigger:", "expected_ui:"))) {
  $issues += New-Issue -Severity "error" -File "docs/product/state-matrix.yaml" -Section "states" -Message "state-matrix 缺少角色、状态、权限态或触发/结果定义。"
}

$implementPrompt = Get-FileContentOrNull -Path $implementPromptPath
if ($null -eq $implementPrompt) {
  $issues += New-Issue -Severity "error" -File ".codex/prompts/implement-one-task.md" -Section "file" -Message "缺少 implement prompt。"
}
elseif (-not (Test-ContainsAll -Content $implementPrompt -Terms @("产品规格摘要", "用户角色", "用户场景", "主流程", "异常流程", "输入", "输出", "状态变化", "权限规则", "边界条件", "非目标", "与现有行为关系", "输出 BLOCKED"))) {
  $issues += New-Issue -Severity "error" -File ".codex/prompts/implement-one-task.md" -Section "prompt" -Message "implement prompt 没有把产品规格层变成实现前必查输入或阻塞条件。"
}

$stage1Prompt = Get-FileContentOrNull -Path $stage1PromptPath
if ($null -eq $stage1Prompt) {
  $issues += New-Issue -Severity "error" -File ".codex/prompts/review-stage1-spec.md" -Section "file" -Message "缺少 Stage 1 review prompt。"
}
elseif (-not (Test-ContainsAll -Content $stage1Prompt -Terms @("产品规格摘要", "用户角色", "用户场景", "主流程", "异常流程", "输入", "输出", "状态变化", "权限规则", "边界条件", "非目标", "与现有行为关系", "product_spec_gap"))) {
  $issues += New-Issue -Severity "error" -File ".codex/prompts/review-stage1-spec.md" -Section "prompt" -Message "Stage 1 prompt 缺少产品规格层完整性检查或缺口分类。"
}

$projectTaskTemplate = Get-FileContentOrNull -Path $projectTaskTemplatePath
if ($null -eq $projectTaskTemplate -or -not (Test-ContainsAll -Content $projectTaskTemplate -Terms @('"product_spec_summary"', '"user_roles"', '"user_scenarios"', '"main_flow"', '"exception_flows"', '"inputs"', '"outputs"', '"state_changes"', '"permission_rules"', '"boundary_conditions"', '"non_goals"', '"existing_behavior_relation"'))) {
  $issues += New-Issue -Severity "error" -File "tools/harness/templates/project-task-template.json" -Section "task-template" -Message "project-task-template 缺少产品规格摘要字段。"
}

$featureTaskTemplate = Get-FileContentOrNull -Path $featureTaskTemplatePath
if ($null -eq $featureTaskTemplate -or -not (Test-ContainsAll -Content $featureTaskTemplate -Terms @('"product_spec_summary"', '"user_roles"', '"user_scenarios"', '"main_flow"', '"exception_flows"', '"inputs"', '"outputs"', '"state_changes"', '"permission_rules"', '"boundary_conditions"', '"non_goals"', '"existing_behavior_relation"'))) {
  $issues += New-Issue -Severity "error" -File "tools/harness/templates/feature-task-reference.json" -Section "task-template" -Message "feature-task-reference 缺少产品规格摘要字段。"
}

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

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

exit $exitCode
