#!/usr/bin/env bash
set -euo pipefail

usage() {
  cat <<'EOF'
Usage: ai/commands/prompt-synthesize.sh <qa|dev|dev-fix|requirements-review>
       ai/commands/prompt-synthesize.sh review <qa|dev|dev-fix|requirements-review>
       ai/commands/prompt-synthesize.sh --help

Generates focused prompts from fixed repository workflow state.

Modes:
  qa                   Generate a QA review prompt for the active chunk.
  dev                  Generate a Developer implementation/continuation prompt.
  dev-fix              Generate a focused Developer fix prompt from QA blockers.
  requirements-review  Generate a Requirements Review prompt for the single active requirements file.
  review <mode>        Generate a Prompt Synthesizer review/improve/veto prompt.

This helper is read-only. It does not execute Codex, submit prompts, complete chunks,
commit changes, read arbitrary paths, or write prompt state.
EOF
}

repo_root() {
  local script_dir
  script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  if git -C "$script_dir" rev-parse --show-toplevel >/dev/null 2>&1; then
    git -C "$script_dir" rev-parse --show-toplevel
    return
  fi
  cd "$script_dir/../.." && pwd
}

section() {
  local file="$1"
  local heading="$2"
  awk -v heading="## ${heading}" '
    $0 == heading { in_section = 1; next }
    in_section && /^## / { exit }
    in_section { print }
  ' "$file" | sed -E '/^[[:space:]]*$/d'
}

metadata_value() {
  local file="$1"
  local key="$2"
  awk -v key="$key" '
    /^---[[:space:]]*$/ {
      fence++
      if (fence == 2) exit
      next
    }
    fence == 1 && index($0, key ":") == 1 {
      value = $0
      sub("^[^:]+:[[:space:]]*", "", value)
      print value
      exit
    }
  ' "$file"
}

latest_pass_entry() {
  local file="$1"
  local role_pattern="${2:-Developer|QA}"
  awk -v role_pattern="$role_pattern" '
    $0 == "## Pass History" { in_history = 1; next }
    in_history && /^## / { exit }
    in_history && $0 ~ "^### (" role_pattern ") Pass [0-9]+" {
      if (entry != "") latest = entry
      entry = $0 "\n"
      next
    }
    in_history && entry != "" { entry = entry $0 "\n" }
    END {
      if (entry != "") latest = entry
      gsub(/\n[[:space:]]*$/, "", latest)
      print latest
    }
  ' "$file"
}

relevant_developer_passes_for_qa() {
  local file="$1"
  awk '
    function flush_entry() {
      if (entry != "") {
        count++
        entries[count] = entry
        roles[count] = role
        if (role == "QA") latest_qa = count
      }
      entry = ""
      role = ""
    }

    $0 == "## Pass History" { in_history = 1; next }
    in_history && /^## / { flush_entry(); exit }
    in_history && /^### (Developer|QA) Pass [0-9]+/ {
      flush_entry()
      role = ($0 ~ /^### Developer Pass /) ? "Developer" : "QA"
      entry = $0 "\n"
      next
    }
    in_history && entry != "" { entry = entry $0 "\n" }
    END {
      flush_entry()
      start = latest_qa > 0 ? latest_qa + 1 : 1
      for (i = start; i <= count; i++) {
        if (roles[i] == "Developer") {
          output = entries[i]
          gsub(/\n[[:space:]]*$/, "", output)
          if (printed) print ""
          print output
          printed = 1
        }
      }
    }
  ' "$file"
}

field_value() {
  local label="$1"
  awk -F': ' -v label="$label" '$1 == label { print substr($0, length(label) + 3); exit }'
}

blocked_output() {
  local canonical_state="$1"
  local reason="$2"
  local next_action="$3"
  local exact_command="$4"
  local approval="$5"

  cat <<EOF
PROMPT SYNTHESIS BLOCKED

Canonical State: $canonical_state
Reason: $reason
Recommended Next Action: $next_action
Exact Next Command: $exact_command
Human Approval Needed: $approval
EOF
}

