#!/usr/bin/env bash
# cf-store-name.sh - deterministic Cloudflare storage-name resolver.
#
# Owns the one naming decision the plugin previously left to the agent: a D1
# database or R2 bucket is named <accountId>-<purpose>, so a store's name
# identifies its owner and purpose (references/d1-data-capture.md § Establishing
# a store, references/r2-object-storage.md). Same inputs always yield the same
# name; that determinism is what lets "does a store for this purpose already
# exist?" be a lookup rather than a guess.
#
# Usage: cf-store-name.sh <accountId> <purpose>
# Output: stdout = the canonical <accountId>-<purpose> name (nothing else).
# Exit:   0 when both segments are valid; non-zero (fail closed) otherwise.
#
# Pure string computation: no network, no secrets, no side effects. It does NOT
# create the store or decide create-vs-reuse; that decision and its
# op=storage-provision breadcrumb live at the create call site, because this
# helper cannot know whether the store already exists.
set -u

die() { echo "[cf-store-name] error: $*" >&2; exit 1; }

account="${1:-}"; purpose="${2:-}"
[ -n "$account" ] || die "accountId required; usage: cf-store-name.sh <accountId> <purpose>"
[ -n "$purpose" ] || die "purpose required; usage: cf-store-name.sh <accountId> <purpose>"

# Lowercase each segment (D1/R2 names are lowercase), then validate. An invalid
# character is an error, not a silent fixup - a name that does not round-trip to
# what the caller asked for would defeat the "name identifies owner+purpose"
# contract.
account="$(printf '%s' "$account" | tr '[:upper:]' '[:lower:]')"
purpose="$(printf '%s' "$purpose" | tr '[:upper:]' '[:lower:]')"

case "$account" in *[!a-z0-9-]*) die "accountId '$account' has a character outside [a-z0-9-]";; esac
case "$purpose" in *[!a-z0-9-]*) die "purpose '$purpose' has a character outside [a-z0-9-]";; esac

printf '%s-%s\n' "$account" "$purpose"
