#!/usr/bin/env bash
# Stamp this template into a new project directory, deterministically.
#
# Runs the whole mechanical flow in order: extract this template's committed
# tree into the target, rename the skeleton to <dist-name>, merge (never
# clobber) an existing .gitignore, git-init if needed, create the development
# environment with uv (or the venv/pip fallback) and run `make check`, install
# the claude-okf-repo-kit operating contract when a kit clone is available,
# rerun the gate after installation, and delete the single-use template scripts
# and the template-only CI workflow from the target. What it cannot
# do — the project README, CHANGELOG, goal, and playbook content — it prints as
# next steps.
#
# The target may be a fresh directory or an existing repo that only carries
# goal-shaped content (like docs/GOAL.md): any file the template would land on
# is a refusal, listed by path, except .gitignore which is merged line-wise.
#
# Agent-neutral by design: plain bash, no Claude Code or Codex assumptions.

set -euo pipefail

usage() {
  cat <<'EOF'
Usage:
  bash scripts/create-project TARGET DIST [--kit DIR | --no-kit] [--pip] [--skip-venv]

Options:
  --kit DIR    Install claude-okf-repo-kit from DIR.
  --no-kit     Skip kit installation.
  --pip        Use venv and pip even when uv is available.
  --skip-venv  Skip dependency installation and verification.

TARGET must not contain #; Python console-script shebangs cannot portably use it.
EOF
}

if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
  usage
  exit 0
fi

if [ "$#" -lt 2 ]; then
  usage >&2
  exit 2
fi

TARGET_ARG="$1"
DIST="$2"
shift 2

KIT_DIR=""
NO_KIT=0
SKIP_VENV=0
USE_PIP=0
while [ "$#" -gt 0 ]; do
  case "$1" in
    --kit)
      KIT_DIR="${2:?--kit needs a path}"
      shift 2
      ;;
    --no-kit)
      NO_KIT=1
      shift
      ;;
    --skip-venv)
      SKIP_VENV=1
      shift
      ;;
    --pip)
      USE_PIP=1
      shift
      ;;
    *)
      printf 'Unknown option: %s\n' "$1" >&2
      exit 2
      ;;
  esac
done

if ! printf '%s' "$DIST" | grep -Eq '^[a-z][a-z0-9]*(-[a-z0-9]+)*$'; then
  printf 'Invalid dist name %s: use lowercase letters, digits, and single hyphens (e.g. spec-drift).\n' "$DIST" >&2
  exit 2
fi

TEMPLATE_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
if ! git -C "$TEMPLATE_ROOT" rev-parse HEAD >/dev/null 2>&1; then
  printf 'This script extracts the committed template tree and needs a git checkout of the template.\n' >&2
  exit 1
fi

# Resolve before creating anything, so a refusal leaves no stray directory.
TARGET="$(python3 -c 'import os, sys; print(os.path.abspath(sys.argv[1]))' "$TARGET_ARG")"

case "$TARGET" in
  *'#'*)
    printf 'Target %s contains #: Python console-script shebangs cannot portably use that path.\n' "$TARGET" >&2
    exit 2
    ;;
esac

case "$TARGET/" in
  "$TEMPLATE_ROOT"/*)
    printf 'Target %s is inside the template checkout; pick a directory elsewhere.\n' "$TARGET" >&2
    exit 1
    ;;
esac

mkdir -p "$TARGET"

# Collision check against the committed tree. .gitignore is merged, so it is
# exempt; everything else refuses rather than overwrites.
collisions=()
while IFS= read -r path; do
  case "$path" in
    */) continue ;;          # directory entries
    .gitignore) continue ;;  # merged below
  esac
  if [ -e "$TARGET/$path" ]; then
    collisions+=("$path")
  fi
done < <(git -C "$TEMPLATE_ROOT" archive HEAD | tar -tf -)

if [ "${#collisions[@]}" -gt 0 ]; then
  printf 'Refusing: the target already has files the template would land on:\n' >&2
  printf '  - %s\n' "${collisions[@]}" >&2
  exit 1
fi

# Extract, holding .gitignore aside when the target already has one.
if [ -f "$TARGET/.gitignore" ]; then
  git -C "$TEMPLATE_ROOT" archive HEAD | tar -xf - -C "$TARGET" --exclude .gitignore
  while IFS= read -r line; do
    grep -qxF "$line" "$TARGET/.gitignore" || printf '%s\n' "$line" >> "$TARGET/.gitignore"
  done < <(git -C "$TEMPLATE_ROOT" show HEAD:.gitignore)
  gitignore_note="merged template entries into existing .gitignore"
else
  git -C "$TEMPLATE_ROOT" archive HEAD | tar -xf - -C "$TARGET"
  gitignore_note="installed template .gitignore"
fi

if ! git -C "$TARGET" rev-parse --git-dir >/dev/null 2>&1; then
  git -C "$TARGET" init -q
  git_note="initialized a new git repository"
else
  git_note="kept the existing git repository"
fi

