#!/usr/bin/env bash

mkdir -p project
compose="$PWD/fake-compose"
engine="$PWD/fake-engine"

cat >"$compose" <<'EOF'
#!/bin/sh
root=$(dirname "$0")
action=
for arg in "$@"; do
  case "$arg" in
  config | ps | up | stop | down)
    action=$arg
    break
    ;;
  esac
done

case "$action" in
config)
  case " $* " in
  *" --services "*) echo cache ;;
  *" --hash=* "*) printf 'cache '; cat "$root/hash" ;;
  *) exit 2 ;;
  esac
  ;;
ps)
  if test -f "$root/state"; then
    state=$(cat "$root/state")
    if test "$state" = running; then
      printf '%s\n' '{"ID":"cache-1","Service":"cache","State":"running","Health":"healthy","ExitCode":0}'
    else
      printf '%s\n' '{"ID":"cache-1","Service":"cache","State":"exited","Health":"","ExitCode":0}'
    fi
  fi
  if test -f "$root/orphan"; then
    printf '%s\n' '{"ID":"old-1","Service":"removed","State":"running","Health":"","ExitCode":0}'
  fi
  ;;
up)
  echo running >"$root/state"
  cp "$root/hash" "$root/applied-hash"
  ;;
stop)
  echo stopped >"$root/state"
  ;;
down)
  rm -f "$root/state" "$root/applied-hash"
  ;;
*) exit 2 ;;
esac
EOF

cat >"$engine" <<'EOF'
#!/bin/sh
root=$(dirname "$0")
if test "${1:-}" = rm; then
  test "${2:-}" = --force
  test "${3:-}" = old-1
  rm -f "$root/orphan"
  exit 0
fi
hash=$(cat "$root/applied-hash")
printf '{"com.docker.compose.config-hash":"%s"}\n' "$hash"
EOF

echo hash-v1 >"hash"

write_config() {
  state=$1
  cat >mise.toml <<EOF
[bootstrap.compose.cache]
project_dir = "$PWD/project"
project_name = "mise-cache"
files = ["compose.yaml"]
state = "$state"
pull = "missing"
build = "auto"
recreate = "auto"
wait = true
wait_timeout = 30
timeout = 10
remove_orphans = true
command = ["sh", "$compose"]
engine_command = ["sh", "$engine"]
EOF
}

write_config running
assert_contains "mise bootstrap compose status" "create"
assert_fail "mise bootstrap compose status --missing"
assert_contains "mise bootstrap compose status --json" '"action": "create"'
assert_contains "mise bootstrap plan" "compose:cache"
assert_contains "mise bootstrap compose apply --dry-run --yes" "would run"
assert_fail "test -f state"

assert_succeed "mise bootstrap --only compose --yes"
assert "cat state" "running"
assert_contains "mise bootstrap compose status" "unchanged"
assert_succeed "mise bootstrap compose status --missing"

echo hash-v2 >"hash"
assert_contains "mise bootstrap compose status" "update"
assert_succeed "mise bootstrap compose apply --yes"
assert "cat applied-hash" "hash-v2"

write_config stopped
assert_contains "mise bootstrap compose status" "update"
assert_succeed "mise bootstrap compose apply --yes"
assert "cat state" "stopped"
assert_contains "mise bootstrap compose status" "unchanged"

touch orphan
assert_contains "mise bootstrap compose status" "update"
assert_contains "mise bootstrap compose apply --dry-run --yes" "rm --force old-1"
assert_succeed "mise bootstrap compose apply --yes"
assert_fail "test -f orphan"
assert_contains "mise bootstrap compose status" "unchanged"

write_config absent
assert_contains "mise bootstrap compose status" "remove"
assert_succeed "mise bootstrap compose apply --yes"
assert_fail "test -f state"
assert_contains "mise bootstrap compose status" "unchanged"

# Aggregate dry-runs may predict Compose convergence only when the exact
# dependency that makes inspection possible will be applied first.
deferred_compose="$PWD/deferred-compose"
mkdir -p deferred-project
cat >mise.toml <<EOF
[bootstrap.files."$deferred_compose"]
content = '''
#!/bin/sh
exit 0
'''
mode = "0644"

[bootstrap.compose.deferred]
project_dir = "$PWD/deferred-project"
command = ["sh", "$deferred_compose"]
engine_command = ["sh", "$engine"]
depends_on = ["file:$deferred_compose"]
EOF

assert_fail "mise bootstrap compose apply --dry-run --yes"
assert_fail "mise bootstrap --dry-run --yes --skip files"
assert_contains "mise bootstrap --dry-run --yes" "deferred-compose --project-directory"
assert_fail "test -f $deferred_compose"

# Phase ordering does not make Compose depend on skipped package changes.
if [[ "$(uname -s)" == "Linux" ]] && command -v apt-get >/dev/null; then
  cat >>mise.toml <<'EOF'
[bootstrap.packages]
"apt:mise-phase-test-nonexistent" = "latest"
EOF
  assert_contains "mise bootstrap --dry-run --yes --skip packages" "deferred-compose --project-directory"
  assert_contains "mise bootstrap --dry-run --yes --only files,compose" "deferred-compose --project-directory"
  assert_fail "mise bootstrap --dry-run --yes --only compose"
fi

# Compose declarations render the same template values as other bootstrap
# resources before validating paths and building the command line.
template_project="$PWD/template-project"
template_compose="$PWD/template-compose"
template_engine="$PWD/template-engine"
mkdir -p "$template_project"
touch "$template_project/compose.yaml" "$template_project/.env"
cat >"$template_compose" <<'EOF'
#!/bin/sh
root=$(dirname "$0")
printf '%s\n' "$@" >"$root/template-compose-args"
case " $* " in
*" config --services "*) echo cache ;;
*" config --hash=* "*) echo "cache template-hash" ;;
*" ps "*) ;;
*) exit 2 ;;
esac
EOF

cat >"$template_engine" <<'EOF'
#!/bin/sh
printf '%s\n' '{"com.docker.compose.config-hash":"template-hash"}'
EOF

cat >mise.toml <<'EOF'
[vars]
compose_project_name = "template-cache"

[bootstrap.compose.templated]
project_dir = "{{ config_root }}/template-project"
files = ["{{ config_root }}/template-project/compose.yaml"]
env_files = ["{{ config_root }}/template-project/.env"]
project_name = "{{ vars.compose_project_name }}"
command = ["sh", "{{ config_root }}/template-compose"]
engine_command = ["sh", "{{ config_root }}/template-engine"]
EOF

assert_contains "mise bootstrap compose status" "create"
assert_contains "cat template-compose-args" "--project-directory"
assert_contains "cat template-compose-args" "$template_project"
assert_contains "cat template-compose-args" "$template_project/compose.yaml"
assert_contains "cat template-compose-args" "$template_project/.env"

# A more local project replaces a global declaration before either is rendered.
# The shadowed declaration deliberately cannot render in this environment.
cat >global.toml <<'EOF'
[bootstrap.compose.shadowed]
project_dir = "{{ env.MISSING_COMPOSE_PROJECT_DIR }}"
EOF
cat >mise.toml <<EOF
[bootstrap.compose.shadowed]
project_dir = "$template_project"
command = ["sh", "$template_compose"]
engine_command = ["sh", "$template_engine"]
EOF
export MISE_GLOBAL_CONFIG_FILE="$PWD/global.toml"
assert_contains "mise bootstrap compose status" "create"
unset MISE_GLOBAL_CONFIG_FILE
