#!/usr/bin/env bash
# PR #31580 "fix(server): Codex image views show in the work log again".
# Claim: already-fixed on main by #30473 (5b11efe5df), which added the Codex imageView branch.
# Shows: the PR's own regression scenario passes on main (server emits the completed image
# row), fails on the PR's merge-base, and main's web client renders it as "Read <path>"
# with an image preview while dropping previews for non-image/multi-line paths.
# Needs: run from the root of a clean pingdotgg/t3code checkout with deps installed (`vp i`), and `gh`.
set -euo pipefail

if [ -n "$(git status --porcelain)" ]; then echo "refusing to run: working tree is dirty" >&2; exit 2; fi
ORIG=$(git symbolic-ref -q --short HEAD || git rev-parse HEAD)
SERVER_TEST=apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.test.ts
WEB_TEST=apps/web/src/codexImageViewRow.proof.test.ts
restore_tree() { git restore --source=HEAD --staged --worktree -- "$SERVER_TEST"; }
cleanup() {
  restore_tree 2>/dev/null || true
  rm -f "$WEB_TEST"
  git checkout -q "$ORIG"
}
trap 'rc=$?; cleanup; [ "${PROOF_DONE:-0}" = 1 ] && exit 0; [ "$rc" -ne 0 ] && exit "$rc"; exit 1' EXIT

git fetch -q origin main

MAIN=$(git rev-parse FETCH_HEAD)
git fetch -q origin pull/31580/head
PR_HEAD=$(git rev-parse FETCH_HEAD)
BASE=$(git merge-base "$MAIN" "$PR_HEAD")
FIX=5b11efe5df2f44741d2a89733ee49b00b753e5d1 # #30473
echo "main=$MAIN pr_head=$PR_HEAD merge_base=$BASE"
ok=1
pass() { echo "PASS  $*"; }
fail() { echo "FAIL  $*"; ok=0; }

# 1. Maintainer closed the PR's issue as fixed by #30473.
issue=$(gh issue view 32193 --repo pingdotgg/t3code --json state,stateReason,comments \
  --jq '.state+" "+.stateReason+" | "+([.comments[]|select(.author.login=="juliusmarminge")|.body]|last|split("\n")[0])')
echo "      issue #32193: $issue"
[[ "$issue" == "CLOSED COMPLETED | Fixed by #30473"* ]] && pass "issue #32193 closed COMPLETED by maintainer comment 'Fixed by #30473'" || fail "issue #32193 not closed as fixed by #30473"

# 2. #30473 is on main and handles Codex imageView items.
git merge-base --is-ancestor "$FIX" "$MAIN" && pass "#30473 ($FIX) is an ancestor of main" || fail "#30473 not on main"
git grep -q 'payload.item.type === "imageView"' "$MAIN" -- apps/server/src/orchestration-v2/Adapters/CodexAdapterV2.ts \
  && pass "main CodexAdapterV2.ts has an imageView branch" || fail "main CodexAdapterV2.ts has no imageView branch"

# The PR's own test, with its server-side `title` assertion swapped for toolName/input checks:
# on main the client derives the "Read <path>" label (checked in step 5).
overlay_pr_test() {
  git checkout -q "$PR_HEAD" -- "$SERVER_TEST"
  node -e '
    const fs = require("fs"); const f = process.argv[1]; const s = fs.readFileSync(f, "utf8");
    const from = "assert.strictEqual(item?.title, `Read ${path}`);";
    const to = "assert.strictEqual(item?.type === \"dynamic_tool\" ? item.toolName : null, \"view_image\");\n" +
      "        assert.deepEqual(item?.type === \"dynamic_tool\" ? item.input : null, { path });";
    if (!s.includes(from)) { console.error("title assertion not found"); process.exit(1); }
    fs.writeFileSync(f, s.replace(from, to));' "$SERVER_TEST"
}
PATTERN='projects viewed images as read rows: (completed|started only)'

# 3. PR's regression scenario passes on main without the PR's source change.
git checkout -q --detach "$MAIN"; overlay_pr_test
if (cd apps/server && vp test run src/orchestration-v2/Adapters/CodexAdapterV2.test.ts -t "$PATTERN") >/tmp/pr31580-main.log 2>&1 \
   && grep -qE 'Tests +2 passed' /tmp/pr31580-main.log; then
  pass "PR #31580 image-view test (completed, started-only) passes on main without the PR's source"
