#!/usr/bin/env bash
# Lesson 11 — the capstone. A repository the learner has never seen, carrying
# the evidence of four things an agent did to it, and nothing that says so.
#
# The whole lesson is one question — "what happened here?" — so this script's
# job is to leave a repository whose history is TRUE, complete, and silent
# about its own past. Every fact the lesson asks for is recoverable from the
# repository alone.
#
# What it builds, in order:
#   1. two ordinary commits on main
#   2. `checkout-flow`, squash-merged  → content in main, no link, branch still
#      reported unmerged, its own commits unreachable from main
#   3. `parse-safety`, rebased then fast-forwarded → author dates from the day
#      the work was done, committer dates from the day it landed
#   4. a force-push that abandoned "Add debug logging" → unreachable, and in
#      the reflog only
#   5. three live agent branches: two that collide on one file, one that cannot
#
# Deterministic: author AND committer dates are fixed for every commit, so
# every hash in the lesson reproduces — including the rebased ones, which is
# the whole point of step 3. Build it anywhere; nothing here prints a path.
#
#     ./sandbox/build-l11.sh /tmp/l11
#     L11_CAPTURE=1 ./sandbox/build-l11.sh /tmp/l11    # print the readouts
#
set -euo pipefail
R="${1:?usage: build-l11.sh <dir>}"
rm -rf "$R"; mkdir -p "$R"; cd "$R"

git init -q -b main --bare origin.git
git init -q -b main app
cd app
git config user.name "Ada Lovelace"; git config user.email "ada@example.com"
git remote add origin ../origin.git

# c <author-date> <committer-date> <message> — the two dates are separate on
# purpose. A rebase preserves the first and rewrites the second, and that
# divergence is the only durable evidence of a replay.
c() { GIT_AUTHOR_DATE="$1" GIT_COMMITTER_DATE="$2" git commit -q -m "$3"; }

# ── 1. main ──────────────────────────────────────────────────────────────
printf 'export const RETRIES = 3\n' > client.ts
printf '# storefront\n' > README.md
git add -A; c "2026-06-01T09:00:00+0100" "2026-06-01T09:00:00+0100" "Add the API client"

printf 'export const RETRIES = 3\nexport const TIMEOUT = 5000\n' > client.ts
git add -A; c "2026-06-02T09:00:00+0100" "2026-06-02T09:00:00+0100" "Add a timeout"

git push -q -u origin main

# ── 2. checkout-flow, landed by SQUASH ───────────────────────────────────
git switch -q -c checkout-flow
printf 'export function checkout() { return "wip" }\n' > checkout.ts
git add -A; c "2026-06-03T09:00:00+0100" "2026-06-03T09:00:00+0100" "Start the checkout flow"
printf 'export function checkout() { return "ok" }\n' > checkout.ts
git add -A; c "2026-06-03T14:00:00+0100" "2026-06-03T14:00:00+0100" "Fix the checkout return value"
printf 'export function checkout() { return "ok" }\nexport const CURRENCY = "GBP"\n' > checkout.ts
git add -A; c "2026-06-04T09:00:00+0100" "2026-06-04T09:00:00+0100" "Add a currency"

git switch -q main
# `--squash` prints "Squash commit -- not updating HEAD" even under -q. Silenced
# so the build is quiet; it is not a readout, and no lesson step reads it.
git merge -q --squash checkout-flow >/dev/null
git add -A; c "2026-06-05T09:00:00+0100" "2026-06-05T09:00:00+0100" "Add the checkout flow (#12)"

# ── 3. parse-safety, landed by REBASE ────────────────────────────────────
# Branched from the commit before the squash, so the rebase has real work to do.
git switch -q -c parse-safety "HEAD~1"
printf 'export function parse(s) { return JSON.parse(s) }\n' > parse.ts
git add -A; c "2026-06-04T11:00:00+0100" "2026-06-04T11:00:00+0100" "Add the parser"
printf 'export function parse(s) {\n  try { return JSON.parse(s) } catch { return null }\n}\n' > parse.ts
git add -A; c "2026-06-04T16:00:00+0100" "2026-06-04T16:00:00+0100" "Parse safely"

# The replay: author dates survive, committer dates become the landing day.
GIT_COMMITTER_DATE="2026-06-06T09:00:00+0100" git rebase -q main
git switch -q main
git merge -q --ff-only parse-safety

# ── 4. the force-push that abandoned a commit ────────────────────────────
printf 'export const RETRIES = 3\nexport const TIMEOUT = 5000\nconsole.log("debug")\n' > client.ts
git add -A; c "2026-06-07T09:00:00+0100" "2026-06-07T09:00:00+0100" "Add debug logging"
git push -q --force origin main          # the remote briefly had it too
git reset -q --hard HEAD~1               # "undo the last change"
git push -q --force origin main          # and the remote no longer does

# ── 5. three agents, mid-flight ──────────────────────────────────────────
# cache and timeout both edit client.ts, adjacently. docs touches nothing else.
git switch -q -c agent-cache
printf 'export const RETRIES = 3\nexport const CACHE = true\nexport const TIMEOUT = 5000\n' > client.ts
git add -A; c "2026-06-08T09:00:00+0100" "2026-06-08T09:00:00+0100" "Cache responses"

git switch -q main
git switch -q -c agent-timeout
printf 'export const RETRIES = 3\nexport const TIMEOUT = 30000\n' > client.ts
git add -A; c "2026-06-08T10:00:00+0100" "2026-06-08T10:00:00+0100" "Raise the timeout"

git switch -q main
git switch -q -c agent-docs
printf '# storefront\n\nRun `pnpm dev` to start.\n' > README.md
git add -A; c "2026-06-08T11:00:00+0100" "2026-06-08T11:00:00+0100" "Document the dev server"

git switch -q main

# ── the readouts ─────────────────────────────────────────────────────────
if [ "${L11_CAPTURE:-}" = "1" ]; then
  p() { printf '\n===== %s\n' "$1"; }

  p "step 2 — the log"
  git log --graph --oneline --decorate --all

  p "step 2 — author against committer"
  git log --format='%h %an %ad %cn %cd' --date=short -6

  p "step 3 — which branches does main reach?"
  git branch --merged main
  p "step 3 — what does checkout-flow still have outstanding?"
  git log --oneline main..checkout-flow

  # Index form, not --date=short. A reflog entry for a `reset` or a `merge`
  # takes its timestamp from the wall clock rather than from GIT_COMMITTER_DATE,
  # so the dated form prints the day you run this and does not reproduce. The
  # `HEAD@{n}` indices do, because the sequence of operations is fixed.
  p "step 4 — the reflog"
  git reflog -14

  p "step 4 — plain --unreachable, which is what most advice reaches for"
  git fsck --unreachable 2>/dev/null

  p "step 4 — and with --no-reflogs, which asks the other question"
  git fsck --unreachable --no-reflogs 2>/dev/null | grep commit | while read -r _ _ h; do
    printf '%s %s\n' "$(git rev-parse --short "$h")" "$(git log -1 --format=%s "$h")"
  done

  p "step 5 — what each agent touched"
  git diff --name-only main...agent-cache
  git diff --name-only main...agent-timeout
  git diff --name-only main...agent-docs

  p "step 5 — merge-tree: cache against timeout"
  git merge-tree --write-tree agent-cache agent-timeout || echo "exit: $?"
  p "step 5 — merge-tree: cache against docs"
  git merge-tree --write-tree agent-cache agent-docs; echo "exit: $?"
fi
