#!/usr/bin/env sh
#
# bootstrap — turn this template into a concrete website.
#
# The template ships as a fully buildable reference site whose identity is the
# neutral placeholder "Site" / "site". This script rewrites those placeholders,
# in place, to the values you provide — then removes itself.
#
# Run once, from the repository root:
#
#   ./Scripts/bootstrap        # or: make bootstrap
#
# It is idempotent only in the sense that it self-destructs: once run, the
# placeholders are gone and the script (and its scaffolding) are deleted.

set -eu

# --- Preconditions ------------------------------------------------------------

if [ ! -d "Services/Website" ]; then
	echo "error: run this from the repository root (Services/Website not found)." >&2
	exit 1
fi

XCODEPROJ="./Site.xcodeproj"

# The placeholder origin the crawler files ship with. It is an RFC 2606 reserved
# domain, so an un-bootstrapped copy can never point a crawler at a real site.
PLACEHOLDER_URL="https://site.example.com"
PLACEHOLDER_URL_PATTERN='https://site\.example\.com'

# --- Prompts ------------------------------------------------------------------

printf 'Site display name (PascalCase, e.g. Acme) [Site]: '
read -r NAME
NAME="${NAME:-Site}"

# The name is interpolated into sed replacements, a Swift string literal, the
# Xcode project's directory name and the pbxproj. In a replacement `&` stands for
# the whole match and `\` escapes, so either would corrupt the rewrites silently
# rather than fail them — reject anything but letters and digits.
case "$NAME" in
	*[!A-Za-z0-9]*)
		echo "error: the display name must be letters and digits only (got \"$NAME\")." >&2
		exit 1
		;;
esac
case "$NAME" in
	[A-Za-z]*) ;;
	*)
		echo "error: the display name must start with a letter (got \"$NAME\")." >&2
		exit 1
		;;
esac

DEFAULT_SLUG="$(printf '%s' "$NAME" | tr '[:upper:]' '[:lower:]')"
printf 'Project slug (lowercase, used for owner/db/compose, e.g. acme-web) [%s]: ' "$DEFAULT_SLUG"
read -r SLUG
SLUG="${SLUG:-$DEFAULT_SLUG}"

# The slug carries the same sed hazard as the name, and additionally becomes the
# Compose project name and the PostgreSQL database name and role, which admit
# lowercase alphanumerics, hyphens and underscores alone.
case "$SLUG" in
	*[!a-z0-9_-]*)
		echo "error: the project slug must be lowercase letters, digits, hyphens or underscores (got \"$SLUG\")." >&2
		exit 1
		;;
esac
case "$SLUG" in
	[a-z0-9]*) ;;
	*)
		echo "error: the project slug must start with a lowercase letter or digit (got \"$SLUG\")." >&2
		exit 1
		;;
esac

printf 'Canonical site URL (scheme + host, e.g. https://acme.example.com) [%s]: ' "$PLACEHOLDER_URL"
read -r URL
URL="${URL:-$PLACEHOLDER_URL}"
URL="${URL%/}" # the rewrites append their own path, so drop a trailing slash

# The URL is interpolated into a sed replacement (with `|` as the delimiter) and
# into the crawler files, so reject anything that is not a plain scheme + host.
case "$URL" in
	http://* | https://*) ;;
	*)
		echo "error: the canonical site URL must start with http:// or https:// (got \"$URL\")." >&2
		exit 1
		;;
esac
case "$URL" in
	*[!-A-Za-z0-9:/._~]*)
		echo "error: the canonical site URL contains unexpected characters (got \"$URL\")." >&2
		exit 1
		;;
esac

echo
echo "  display name : $NAME       (server name \"${NAME}Website\", ${NAME}.xcodeproj)"
echo "  slug         : $SLUG       (container owner, database name/user, compose project)"
echo "  canonical URL: $URL       (robots.txt, sitemap.xml, String.Site.origin)"
echo
printf 'Apply these values? [y/N]: '
read -r CONFIRM
case "$CONFIRM" in
	y | Y | yes | YES) ;;
	*)
		echo "Aborted. Nothing was changed."
		exit 1
		;;
