#!/usr/bin/env bash
# Convenience wrapper around infra/local/dev.compose.yml. Documented
# in infra/README.md → "Local-dev stack" → "Convenience script".
#
# Hides the Compose-profile gotcha: `docker compose down` only acts
# on services whose profile is currently active, so anything brought
# up with `--profile X` keeps running unless the same flag is on
# `down`. Every teardown / status / log command in this script
# always includes every profile in scope, so profile-gated services
# are visible and stoppable.

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
COMPOSE_FILE="${SCRIPT_DIR}/dev.compose.yml"

# Profiles defined in dev.compose.yml. Keep in sync if a new profile
# is added.
ALL_PROFILES=(dbtools observability serve-static)

# Build "--profile p1 --profile p2 …" as separate arguments.
build_all_profile_flags() {
  local p
  ALL_PROFILE_FLAGS=()
  for p in "${ALL_PROFILES[@]}"; do
    ALL_PROFILE_FLAGS+=(--profile "$p")
  done
}

# Run docker compose with every profile flagged in.
dc_all() {
  build_all_profile_flags
  docker compose -f "$COMPOSE_FILE" "${ALL_PROFILE_FLAGS[@]}" "$@"
}

# Run docker compose with no profiles (core services only).
dc_core() {
  docker compose -f "$COMPOSE_FILE" "$@"
}

# Run `up -d` with a custom set of profiles.
up_with_profiles() {
  local p
  local -a flags=()
  for p in "$@"; do
    flags+=(--profile "$p")
  done
  docker compose -f "$COMPOSE_FILE" "${flags[@]}" up -d
}

usage() {
  cat <<'USAGE'
Usage: ./infra/local/dev.sh <command> [args]

Commands:
  up [target...]        Bring up the stack.
                        Targets:
                          (none)         core only (postgres + redis + otel-collector)
                          all            core + all profiles
                          dbtools        core + pgweb
                          observability  core + jaeger
                          serve-static   core + caddy (production-build reverse proxy)
                          Multiple targets allowed (e.g. `up dbtools observability`).

  down [-v]             Tear the stack down. Always runs with every
                        profile in scope, so profile-gated services
                        (pgweb, jaeger) are caught too. -v also wipes
                        the named Docker volumes.

  stop <service>        Stop a single service (containers stay around).
                        Use `up <service>` (or restart) to bring it back.

  restart <service>     Restart a single service.

  status                docker compose ps, with every profile in scope.

  logs [service]        Follow logs (one service or all of them).

  exec <service> <cmd>  Run a command inside a running service container.

  help                  Show this help.

  <other> [args...]     Anything else is passed through to
                        `docker compose -f dev.compose.yml ...` with
                        every profile in scope.

Examples:
  ./infra/local/dev.sh up
  ./infra/local/dev.sh up all
  ./infra/local/dev.sh up observability
  ./infra/local/dev.sh down -v
  ./infra/local/dev.sh stop pgweb
  ./infra/local/dev.sh logs otel-collector
  ./infra/local/dev.sh exec postgres psql -U "$POSTGRES_USER" -d "$POSTGRES_DB"
USAGE
}

require_args() {
  local verb="$1"
  local n="$2"
  local hint="$3"
  if [[ $# -lt $((3 + n)) ]]; then
    echo "Error: '$verb' requires $hint." >&2
    echo "Try: ./infra/local/dev.sh help" >&2
    exit 64
  fi
}

cmd="${1:-help}"
[[ $# -gt 0 ]] && shift

case "$cmd" in
  up | start)
    if [[ $# -eq 0 ]]; then
      dc_core up -d
    elif [[ "$1" == "all" ]]; then
      dc_all up -d
    else
      up_with_profiles "$@"
    fi
    ;;
  down)
    dc_all down "$@"
    ;;
  stop)
    require_args stop 1 "a service name (use 'down' to tear down the whole stack)" "$@"
    dc_all stop "$@"
    ;;
  restart)
    require_args restart 1 "a service name" "$@"
    dc_all restart "$@"
    ;;
  status | ps)
    dc_all ps "$@"
    ;;
  logs)
    dc_all logs -f "$@"
    ;;
  exec)
    require_args exec 2 "a service name and a command" "$@"
    dc_all exec "$@"
    ;;
  help | -h | --help | "")
    usage
    ;;
  *)
    # Pass-through to docker compose, with profiles in scope.
    dc_all "$cmd" "$@"
    ;;
esac