else cat /tmp/pr31580-main.log; fail "PR #31580 image-view test fails on main"; fi
restore_tree

# 4. Same test fails on the PR's merge-base (before #30473): no image row is emitted.
git checkout -q --detach "$BASE"; overlay_pr_test
if (cd apps/server && vp test run src/orchestration-v2/Adapters/CodexAdapterV2.test.ts -t "$PATTERN") >/tmp/pr31580-base.log 2>&1; then
  cat /tmp/pr31580-base.log; fail "test unexpectedly passes on merge-base $BASE"
elif grep -q "expected \[\] to deeply equal \[ 'completed' \]" /tmp/pr31580-base.log; then
  pass "same test fails on merge-base $BASE (no completed image row)"
else cat /tmp/pr31580-base.log; fail "test failed on merge-base for an unexpected reason"; fi
restore_tree

# 5. Main's web client renders main's emitted item as a Read row with a preview.
git checkout -q --detach "$MAIN"
cat > "$WEB_TEST" <<'EOF'
import {
  RunId,
  ThreadId,
  TurnItemId,
  type OrchestrationV2ProjectedTurnItem,
  type OrchestrationV2TurnItem,
} from "@t3tools/contracts";
import { workEntryViewedImagePath } from "@t3tools/client-runtime/work-log/presentation";
import * as DateTime from "effect/DateTime";
import { describe, expect, it } from "vite-plus/test";
import { workEntryDisplayLabel } from "./components/chat/MessagesTimeline.logic";
import { deriveTimelineEntriesFromVisibleTurnItems } from "./session-logic";

const at = DateTime.makeUnsafe("2026-11-11T12:00:00.000Z");
// The item main's CodexAdapterV2 emits for item/completed { type: "imageView", path } (step 3).
const imageViewItem = (path: string): OrchestrationV2TurnItem => ({
  id: TurnItemId.make("image-view"),
  threadId: ThreadId.make("thread"),
  runId: RunId.make("run"),
  nodeId: null,
  providerThreadId: null,
  providerTurnId: null,
  nativeItemRef: null,
  parentItemId: null,
  ordinal: 1,
  status: "completed",
  title: null,
  startedAt: at,
  completedAt: at,
  updatedAt: at,
  type: "dynamic_tool",
  toolName: "view_image",
  input: { path },
  viewedImagePath: path,
});
const workEntry = (path: string) => {
  const item = imageViewItem(path);
  const visible: OrchestrationV2ProjectedTurnItem = {
    position: item.ordinal,
    visibility: "local",
    sourceThreadId: item.threadId,
    sourceItemId: item.id,
    item,
  };
  const [entry] = deriveTimelineEntriesFromVisibleTurnItems({
    visibleTurnItems: [visible],
    optimisticMessages: [],
  });
  if (entry?.kind !== "work") throw new Error(`expected a work entry, got ${entry?.kind}`);
  return entry.entry;
};

describe("PR #31580 proof: Codex image views on main's client", () => {
  it("shows a Read row with an image preview", () => {
    const entry = workEntry("/workspace/shots/compare.png");
    expect(workEntryDisplayLabel(entry, undefined)).toBe("Read /workspace/shots/compare.png");
    expect(workEntryViewedImagePath(entry)).toBe("/workspace/shots/compare.png");
  });
  it.each(["/workspace/notes.txt", "/workspace/a\nb.png"])("shows no image preview for %j", (path) => {
    expect(workEntryViewedImagePath(workEntry(path))).toBeNull();
  });
});
EOF
if (cd apps/web && vp test run src/codexImageViewRow.proof.test.ts) >/tmp/pr31580-web.log 2>&1 \
   && grep -qE 'Tests +3 passed' /tmp/pr31580-web.log; then
  pass "main web client labels the image row 'Read <path>', shows its preview, and drops previews for .txt / multi-line paths"
else cat /tmp/pr31580-web.log; fail "main web client does not render the Codex image row as the PR describes"; fi

if [ "$ok" = 1 ]; then
  echo "VERDICT: CLOSE (already-fixed) - main delivers PR #31580's behavior via #30473"; PROOF_DONE=1; exit 0
else
  echo "VERDICT: claim not proven - keep PR #31580 open"; exit 1
fi