esac

# --- Helpers ------------------------------------------------------------------

# Portable in-place sed (works on both BSD/macOS and GNU/Linux).
rewrite() {
	file="$1"
	shift
	[ -f "$file" ] || return 0
	tmp="${file}.bootstrap.tmp"
	if sed "$@" "$file" >"$tmp"; then
		mv "$tmp" "$file"
	else
		rm -f "$tmp"
		return 1
	fi
}

W="Services/Website"

# --- Rewrites -----------------------------------------------------------------

# Swift constants: server name + database name/username.
rewrite "$W/Sources/Library/Public/Extensions/String+Constants.swift" \
	-e "s/SiteWebsite/${NAME}Website/g" \
	-e "s/= \"site\"/= \"${SLUG}\"/g"

# Canonical origin. Left empty for the placeholder, which keeps the HTTPS
# redirect off rather than aimed at a reserved example domain.
if [ "$URL" != "$PLACEHOLDER_URL" ]; then
	rewrite "$W/Sources/Library/Public/Extensions/String+Constants.swift" \
		-e "s|^        public static let origin = \"\"\$|        public static let origin = \"${URL}\"|"
fi

# Local env example.
rewrite "$W/.env.local" \
	-e "s/SiteWebsite/${NAME}Website/g" \
	-e "s/^HOST_OWNER=site\$/HOST_OWNER=${SLUG}/" \
	-e "s/^DATABASE_NAME=site\$/DATABASE_NAME=${SLUG}/" \
	-e "s/^DATABASE_USERNAME=site\$/DATABASE_USERNAME=${SLUG}/" \
	-e "s/^DATABASE_PASSWORD=site\$/DATABASE_PASSWORD=${SLUG}/"

# Production compose.
rewrite "$W/docker-compose.yml" \
	-e "s/SiteWebsite/${NAME}Website/g" \
	-e "s/^name: site-platform\$/name: ${SLUG}-platform/" \
	-e "s/DATABASE_NAME:-site}/DATABASE_NAME:-${SLUG}}/" \
	-e "s/DATABASE_USERNAME:-site}/DATABASE_USERNAME:-${SLUG}}/"

# Local override compose.
rewrite "$W/docker-compose.override.yml" \
	-e "s/HOST_OWNER:-site}/HOST_OWNER:-${SLUG}}/" \
	-e "s/DATABASE_NAME:-site}/DATABASE_NAME:-${SLUG}}/" \
	-e "s/DATABASE_USERNAME:-site}/DATABASE_USERNAME:-${SLUG}}/" \
	-e "s/DATABASE_PASSWORD:-site}/DATABASE_PASSWORD:-${SLUG}}/"

# Makefile db-shell fallbacks.
rewrite "$W/Makefile" \
	-e "s/),site)/),${SLUG})/g"

# Web app manifest: the identity shown by an install prompt.
rewrite "$W/Resources/Static/site.webmanifest" \
	-e "s/^  \"short_name\": \"\"/  \"short_name\": \"${NAME}\"/" \
	-e "s/^  \"name\": \"\"/  \"name\": \"${NAME}\"/"

# Persistence integration test: its POSTGRES_TEST_* fallbacks address the local
# container `make db-mount` creates, which is initialised from the slug.
rewrite "Packages/Persistence/Tests/Cases/Public/Methods/ServiceTests.swift" \
	-e "s/?? \"site\"/?? \"${SLUG}\"/g" \
	-e "s/: \"site\",\$/: \"${SLUG}\",/"

# Crawler files: the absolute origin they must carry. `|` is the sed delimiter,
# since both the placeholder and the replacement contain slashes.
rewrite "$W/Resources/Static/robots.txt" \
	-e "s|${PLACEHOLDER_URL_PATTERN}|${URL}|g"

