#!/usr/bin/env bash
set -uo pipefail

CONFIG_FILE="${STACK_CONF:-$HOME/stack.conf}"

COMPOSE_CANDIDATES=(
  "compose.yml"
  "compose.yaml"
  "docker-compose.yml"
  "docker-compose.yaml"
)

usage() {
  cat <<'EOF'
Usage:
  stack start
  stack add .
  stack add foldername
  stack add folder1 folder2 folder3
EOF
}

strip_quotes() {
  local s="$1"
  s="${s#\"}"
  s="${s%\"}"
  s="${s#\'}"
  s="${s%\'}"
  printf '%s' "$s"
}

resolve_path() {
  local input="$1"
  if command -v realpath >/dev/null 2>&1; then
    realpath -m "$input"
  else
    python3 - "$input" <<'PY'
import os, sys
print(os.path.abspath(os.path.expanduser(sys.argv[1])))
PY
  fi
}

ensure_config_exists() {
  if [[ ! -f "$CONFIG_FILE" ]]; then
    mkdir -p "$(dirname "$CONFIG_FILE")"
    cat > "$CONFIG_FILE" <<EOF
STACK_HOME=$HOME
# one folder per line below
EOF
  fi
}

load_config() {
  STACK_HOME=""
  STACK_DIRS=()

  while IFS= read -r line || [[ -n "$line" ]]; do
    line="${line%$'\r'}"
    [[ -z "$line" ]] && continue
    [[ "$line" =~ ^[[:space:]]*# ]] && continue

    if [[ "$line" == STACK_HOME=* ]]; then
      STACK_HOME="$(strip_quotes "${line#STACK_HOME=}")"
      continue
    fi

    STACK_DIRS+=("$(strip_quotes "$line")")
  done < "$CONFIG_FILE"

  [[ -z "$STACK_HOME" ]] && STACK_HOME="$HOME"
}

compose_cmd() {
  if docker compose version >/dev/null 2>&1; then
    printf 'docker compose'
  elif command -v docker-compose >/dev/null 2>&1; then
    printf 'docker-compose'
  else
    echo "error: neither 'docker compose' nor 'docker-compose' is installed." >&2
    exit 1
  fi
}

add_folder() {
  ensure_config_exists
  load_config

  if [[ $# -eq 0 ]]; then
    echo "usage: stack add .|foldername|/path/to/folder [more folders...]" >&2
    exit 1
  fi

  local added=0
  local arg target
  for arg in "$@"; do
    if [[ "$arg" == "." ]]; then
      target="$PWD"
    elif [[ "$arg" == /* ]]; then
      target="$arg"
    else
      target="$PWD/$arg"
    fi

    target="$(resolve_path "$target")"

    if [[ ! -d "$target" ]]; then
      echo "error: folder does not exist: $target" >&2
      continue
    fi

    if grep -Fxq -- "$target" "$CONFIG_FILE"; then
      echo "already in stack.conf: $target"
      continue
    fi || true

    printf '%s\n' "$target" >> "$CONFIG_FILE"
    echo "added: $target"
    added=1
  done

  [[ "$added" -eq 0 ]] && exit 1
}

start_stacks() {
  ensure_config_exists
  load_config

  local cc
  cc="$(compose_cmd)"

  local any=0 dir file found
  for dir in "${STACK_DIRS[@]}"; do
    [[ -z "$dir" ]] && continue

    if [[ ! -d "$dir" ]]; then
      echo "skipping missing folder: $dir" >&2
      continue
    fi

    found=0
    for file in "${COMPOSE_CANDIDATES[@]}"; do
      if [[ -f "$dir/$file" ]]; then
        found=1
        any=1
        echo "starting: $dir/$file"
        (cd "$dir" && $cc -f "$file" up -d)
      fi
    done

    if [[ "$found" -eq 0 ]]; then
      echo "no compose file found in: $dir" >&2
    fi
  done

  if [[ "$any" -eq 0 ]]; then
    echo "no compose files were started."
  fi
}

update_stacks() {
  ensure_config_exists
  load_config

  local cc
  cc="$(compose_cmd)"

  local any=0 dir file found
  for dir in "${STACK_DIRS[@]}"; do
    [[ -z "$dir" ]] && continue

    if [[ ! -d "$dir" ]]; then
      echo "skipping missing folder: $dir" >&2
      continue
    fi

    found=0
    for file in "${COMPOSE_CANDIDATES[@]}"; do
      if [[ -f "$dir/$file" ]]; then
        found=1
        any=1
        echo "updating: $dir/$file"

        (
          cd "$dir" && \
          $cc -f "$file" pull && \
          $cc -f "$file" up -d
        )
      fi
    done

    if [[ "$found" -eq 0 ]]; then
      echo "no compose file in: $dir" >&2
    fi
  done

  if [[ "$any" -eq 0 ]]; then
    echo "no compose files updated."
  fi
}

main() {
  local cmd="${1:-}"
  shift || true

  case "$cmd" in
    start)
      start_stacks
      ;;
    update)
      update_stacks
      ;;
    add)
      add_folder "$@"
      ;;
    ""|-h|--help|help)
      usage
      ;;
    *)
      echo "unknown command: $cmd" >&2
      usage
      exit 1
      ;;
  esac
}

main "$@"