#!/usr/bin/env bash
# render-work-summary.sh  -  v7.1.0
#
# Reads agent-state.json + phase-tracker.json + git diff, emits a concise
# "### Work Summary" markdown block for Phase 7 channels dispatch. Intended
# as an executive-summary companion to the existing Normal Analysis /
# Technical Details / Test Scenarios sections  -  gives Jira/Confluence
# readers and PR reviewers a single-screen answer to "what actually
# changed?" without parsing the full pipeline log.
#
# Usage:
#   render-work-summary.sh <task-id> [--worktree <path>] [--branch <name>]
#                                    [--base-branch <name>]
#
# Source of truth precedence:
#   1. explicit flags (for tests / post-hoc invocation)
#   2. agent-state.json in the worktree
#   3. git status / git log on the worktree branch
#
# Output (stdout, always printable):
#   ### Work Summary
#   **Task**: <jiraId>  |  **Branch**: <branch>  |  **Base**: <base>  |  **PR**: #<N>
#
#   #### Scope delivered
#   - [done] T1 <title>
#   - [done] T2 <title>
#   - [pending] T3 <title> (deferred  -  rationale)
#
#   #### Changed files (<N> files · +<ADD> / -<DEL>)
#   - `path/a.ext`  (+12 / -4)
#   - `path/b.ext`  (+5 / -0)
#
#   #### Review outcome
#   - <A> accepted · <D> deferred · <R> rejected · approved=<bool>
#
#   #### Phases
#   - 0 Init done · 1 Analysis done · 2 Planning done · 3 Dev done · 4 Review done · 5 Test done · 6 Commit done · 7 Report active
#
# Exit codes: 0 = rendered, 2 = missing state (caller should skip section).

set -euo pipefail

TASK_ID="${1:?usage: render-work-summary.sh <task-id>}"
shift || true

WORKTREE=""; BRANCH=""; BASE=""
while [ $# -gt 0 ]; do
  case "$1" in
    --worktree)    WORKTREE="$2"; shift 2 ;;
    --branch)      BRANCH="$2"; shift 2 ;;
    --base-branch) BASE="$2"; shift 2 ;;
    *) shift ;;
  esac
done

command -v jq >/dev/null 2>&1 || { echo "render-work-summary: jq required" >&2; exit 2; }

# Locate the worktree + state files
if [ -z "$WORKTREE" ]; then
  task_bare="${TASK_ID##*-}"
  for candidate in \
    "$PWD/.worktrees/$TASK_ID" \
    "$PWD/.worktrees/$task_bare" \
    "$PWD/.worktrees/task-$task_bare"
  do
    [ -d "$candidate" ] && { WORKTREE="$candidate"; break; }
  done
fi

STATE_FILE=""
TRACKER_FILE=""
TRIAGE_FILE=""
if [ -n "$WORKTREE" ]; then
  [ -f "$WORKTREE/agent-state.json" ]    && STATE_FILE="$WORKTREE/agent-state.json"
  [ -f "$WORKTREE/phase-tracker.json" ]  && TRACKER_FILE="$WORKTREE/phase-tracker.json"
  [ -f "$WORKTREE/triage-output.json" ]  && TRIAGE_FILE="$WORKTREE/triage-output.json"
fi

# Fall back to the salvaged artefacts when the worktree is gone. Phase 6 removes it
# once the PR is open (worktree-finalize), and this script used to resolve state
# ONLY from the worktree - so it exited 2 and the whole Work Summary silently
# disappeared from the PR body and the Jira comment. render-agent-log-cost.sh has
# had a log-dir fallback all along; this is the same list.
if [ -z "$STATE_FILE" ] || [ -z "$TRACKER_FILE" ]; then
  task_bare="${TASK_ID##*-}"
  for base in \
    "$HOME/.claude/logs/multi-agent/$TASK_ID/artifacts" \
    "$HOME/.claude/logs/multi-agent/$task_bare/artifacts" \
    "$HOME"/.claude/logs/multi-agent/*/"$TASK_ID"/artifacts \
    "$HOME"/.claude/logs/multi-agent/*/"$task_bare"/artifacts
  do
    [ -d "$base" ] || continue
    [ -z "$STATE_FILE" ]   && [ -f "$base/agent-state.json" ]    && STATE_FILE="$base/agent-state.json"
    [ -z "$TRACKER_FILE" ] && [ -f "$base/phase-tracker.json" ]  && TRACKER_FILE="$base/phase-tracker.json"
    [ -z "$TRIAGE_FILE" ]  && [ -f "$base/triage-output.json" ]  && TRIAGE_FILE="$base/triage-output.json"
  done
fi