rewrite "$W/Resources/Static/sitemap.xml" \
	-e "s|${PLACEHOLDER_URL_PATTERN}|${URL}|g"

# Service README.
rewrite "$W/README.md" \
	-e "s/^# Site Website\$/# ${NAME} Website/" \
	-e "s/\*\*Site\*\*/**${NAME}**/" \
	-e "s/SiteWebsite/${NAME}Website/g" \
	-e "s/\`site\`/\`${SLUG}\`/g" \
	-e "s/\`Site\.xcodeproj\`/\`${NAME}.xcodeproj\`/g" \
	-e "s|^\`robots.txt\` and \`sitemap.xml\` need an absolute origin.*\$|\`robots.txt\` and \`sitemap.xml\` carry the absolute origin bootstrap wrote into them: ${URL}. Update both if the canonical origin ever changes.|"

# Xcode project: PBXProject name + directory.
if [ -d "$XCODEPROJ" ]; then
	rewrite "$XCODEPROJ/project.pbxproj" -e "s/\"Site\"/\"${NAME}\"/g"

	# Skip the rename when the display name was left as the placeholder: the
	# source and destination are then the same path, and `mv dir dir` fails
	# (it moves the directory into itself), which would abort the run here —
	# after the rewrites, but before the self-cleanup.
	XCODEPROJ_RENAMED="./${NAME}.xcodeproj"

	if [ "$XCODEPROJ" != "$XCODEPROJ_RENAMED" ]; then
		if command -v git >/dev/null 2>&1 && git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
			git mv "$XCODEPROJ" "$XCODEPROJ_RENAMED" 2>/dev/null || mv "$XCODEPROJ" "$XCODEPROJ_RENAMED"
		else
			mv "$XCODEPROJ" "$XCODEPROJ_RENAMED"
		fi
	fi
fi

# --- Optional: fresh git history ---------------------------------------------

echo
printf 'Start a fresh git history (drops the template history)? [y/N]: '
read -r FRESH
case "$FRESH" in
	y | Y | yes | YES)
		if command -v git >/dev/null 2>&1; then
			rm -rf .git
			git init -q
			echo "Reinitialised git. Remember to add your remote and commit."
		fi
		;;
	*) ;;
esac

# --- Self-cleanup -------------------------------------------------------------

rm -f Scripts/bootstrap
rmdir Scripts 2>/dev/null || true
rm -f Makefile          # root convenience Makefile (bootstrap only)
rm -f README.md       # template usage docs

# --- Done ---------------------------------------------------------------------

cat <<EOF

Done. "${NAME}" (slug "${SLUG}") is ready.

Next steps:
  1. Replace the placeholder content:
       - $W/Sources/Library/Catalogs/Localizable.xcstrings   (copy)
       - $W/Sources/Library                                   (landing / 404 pages)
       - $W/Resources/Static                                  (css, js, favicon, icons)
       - $W/Resources/Static/site.webmanifest                 (theme colours)
  2. Set a real database password in a git-ignored $W/.env
     (the committed .env.local defaults the password to the slug — do NOT ship that).
  3. Analytics ships OFF, and stays off until you opt in. To enable it: check
     String.Analytics.origin (it defaults to the platform instance at
     https://analytics.rock-n-code.com), allow that origin in
     security.contentSecurityPolicy, then set ANALYTICS_WEBSITE_ID.
  4. Point the git remote at your new repository:
       git remote set-url origin <new-repo-url>     # or 'git remote add origin ...'
  5. Build and run:
       cd $W && make site-run

The Persistence package still ships an ExampleRecord / ExampleRepository sample
model — replace it with your real domain models when you add persistence.
EOF

if [ "$URL" = "$PLACEHOLDER_URL" ]; then
	cat <<EOF

WARNING: the canonical site URL was left as the placeholder "${PLACEHOLDER_URL}".
Set the real origin in $W/Resources/Static/robots.txt and sitemap.xml before
going live, or crawlers will be pointed at a reserved example domain.
EOF
fi