print_tailed_block() {
  local label="$1"
  local content="$2"
  local max_lines="${3:-80}"
  echo "$label"
  if [[ -z "$content" ]]; then
    echo "(none)"
  else
    printf '%s\n' "$content" | tail -n "$max_lines"
  fi
}

print_validation_section() {
  local validation_value="$1"
  echo "Validation:"
  if [[ -n "$validation_value" ]]; then
    printf '%s\n' "$validation_value" |
      awk -F';' '
        {
          for (i = 1; i <= NF; i++) {
            value = $i
            gsub(/^[[:space:]]+|[[:space:]]+$/, "", value)
            if (value != "") print "- " value
          }
        }
      '
  else
    cat <<'EOF'
- bash -n ai/commands/*.sh ai/tools/telegram/*.sh && node ai/tools/telegram/test/telegram-test.mjs
- ai/commands/workflow-state.sh
- ai/commands/orchestrator-next.sh
- ai/commands/workflow-state.sh --ready-for-qa
- ai/commands/workflow-state.sh --ready-to-complete || true
- ai/commands/orchestrator-status.sh
EOF
  fi
}

active_chunk_path() {
  shopt -s nullglob
  local chunks=("$root"/ai/chunks/active/chunk-[0-9]*-*.md)
  shopt -u nullglob
  if (( ${#chunks[@]} != 1 )); then
    return 1
  fi
  printf '%s\n' "${chunks[0]}"
}

active_requirements_path() {
  shopt -s nullglob
  local requirements=("$root"/ai/requirements/active/*.md)
  shopt -u nullglob
  if (( ${#requirements[@]} != 1 )); then
    return 1
  fi
  printf '%s\n' "${requirements[0]}"
}

workflow_output_for_chunk() {
  "$root"/ai/commands/workflow-state.sh
}

canonical_state_from_output() {
  local workflow_output="$1"
  printf '%s\n' "$workflow_output" | field_value "Canonical state"
}

recommended_action_from_output() {
  local workflow_output="$1"
  printf '%s\n' "$workflow_output" | field_value "Recommended next action"
}

ensure_chunk_prompt_allowed() {
  local prompt_mode="$1"
  local workflow_output="$2"
  local canonical_state recommended_action
  canonical_state="$(canonical_state_from_output "$workflow_output")"
  recommended_action="$(recommended_action_from_output "$workflow_output")"
  [[ -n "$canonical_state" ]] || canonical_state="manual_intervention_required"
  [[ -n "$recommended_action" ]] || recommended_action="manual intervention required"

  case "$canonical_state" in
    manual_intervention_required)
      blocked_output "$canonical_state" "Workflow state is ambiguous or unsafe for role prompt generation." "$recommended_action" "ai/commands/workflow-state.sh" "yes"
      return 1
      ;;
  esac

  case "$prompt_mode" in
    qa)
      if [[ "$canonical_state" != "ready_for_qa" ]]; then
        case "$canonical_state" in
          ready_to_complete | commit_ready)
            blocked_output "$canonical_state" "QA prompt generation is blocked because QA already passed and the next step is human review/completion." "$recommended_action" "ai/commands/workflow-summary.sh" "yes"
            ;;
          *)
            blocked_output "$canonical_state" "QA prompt generation is allowed only when canonical state is ready_for_qa." "$recommended_action" "ai/commands/workflow-state.sh --ready-for-qa" "no"
            ;;
        esac
        return 1
      fi
      ;;
    dev)
      case "$canonical_state" in
        ready_to_complete)
          blocked_output "$canonical_state" "Developer prompt generation is blocked because the correct next action is human review before completion/archive." "$recommended_action" "ai/commands/workflow-summary.sh" "yes"
          return 1
          ;;
        ready_for_qa)
          blocked_output "$canonical_state" "Developer prompt generation is blocked because the correct next action is QA review." "$recommended_action" "ai/commands/prompt-synthesize.sh qa" "no"
          return 1
          ;;
        qa_blocked_fixable)
          blocked_output "$canonical_state" "Use dev-fix for retry-safe QA BLOCKED state so the prompt stays focused on QA blockers." "$recommended_action" "ai/commands/prompt-synthesize.sh dev-fix" "no"
          return 1
          ;;
        qa_blocked_requires_decision | qa_blocked_scope_change | retry_limit_reached)
          blocked_output "$canonical_state" "Developer implementation prompt is blocked because the QA blocker requires escalation instead of a normal Developer pass." "$recommended_action" "ai/commands/workflow-summary.sh" "yes"
          return 1
          ;;
      esac
      ;;
    dev-fix)
      if [[ "$canonical_state" != "qa_blocked_fixable" ]]; then
        blocked_output "$canonical_state" "Developer fix prompt generation is allowed only when QA blockers are classified as retry-safe/fixable." "$recommended_action" "ai/commands/workflow-summary.sh" "$(case "$canonical_state" in qa_blocked_requires_decision|qa_blocked_scope_change|retry_limit_reached) echo yes ;; *) echo no ;; esac)"
        return 1
      fi
      ;;
  esac
}

print_common_chunk_context() {
  local chunk="$1"
  local workflow_output="$2"
  local context_mode="${3:-general}"
  local execution_notes qa_review latest_pass relevant_dev_passes status_output diff_output
  execution_notes="$(section "$chunk" "Execution Notes")"
  qa_review="$(section "$chunk" "QA Review")"
  latest_pass="$(latest_pass_entry "$chunk")"
  relevant_dev_passes="$(relevant_developer_passes_for_qa "$chunk")"
  status_output="$(git -C "$root" status --short --untracked-files=all)"
  diff_output="$(git -C "$root" diff --stat)"

  cat <<EOF
Prompt Source:
- ai/commands/prompt-synthesize.sh
- ai/standards/prompt-synthesis.md
- ai/standards/workflow-state.md
- ai/standards/done.md
- ai/standards/qa-gates.md
- Active chunk metadata and sections
- ai/commands/workflow-state.sh output
- git status --short --untracked-files=all
- git diff --stat

Active Chunk:
$chunk

Chunk Metadata:
- Status: $(metadata_value "$chunk" "Status")
- Owner Role: $(metadata_value "$chunk" "Owner Role")
- Created: $(metadata_value "$chunk" "Created")
- Completed: $(metadata_value "$chunk" "Completed")
- Depends On: $(metadata_value "$chunk" "Depends On")
- Validation: $(metadata_value "$chunk" "Validation")

Canonical Workflow State:
$workflow_output

EOF

  print_tailed_block "Execution Notes:" "$execution_notes" 80
  echo
  print_tailed_block "QA Review:" "$qa_review" 80
  echo
  if [[ "$context_mode" == "qa" ]]; then
    print_tailed_block "Relevant Pass History For QA:" "$relevant_dev_passes" 160
    echo
    print_tailed_block "Latest Pass Summary:" "$latest_pass" 40
  else
    print_tailed_block "Latest Pass History Entry:" "$latest_pass" 80
  fi
  echo
  print_tailed_block "Git Status:" "$status_output" 120
  echo
  print_tailed_block "Git Diff Stat:" "$diff_output" 120
}

generate_qa_prompt() {
  local chunk workflow_output validation_value
  chunk="$(active_chunk_path)" || {
    echo "Cannot synthesize QA prompt: expected exactly one active chunk." >&2
    return 1
  }
  workflow_output="$(workflow_output_for_chunk)"
  ensure_chunk_prompt_allowed "qa" "$workflow_output" || return 1
  validation_value="$(metadata_value "$chunk" "Validation")"
  cat <<EOF
Use ai/roles/qa.md.
Review ai/chunks/active/$(basename "$chunk").

Goal:
Validate the active chunk against:
- ai/standards/done.md
- ai/standards/qa-gates.md
- ai/standards/workflow-state.md
- ai/standards/workflow-handoff.md
- ai/standards/prompt-synthesis.md

EOF
  print_common_chunk_context "$chunk" "$workflow_output" "qa"
  echo
  print_validation_section "$validation_value"
  cat <<'EOF'

Deliver:
- PASS or BLOCKED
- blockers first
- runtime smoke applicability
- validation results
- cleanup assessment
- safety/regression assessment
- standard ## QA Review
- ### QA Pass N under ## Pass History
- ## Handoff block
- git status
- git diff --stat
EOF
}

generate_dev_prompt() {
  local chunk workflow_output canonical_state
  chunk="$(active_chunk_path)" || {
    echo "Cannot synthesize Developer prompt: expected exactly one active chunk." >&2
    return 1
  }
  workflow_output="$(workflow_output_for_chunk)"
  ensure_chunk_prompt_allowed "dev" "$workflow_output" || return 1
  canonical_state="$(canonical_state_from_output "$workflow_output")"
  cat <<EOF
Use ai/roles/developer.md.
Continue ai/chunks/active/$(basename "$chunk").

Goal:
Continue the active chunk according to the canonical workflow state.

Canonical State:
$canonical_state

EOF
  print_common_chunk_context "$chunk" "$workflow_output"
  cat <<'EOF'

Instructions:
- Keep scope limited to the active chunk.
- If the state is ready_for_qa or ready_to_complete, do not add implementation work unless explicitly requested.
- If the state is qa_blocked_fixable, prefer `ai/commands/prompt-synthesize.sh dev-fix` for a focused fix prompt.
- If the state requires a decision, scope change, or retry-limit escalation, do not continue implementation without human direction.
- Do not change app source code or dependencies unless the chunk scope explicitly allows it.
- Do not execute Codex, complete chunks, or commit changes.

Validation:
Use the validation commands from the active chunk metadata and Execution Notes.

After editing:
- update Execution Notes
- add/update Developer Pass N under ## Pass History
- include ## Handoff block
- run git status
- run git diff --stat
- summarize
EOF
}

generate_dev_fix_prompt() {
  local chunk workflow_output blockers
  chunk="$(active_chunk_path)" || {
    echo "Cannot synthesize Developer fix prompt: expected exactly one active chunk." >&2
    return 1
  }
  workflow_output="$(workflow_output_for_chunk)"
  ensure_chunk_prompt_allowed "dev-fix" "$workflow_output" || return 1
  blockers="$(printf '%s\n' "$workflow_output" | field_value "Blockers")"
  cat <<EOF
Use ai/roles/developer.md.
Continue ai/chunks/active/$(basename "$chunk").

Goal:
Fix only the current QA blockers.

QA Blockers:
${blockers:-"(none reported)"}

EOF
  print_common_chunk_context "$chunk" "$workflow_output"
  cat <<'EOF'

Scope:
- Fix QA blockers only.
- Preserve out-of-scope items.
- Do not add unrelated cleanup or new features.

Validation:
Use the validation commands from the active chunk metadata and QA Review.

After editing:
- update Execution Notes
- add/update Developer Pass N under ## Pass History
- mark stale QA review if needed
- include ## Handoff block
- run git status
- run git diff --stat
- summarize
EOF
}

generate_requirements_review_prompt() {
  local requirements state_output review_section pass_history
  requirements="$(active_requirements_path)" || {
    echo "Cannot synthesize Requirements Review prompt: expected exactly one active requirements file." >&2
    return 1
  }
  state_output="$("$root"/ai/commands/requirements-state.sh "$requirements")"
  review_section="$(section "$requirements" "Requirements Review")"
  pass_history="$(section "$requirements" "Pass History")"
  cat <<EOF
Use ai/roles/requirements-review.md.
Review ${requirements#"$root"/}.

Apply:
- ai/standards/requirements.md
- ai/standards/requirements-gates.md
- ai/standards/workflow-handoff.md
- ai/standards/prompt-synthesis.md

Requirements State:
$state_output

EOF
  print_tailed_block "Current Requirements Review:" "$review_section" 80
  echo
  print_tailed_block "Pass History:" "$pass_history" 100
  cat <<'EOF'

Deliver:
- PASS or BLOCKED
- blockers first
- requirements completeness assessment
- risks
- recommended next action
- update ## Requirements Review
- update Requirements Review Pass N
- include ## Handoff block
EOF
}

generate_prompt() {
  local prompt_mode="$1"
  case "$prompt_mode" in
    qa) generate_qa_prompt ;;
    dev) generate_dev_prompt ;;
    dev-fix) generate_dev_fix_prompt ;;
    requirements-review) generate_requirements_review_prompt ;;
    *)
      usage >&2
      return 2
      ;;
  esac
}

generate_review_prompt() {
  local prompt_mode="$1"
  local draft_output draft_status workflow_output canonical_state stale_risk state_note_label state_note_value
  set +e
  draft_output="$(generate_prompt "$prompt_mode" 2>&1)"
  draft_status="$?"
  set -e

  workflow_output="$(workflow_output_for_chunk 2>/dev/null || true)"
  canonical_state="$(canonical_state_from_output "$workflow_output")"
  stale_risk="$(printf '%s\n' "$workflow_output" | field_value "Stale QA risk")"
  [[ -n "$canonical_state" ]] || canonical_state="unknown"
  [[ -n "$stale_risk" ]] || stale_risk="unknown"
  if [[ "$canonical_state" == "ready_for_qa" ]]; then
    state_note_label="QA Needed"
    state_note_value="yes - latest Developer pass awaits QA"
  else
    state_note_label="Stale-State Risk"
    state_note_value="$stale_risk"
  fi

  cat <<EOF
Use ai/roles/prompt-synthesizer.md.

Goal:
Review, improve, or veto the deterministic prompt generated by ai/commands/prompt-synthesize.sh.

Target Prompt Mode:
$prompt_mode

Expected Target Role:
$(case "$prompt_mode" in
  qa) echo "QA" ;;
  dev) echo "Developer" ;;
  dev-fix) echo "Developer" ;;
  requirements-review) echo "Requirements Review" ;;
esac)

Canonical Workflow State:
$canonical_state

$state_note_label:
$state_note_value

Prompt Source Context:
- Deterministic draft command: ai/commands/prompt-synthesize.sh $prompt_mode
- Prompt synthesis standard: ai/standards/prompt-synthesis.md
- Workflow state standard: ai/standards/workflow-state.md
- Handoff standard: ai/standards/workflow-handoff.md
- Draft command exit status: $draft_status

Scope Boundaries:
- Review prompt quality only.
- Do not execute Codex.
- Do not submit prompts to tmux.
- Do not complete chunks or commit changes.
- Do not add app source or dependency changes.
- Do not bypass deterministic blocked output without explaining why human approval is needed.

Review Criteria:
- Correct target role and task.
- Correct canonical-state handling.
- No stale QA PASS or stale pass-history risk.
- Scope and out-of-scope boundaries are explicit.
- Validation and cleanup expectations are present.
- No secrets, tokens, .env values, or arbitrary file content.
- Exact next action is clear.

Deterministic Draft Prompt:
The following fenced block is the deterministic draft under review. Internal headings such as Deliver belong to the draft, not to this Prompt Synthesizer review task.

\`\`\`md
$(printf '%s\n' "$draft_output")
\`\`\`

Prompt Synthesizer Review Task:

Deliver:
- PASS or BLOCKED
- Improved prompt if PASS
- Veto reason if BLOCKED
- Missing context
- Scope-risk assessment
- Exact next action
EOF
}

mode="${1:-}"
review_mode="false"

if [[ "$mode" == "review" ]]; then
  review_mode="true"
  mode="${2:-}"
fi

case "$mode" in
  qa)
    ;;
  dev)
    ;;
  dev-fix)
    ;;
  requirements-review)
    ;;
  -h | --help | help)
    usage
    exit 0
    ;;
  *)
    usage >&2
    exit 2
    ;;
esac

root="$(repo_root)"

if [[ "$review_mode" == "true" ]]; then
  generate_review_prompt "$mode"
else
  generate_prompt "$mode"
fi