# Last resort for the tracker: the file phase-tracker.sh actually writes on
# every run. The worktree/artifacts copies above exist only when something
# mirrored them there; the logs/ state file always exists for a tracked task.
if [ -z "$TRACKER_FILE" ]; then
  task_bare="${TASK_ID##*-}"
  for f in \
    "$HOME/.claude/logs/multi-agent/$TASK_ID/tracker-state.json" \
    "$HOME/.claude/logs/multi-agent/$task_bare/tracker-state.json"
  do
    [ -f "$f" ] && { TRACKER_FILE="$f"; break; }
  done
fi

# The diffstat below runs in the worktree. For a finalized task the worktree is
# gone but the BRANCH is still local in the project root, so derive a repo to diff
# in from the salvaged state rather than dropping the Changed-files section.
if [ -z "$WORKTREE" ] && [ -n "$STATE_FILE" ]; then
  DIFF_REPO="$(node -e '
    const fs=require("fs");
    try { const s=JSON.parse(fs.readFileSync(process.argv[1],"utf8"));
      process.stdout.write(s.projectRoot || "");
    } catch { process.stdout.write(""); }
  ' "$STATE_FILE" 2>/dev/null || echo "")"
  [ -n "$BRANCH" ] || BRANCH="$(node -e '
    const fs=require("fs");
    try { process.stdout.write(JSON.parse(fs.readFileSync(process.argv[1],"utf8")).branch || ""); }
    catch { process.stdout.write(""); }
  ' "$STATE_FILE" 2>/dev/null || echo "")"
fi

if [ -z "$STATE_FILE" ] && [ -z "$TRACKER_FILE" ] && [ -z "$BRANCH" ]; then
  # Not enough to render anything meaningful
  exit 2
fi

# Extract fields (tolerate missing keys)
state_jira=""
state_branch=""
state_base=""
state_pr=""
state_tasks="[]"
state_triage="{}"
if [ -n "$STATE_FILE" ]; then
  state_jira=$(jq -r '.taskId // ""' "$STATE_FILE")
  state_branch=$(jq -r '.branch // (.projects // [] | .[0].branch // "")' "$STATE_FILE")
  state_base=$(jq -r '.baseBranch // (.projects // [] | .[0].baseBranch // "")' "$STATE_FILE")
  # agent-state.schema.json has no .prNumber/.prUrl/.planTodos/.reviewConsensus/
  # .phases["2"].tasks/.phases["4"].triage - PR data lives at .pr.{number,url},
  # the plan at .plan.todos[], and review triage in the separate
  # triage-output.json (read via TRIAGE_FILE below, not out of agent-state).
  state_pr=$(jq -r '.pr.number // .pr.url // ""' "$STATE_FILE")
  state_tasks=$(jq -c '.plan.todos // []' "$STATE_FILE" 2>/dev/null || echo "[]")
fi
if [ -n "$TRIAGE_FILE" ]; then
  state_triage=$(jq -c '.' "$TRIAGE_FILE" 2>/dev/null || echo "{}")
fi

# Flag overrides
[ -n "$BRANCH" ] && state_branch="$BRANCH"
[ -n "$BASE" ]   && state_base="$BASE"
[ -z "$state_jira" ] && state_jira="$TASK_ID"
[ -z "$state_base" ] && state_base="main"

