#!/usr/bin/env bash
# appctl — convenience wrapper for the dev/prod compose split.
#
# Replaces the old Makefile. The dev compose chain layers
# docker-compose.dev.yml on top of docker-compose.yml so source bind
# mounts and the css-watcher sidecar only show up for `appctl dev …`.
# Prod uses docker-compose.yml alone.

set -euo pipefail

# Resolve the repo root (parent of bin/) regardless of where the user
# invokes us from, so cd into the right place even when called via the
# top-level ./appctl symlink.
SCRIPT_PATH="$(readlink -f "${BASH_SOURCE[0]}")"
REPO_ROOT="$(cd "$(dirname "$SCRIPT_PATH")/.." && pwd)"
cd "$REPO_ROOT"

# Exported so the css-watcher service can run as the host user via
# `user: "${HOST_UID}:${HOST_GID}"` in docker-compose.dev.yml. Files
# the watcher writes into bind-mounted host paths
# (public/assets/css/app.css, public/assets/js/vendor/*) then land
# with normal ownership.
export HOST_UID="${HOST_UID:-$(id -u)}"
export HOST_GID="${HOST_GID:-$(id -g)}"

COMPOSE_PROD=(docker compose)
COMPOSE_DEV=(docker compose -f docker-compose.yml -f docker-compose.dev.yml)

usage() {
    cat <<'EOF'
appctl — Sprint Planner dev/prod/test wrapper

USAGE
  appctl <command> [subcommand]

DEV
  appctl dev start       start dev stack (app + css-watcher) in foreground
  appctl dev stop        stop and remove dev containers
  appctl dev build       rebuild dev images
  appctl dev shell       bash into the running app container
  appctl dev logs        tail logs from the dev stack

PROD
  appctl prod start      start prod stack detached
  appctl prod stop       stop and remove prod containers
  appctl prod build      rebuild prod images

CHECKS (one-shot containers, no running stack required)
  appctl lint            php -l on src/ + tests/
  appctl test            phpunit
  appctl check           lint + test (used by the /check Claude Code skill)

OTHER
  appctl completion      print the bash completion script to stdout
  appctl help            show this message
EOF
}

die() {
    printf 'appctl: %s\n' "$*" >&2
    exit 2
}

# --- bash completion auto-source on first run --------------------------
#
# We ship bin/appctl-completion.bash next to this script. On the very
# first interactive invocation we offer to add a `source` line to the
# user's ~/.bashrc — only once, gated by a marker file in $HOME so the
# prompt never appears again, and only when stdin is a TTY (CI / piped
# invocations are silent).
maybe_offer_completion() {
    [[ -t 0 && -t 1 ]] || return 0
    [[ "${APPCTL_NO_COMPLETION_PROMPT:-}" != "1" ]] || return 0

    local marker="${HOME}/.config/appctl/completion-installed"
    [[ ! -e "$marker" ]] || return 0

    local completion_script="${REPO_ROOT}/bin/appctl-completion.bash"
    [[ -f "$completion_script" ]] || return 0

    local bashrc="${HOME}/.bashrc"
    if [[ -f "$bashrc" ]] && grep -Fq "appctl-completion.bash" "$bashrc"; then
        # Already wired up by a previous repo checkout; just write the
        # marker so we stop asking.
        mkdir -p "$(dirname "$marker")"
        : > "$marker"
        return 0
    fi

    printf '\n[appctl] bash completion is not yet installed.\n'
    printf '        Add `source %s` to %s? [y/N] ' \
        "$completion_script" "$bashrc"
    local reply
    read -r reply || reply=""
    case "$reply" in
        y|Y|yes|YES)
            mkdir -p "$(dirname "$marker")"
            {
                printf '\n# appctl bash completion (added by appctl on first run)\n'
                printf 'source %q\n' "$completion_script"
            } >> "$bashrc"
            : > "$marker"
            printf '[appctl] added. Open a new shell or `source %s` to activate.\n\n' "$bashrc"
            ;;
        *)
            mkdir -p "$(dirname "$marker")"
            : > "$marker"
            printf '[appctl] skipped. Re-run with APPCTL_NO_COMPLETION_PROMPT=1 to silence; delete %s to ask again.\n\n' "$marker"
            ;;
    esac
}

# --- subcommands -------------------------------------------------------

cmd_dev() {
    local sub="${1:-}"
    case "$sub" in
        start)  "${COMPOSE_DEV[@]}" up ;;
        stop)   "${COMPOSE_DEV[@]}" down ;;
        build)  "${COMPOSE_DEV[@]}" build ;;
        shell)  "${COMPOSE_DEV[@]}" exec app bash ;;
        logs)   "${COMPOSE_DEV[@]}" logs -f ;;
        ""|help|-h|--help) usage ;;
        *) die "unknown dev subcommand: $sub (try: start|stop|build|shell|logs)" ;;
    esac
}

cmd_prod() {
    local sub="${1:-}"
    case "$sub" in
        start)  "${COMPOSE_PROD[@]}" up -d ;;
        stop)   "${COMPOSE_PROD[@]}" down ;;
        build)  "${COMPOSE_PROD[@]}" build ;;
        ""|help|-h|--help) usage ;;
        *) die "unknown prod subcommand: $sub (try: start|stop|build)" ;;
    esac
}

cmd_lint() {
    "${COMPOSE_DEV[@]}" --profile test run --rm tests \
        sh -c 'find src tests -name "*.php" -print0 | xargs -0 -n1 -P 4 php -l > /dev/null && echo "lint: OK"'
}

cmd_test() {
    "${COMPOSE_DEV[@]}" --profile test run --rm tests
}

cmd_check() {
    cmd_lint
    cmd_test
}

cmd_completion() {
    local completion_script="${REPO_ROOT}/bin/appctl-completion.bash"
    [[ -f "$completion_script" ]] || die "completion script missing: $completion_script"
    cat "$completion_script"
}

# --- dispatch ----------------------------------------------------------

maybe_offer_completion

cmd="${1:-help}"
shift || true

case "$cmd" in
    dev)         cmd_dev "$@" ;;
    prod)        cmd_prod "$@" ;;
    lint)        cmd_lint ;;
    test)        cmd_test ;;
    check)       cmd_check ;;
    completion)  cmd_completion ;;
    help|-h|--help|"") usage ;;
    *) die "unknown command: $cmd (try: appctl help)" ;;
esac