# Rename the skeleton with the target's own copy of the script, then remove
# the template-side tooling from the target: both single-use scripts, the
# smoke test that exercises them (it has nothing to test once they are gone),
# and the template-chassis workflow (its jobs run those scripts and assert the
# template's own version, so they only fail in a generated project).
bash "$TARGET/scripts/rename-project" "$DIST" | sed -n '1,2p'
rm -f "$TARGET/scripts/rename-project" "$TARGET/scripts/create-project" \
  "$TARGET/tests/test_create_project.py" \
  "$TARGET/.github/workflows/template-chassis.yml"

venv_note="skipped (--skip-venv); preferred: uv sync --all-extras && uv run make check; fallback: python3 -m venv .venv && .venv/bin/pip install -e \".[dev,anthropic,openai]\" && make check"
if [ "$SKIP_VENV" -eq 0 ]; then
  if [ "$USE_PIP" -eq 0 ] && command -v uv >/dev/null 2>&1; then
    if (cd "$TARGET" && uv sync --all-extras --quiet && uv run make check >/dev/null 2>&1); then
      venv_note="uv synced all extras; uv.lock created; make check passed"
    else
      printf 'uv setup or make check FAILED in the target; inspect before continuing:\n  cd %s && uv sync --all-extras && uv run make check\n' "$TARGET" >&2
      exit 1
    fi
  else
    python3 -m venv "$TARGET/.venv"
    (cd "$TARGET" && .venv/bin/python -m pip install -q -e ".[dev,anthropic,openai]")
    if make -C "$TARGET" check >/dev/null 2>&1; then
      venv_note="venv/pip environment created; make check passed"
    else
      printf 'make check FAILED in the target; inspect before continuing:\n  cd %s && make check\n' "$TARGET" >&2
      exit 1
    fi
  fi
fi

kit_note="not installed; run when ready: bash /path/to/claude-okf-repo-kit/scripts/update-existing-repo $TARGET"
kit_installed=0
if [ "$NO_KIT" -eq 0 ] && [ -z "$KIT_DIR" ] && [ -d "$TEMPLATE_ROOT/../claude-okf-repo-kit/scripts" ]; then
  KIT_DIR="$(cd "$TEMPLATE_ROOT/../claude-okf-repo-kit" && pwd)"
fi
if [ "$NO_KIT" -eq 1 ]; then
  KIT_DIR=""
  kit_note="skipped (--no-kit)"
fi
if [ -n "$KIT_DIR" ]; then
  if [ -f "$KIT_DIR/scripts/update-existing-repo" ]; then
    printf '\nKit installer output:\n'
    bash "$KIT_DIR/scripts/update-existing-repo" "$TARGET"
    kit_note="claude-okf-repo-kit installed from $KIT_DIR"
    kit_installed=1
  else
    printf 'Kit path %s has no scripts/update-existing-repo; skipping kit install.\n' "$KIT_DIR" >&2
  fi
fi

if [ "$SKIP_VENV" -eq 0 ] && [ "$kit_installed" -eq 1 ]; then
  if [ "$USE_PIP" -eq 0 ] && command -v uv >/dev/null 2>&1; then
    if ! (cd "$TARGET" && uv run make check >/dev/null 2>&1); then
      printf 'Post-kit make check FAILED in the target; inspect before continuing:\n  cd %s && uv run make check\n' "$TARGET" >&2
      exit 1
    fi
  elif ! make -C "$TARGET" check >/dev/null 2>&1; then
    printf 'Post-kit make check FAILED in the target; inspect before continuing:\n  cd %s && make check\n' "$TARGET" >&2
    exit 1
  fi
  kit_note="$kit_note; post-kit make check passed"
fi

PKG="${DIST//-/_}"
cat <<EOF

Project created
Target: $TARGET
Stack:  $DIST (package $PKG), $git_note, $gitignore_note
Gate:   $venv_note
Kit:    $kit_note

Judgment steps this script cannot do:
  1. Rewrite README.md for the project (it still describes the template).
  2. Review CHANGELOG.md's new 0.1.0 entry, the reset docs/log.md,
     maintainer contact details, and LICENSE ownership.
EOF

next_step=3
if [ "$kit_installed" -eq 1 ]; then
  cat <<'EOF'
  3. Resolve numbered kit candidates before the first commit. CLAUDE.2.md is
     a review candidate, not an instruction file Claude Code loads. Merge
     shared rules into AGENTS.md, add Claude-specific imports and skill
     references to CLAUDE.md, then delete CLAUDE.2.md. Review other numbered
     candidates the same way; do not commit unresolved candidates.
EOF
  next_step=4
fi

cat <<EOF
  $next_step. Provide the goal: drop a finished docs/GOAL.md in, or run the goal
     interview from Claude Code. AGENTS.md already carries Codex's stack
     commands; port any kit-specific workflow rules, hooks, and skills per the
     kit README's second-agent section, or fill the goal by hand. Then fill the
     playbook's master-objective and verification-command brackets from it.
  $((next_step + 1)). If the CLI will not call language models, delete the provider layer
     (the README's "Deleting the model-provider layer" section lists every
     piece).
  $((next_step + 2)). Review and commit (including uv.lock when uv created it).
EOF