# Phase tick marks
phase_ticks=""
if [ -n "$TRACKER_FILE" ]; then
  phase_ticks=$(jq -r '
    # Normalize both tracker shapes: phase-tracker.sh writes phases as an
    # array of {id,...}; older fixtures keyed an object by phase id. The
    # string-index below errors on an array and 2>/dev/null would swallow
    # it, silently dropping the whole tick strip.
    (if (.phases | type) == "array"
     then (.phases | map({key: (.id | tostring), value: .}) | from_entries)
     else (.phases // {}) end) as $ph |
    ["0 Init","1 Analysis","2 Planning","3 Dev","4 Review","5 Test","6 Commit","7 Report"] as $labels |
    [range(0;8) | tostring] as $ids |
    $ids
    | map(. as $i |
        ($labels[$i | tonumber]) as $label |
        ($ph[$i] // {}) as $p |
        (if   $p.status == "completed"   then "done"
         elif $p.status == "in_progress" then "active"
         elif $p.status == "failed"      then "failed"
         elif $p.status == "skipped"     then "skipped"
         else                                  "pending" end) as $mark |
        "\($label) \($mark)")
    | join(" · ")
  ' "$TRACKER_FILE" 2>/dev/null || echo "")
fi

# Changed files + diffstat
changed_block=""
files_count=0
total_add=0; total_del=0
# Only the directory needs to exist here; `git rev-parse HEAD` below is the real
# repository check. Testing -d "$WORKTREE/.git" would be wrong regardless: in a
# linked worktree .git is a file, not a directory.
# Prefer the worktree; fall back to the project root with the branch by name. A
# finalized task (Phase 6 removed the worktree after the PR) has no worktree but
# the branch is still local, so without this the Changed-files section vanished
# from the PR body and the Jira comment even though the data was right there.
DIFF_IN=""; DIFF_TIP=""
if [ -n "$WORKTREE" ] && [ -d "$WORKTREE" ] && git -C "$WORKTREE" rev-parse HEAD >/dev/null 2>&1; then
  DIFF_IN="$WORKTREE"; DIFF_TIP="HEAD"
elif [ -n "${DIFF_REPO:-}" ] && [ -n "$BRANCH" ] \
     && git -C "$DIFF_REPO" rev-parse --verify --quiet "$BRANCH" >/dev/null 2>&1; then
  DIFF_IN="$DIFF_REPO"; DIFF_TIP="$BRANCH"
fi

if [ -n "$DIFF_IN" ]; then
  if true; then
    diffstat=$(git -C "$DIFF_IN" diff --numstat "$state_base"..."$DIFF_TIP" 2>/dev/null || true)
    if [ -n "$diffstat" ]; then
      files_count=$(echo "$diffstat" | grep -c . || true)
      total_add=$(echo "$diffstat" | awk '{ if ($1 ~ /^[0-9]+$/) s+=$1 } END { print s+0 }')
      total_del=$(echo "$diffstat" | awk '{ if ($2 ~ /^[0-9]+$/) s+=$2 } END { print s+0 }')
      changed_block=$(echo "$diffstat" | awk '{ printf "- `%s` (+%s / -%s)\n", $3, $1, $2 }' | head -20)
      # If more than 20 files, note the truncation
      extra=$((files_count - 20))
      if [ "$extra" -gt 0 ]; then
        changed_block="${changed_block}
- _... +${extra} more files not shown_"
      fi
    fi
  fi
fi

# Task list with [done] / [pending] marks. plan-todos.schema.json's todo items
# carry `.task` (not `.title`/`.subject`) and a status enum of
# pending|in_progress|completed|skipped|failed (not "done"/"deferred") -
# the old check here never matched real data, so every todo rendered pending.
#
# The marks are words, not check/hourglass glyphs. This summary is section 1 of
# the Jira comment, and channels/jira.md bans decorative glyphs in a comment
# body while this renderer was filling it with them. Words also survive every
# adapter unchanged: `- [x] ` is rewritten to a numbered list by the Jira
# markdown->wiki conversion, which would have dropped the distinction entirely.
tasks_block=""
if [ "$(jq 'length' <<< "$state_tasks")" != "0" ]; then
  tasks_block=$(jq -r '
    .[] |
    "- " +
    (if (.status // "pending") == "completed" then "[done] " else "[pending] " end) +
    (.id // "") +
    (if (.id // "") != "" then " " else "" end) +
    (.task // "(untitled)")
  ' <<< "$state_tasks")
fi

# Review outcome
accepted=$(jq -r '.accepted // [] | length' <<< "$state_triage" 2>/dev/null || echo 0)
deferred=$(jq -r '.deferred // [] | length' <<< "$state_triage" 2>/dev/null || echo 0)
rejected=$(jq -r '.rejected // [] | length' <<< "$state_triage" 2>/dev/null || echo 0)
approved=$(jq -r '.approved // false' <<< "$state_triage" 2>/dev/null || echo false)

# ─── Render ────────────────────────────────────────────────────────
printf '### Work Summary\n'

header="**Task**: \`$state_jira\`"
[ -n "$state_branch" ] && header="$header  |  **Branch**: \`$state_branch\`"
[ -n "$state_base" ]   && header="$header  |  **Base**: \`$state_base\`"
if [ -n "$state_pr" ] && [ "$state_pr" != "null" ]; then
  case "$state_pr" in
    *[!0-9]*) header="$header  |  **PR**: $state_pr" ;;
    *)        header="$header  |  **PR**: #$state_pr" ;;
  esac
fi
printf '%s\n\n' "$header"

if [ -n "$tasks_block" ]; then
  printf '#### Scope delivered\n%s\n\n' "$tasks_block"
fi

if [ "$files_count" -gt 0 ]; then
  printf '#### Changed files (%d files · +%d / -%d)\n%s\n\n' \
    "$files_count" "$total_add" "$total_del" "$changed_block"
fi

if [ "$accepted" != "0" ] || [ "$deferred" != "0" ] || [ "$rejected" != "0" ]; then
  printf '#### Review outcome\n- %s accepted · %s deferred · %s rejected · approved=%s\n\n' \
    "$accepted" "$deferred" "$rejected" "$approved"
fi

if [ -n "$phase_ticks" ]; then
  printf '#### Phases\n- %s\n' "$phase_ticks"
fi

exit 0
