#!/usr/bin/env bash
# contract-scaffold/scaffold.sh -- M1: the contract-first codegen spine.
#
# The single load-bearing mechanism behind "a real wired app, not a static shell"
# (see artifacts/beat-replit-engineering-plan.md, studied from Replit's Adopt).
# It emits an OpenAPI contract as a first-class artifact, then generates typed
# client hooks + validators FROM it, so a page PHYSICALLY CANNOT call an endpoint
# the backend does not implement -- it will not typecheck. A static mock stops
# being possible to pass off as a wired app.
#
# STATUS: STANDALONE + ADDITIVE. Wired into NO build lane. It is a capability
# module proven on throwaway specs; wiring it into the default build is the
# gated next step (like FV-1 -> FV-2), requiring bash+bun parity work + council.
#
# GENERAL by construction: it takes a portable {name, entity, fields} descriptor
# and produces a contract-first skeleton for ANY simple REST resource. It has NO
# knowledge of any specific PRD (anti-teaching-to-the-test).
#
# Usage:
#   scaffold.sh <out_dir> <resource_name> [field:type ...]
# Example:
#   scaffold.sh /tmp/bookmarks bookmark url:string title:string
set -euo pipefail

OUT="${1:?usage: scaffold.sh <out_dir> <resource> [field:type ...]}"
RES="${2:?resource name required}"
shift 2 || true
FIELDS=("$@")
[ "${#FIELDS[@]}" -eq 0 ] && FIELDS=("title:string")

# Plural collection path (naive but sufficient: append 's' unless already plural).
case "$RES" in *s) COLL="$RES" ;; *) COLL="${RES}s" ;; esac

mkdir -p "$OUT"

# ---- 1. The OpenAPI contract (the single source of truth) -------------------
# Build the schema properties + a create-body from the field descriptors.
_props=""; _required=""; _create_props=""
for f in "${FIELDS[@]}"; do
    fname="${f%%:*}"; ftype="${f##*:}"
    case "$ftype" in
        int|integer|number) otype="integer" ;;
        bool|boolean) otype="boolean" ;;
        *) otype="string" ;;
    esac
    _props+="        ${fname}: { type: ${otype} }
"
    _create_props+="        ${fname}: { type: ${otype} }
"
    _required+="        - ${fname}
"
done

cat > "$OUT/openapi.yaml" <<YAML
openapi: 3.1.0
info: { title: Api, version: 1.0.0 }
servers: [{ url: /api }]
paths:
  /${COLL}:
    get:
      operationId: list${RES^}
      responses:
        '200':
          description: OK
          content:
            application/json:
              schema: { type: array, items: { \$ref: '#/components/schemas/${RES^}' } }
    post:
      operationId: create${RES^}
      requestBody:
        required: true
        content:
          application/json:
            schema: { \$ref: '#/components/schemas/${RES^}Create' }
      responses:
        '201':
          description: Created
          content:
            application/json:
              schema: { \$ref: '#/components/schemas/${RES^}' }
  /${COLL}/{id}:
    delete:
      operationId: delete${RES^}
      parameters:
        - { name: id, in: path, required: true, schema: { type: string } }
      responses:
        '204': { description: No Content }
components:
  schemas:
    ${RES^}:
      type: object
      required:
        - id
${_required}      properties:
        id: { type: string }
${_props}    ${RES^}Create:
      type: object
      required:
${_required}      properties:
${_create_props}
YAML

# ---- 2. Orval config: contract -> typed react-query hooks -------------------
cat > "$OUT/orval.config.ts" <<'TS'
import { defineConfig } from "orval";
export default defineConfig({
  client: {
    input: "./openapi.yaml",
    output: {
      target: "./src/generated/api.ts",
      client: "react-query",
      mode: "single",
      clean: true,
      override: { header: () => ["// GENERATED by orval -- do not edit manually."] },
    },
  },
});
TS

# ---- 3. package.json (real deps, real codegen script) ----------------------
cat > "$OUT/package.json" <<JSON
{
  "name": "contract-first-${COLL}",
  "private": true,
  "type": "module",
  "scripts": {
    "codegen": "orval --config ./orval.config.ts",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": { "@tanstack/react-query": "^5.0.0", "react": "^18.0.0" },
  "devDependencies": { "orval": "^8.0.0", "typescript": "^5.4.0", "@types/react": "^18.0.0" }
}
JSON

cat > "$OUT/tsconfig.json" <<'JSON'
{
  "compilerOptions": {
    "strict": true, "noEmit": true, "jsx": "react-jsx",
    "module": "ESNext", "moduleResolution": "Bundler",
    "target": "ES2022", "lib": ["ES2022", "DOM"], "skipLibCheck": true
  },
  "include": ["src"]
}
JSON

echo "scaffolded contract-first skeleton at $OUT (resource=$RES, collection=/$COLL)"
echo "next: (cd $OUT && npm install && npm run codegen && npm run typecheck)"
