#!/usr/bin/env bash

# `mise bootstrap` runs the `bootstrap` task nobody named. A safe project
# config (plain [tasks]) and a template-free task file load without trust, and
# a project task replaces a global one by name, so the task phase must refuse
# tasks from config the user has not trusted instead of running them.

export MISE_TRUSTED_CONFIG_PATHS=""
unset CI GITHUB_ACTIONS GITHUB_ACTION 2>/dev/null || true

cat <<'EOF' >"$MISE_CONFIG_DIR/config.toml"
[tasks.bootstrap]
run = "touch $HOME/global_task_ran"
EOF

# the global task runs from a directory with no project config
mkdir -p empty project file-task
assert_succeed "cd empty && MISE_YES=0 mise bootstrap --skip tools"
assert "test -f $HOME/global_task_ran"
rm -f "$HOME/global_task_ran"

# an untrusted project task must neither run nor replace the global one
cat <<'EOF' >project/mise.toml
[tasks.bootstrap]
run = "touch $HOME/project_task_ran"
EOF
assert_fail "cd project && MISE_YES=0 mise bootstrap --skip tools" "is not trusted"
assert_fail "test -f $HOME/project_task_ran"
assert_fail "test -f $HOME/global_task_ran"
assert_contains "cd project && mise trust --show" "untrusted"

# the same for a bare task file
mkdir -p file-task/mise-tasks
cat <<'EOF' >file-task/mise-tasks/bootstrap
#!/usr/bin/env bash
touch "$HOME/project_task_ran"
EOF
chmod +x file-task/mise-tasks/bootstrap
assert_fail "cd file-task && MISE_YES=0 mise bootstrap --skip tools" "is not trusted"
assert_fail "test -f $HOME/project_task_ran"

# a project task cannot claim to be global
mkdir -p claims-global
# schema: invalid
cat <<'EOF' >claims-global/mise.toml
[tasks.bootstrap]
global = true
run = "touch $HOME/project_task_ran"
EOF
assert_fail "cd claims-global && MISE_YES=0 mise bootstrap --skip tools" "is not trusted"
assert_fail "test -f $HOME/project_task_ran"

# --skip task still applies the rest
assert_succeed "cd project && MISE_YES=0 mise bootstrap --skip tools,task"
assert_fail "test -f $HOME/project_task_ran"

# once trusted, the project task runs in place of the global one
assert_succeed "cd project && mise trust"
assert_succeed "cd project && MISE_YES=0 mise bootstrap --skip tools"
assert "test -f $HOME/project_task_ran"
assert_fail "test -f $HOME/global_task_ran"

# Only what the bootstrap task reaches has to be trusted: a task it depends on
# or names in `run`. An unrelated task in an untrusted project does not stop
# the user's own bootstrap.
cat <<'EOF' >"$MISE_CONFIG_DIR/config.toml"
[tasks.setup]
run = "touch $HOME/global_setup_ran"

[tasks.finish]
run = "touch $HOME/global_finish_ran"

[tasks.bootstrap]
depends = ["setup"]
run = [{ task = "finish" }, "touch $HOME/global_task_ran"]
EOF
rm -f "$HOME"/*_ran

mkdir -p unrelated
cat <<'EOF' >unrelated/mise.toml
[tasks.lint]
run = "touch $HOME/project_task_ran"
EOF
assert_succeed "cd unrelated && MISE_YES=0 mise bootstrap --skip tools"
assert "test -f $HOME/global_setup_ran"
assert "test -f $HOME/global_finish_ran"
assert "test -f $HOME/global_task_ran"
assert_fail "test -f $HOME/project_task_ran"
assert_contains "cd unrelated && mise trust --show" "untrusted"
rm -f "$HOME"/*_ran

# a project task that replaces a dependency
mkdir -p dep-shadow
cat <<'EOF' >dep-shadow/mise.toml
[tasks.setup]
run = "touch $HOME/project_task_ran"
EOF
assert_fail "cd dep-shadow && MISE_YES=0 mise bootstrap --skip tools" "is not trusted"
assert_fail "test -f $HOME/project_task_ran"
assert_fail "test -f $HOME/global_task_ran"

# a project task that replaces one the bootstrap task runs
mkdir -p run-shadow
cat <<'EOF' >run-shadow/mise.toml
[tasks.finish]
run = "touch $HOME/project_task_ran"
EOF
assert_fail "cd run-shadow && MISE_YES=0 mise bootstrap --skip tools" "is not trusted"
assert_fail "test -f $HOME/project_task_ran"
assert_fail "test -f $HOME/global_task_ran"

# a `run` entry whose task name is a template only resolves when the task runs
cat <<'EOF' >"$MISE_CONFIG_DIR/config.toml"
[tasks.finish]
run = "touch $HOME/global_finish_ran"

[tasks.bootstrap]
run = [{ task = "{{ 'finish' }}" }, "touch $HOME/global_task_ran"]
EOF
rm -f "$HOME"/*_ran
assert_fail "cd run-shadow && MISE_YES=0 mise bootstrap --skip tools" "not trusted"
assert_fail "test -f $HOME/project_task_ran"
assert_fail "test -f $HOME/global_task_ran"
assert_succeed "cd unrelated && MISE_YES=0 mise bootstrap --skip tools"
assert "test -f $HOME/global_finish_ran"
assert "test -f $HOME/global_task_ran"

# a `run` entry finds tasks the way the executor does: an exact name wins, so
# an untrusted `group:finish` is not reached when the global `finish` exists
cat <<'EOF' >"$MISE_CONFIG_DIR/config.toml"
[tasks.finish]
run = "touch $HOME/global_finish_ran"

[tasks.bootstrap]
run = [{ task = "finish" }, "touch $HOME/global_task_ran"]
EOF
rm -f "$HOME"/*_ran
mkdir -p namespaced
cat <<'EOF' >namespaced/mise.toml
[tasks."group:finish"]
run = "touch $HOME/project_task_ran"
EOF
assert_succeed "cd namespaced && MISE_YES=0 mise bootstrap --skip tools"
assert "test -f $HOME/global_finish_ran"
assert "test -f $HOME/global_task_ran"
assert_fail "test -f $HOME/project_task_ran"

# an exact `bootstrap` wins over an alias an untrusted task declares, as it
# does for `mise run bootstrap`
cat <<'EOF' >"$MISE_CONFIG_DIR/config.toml"
[tasks.bootstrap]
run = "touch $HOME/global_task_ran"
EOF
rm -f "$HOME"/*_ran
mkdir -p alias-shadow
cat <<'EOF' >alias-shadow/mise.toml
[tasks.setup]
alias = "bootstrap"
run = "touch $HOME/project_task_ran"
EOF
assert_succeed "cd alias-shadow && MISE_YES=0 mise bootstrap --skip tools"
assert "test -f $HOME/global_task_ran"
assert_fail "test -f $HOME/project_task_ran"

# a templated `run` entry that resolves to an untrusted task is refused before
# that task's tools are installed
cat <<'EOF' >"$MISE_CONFIG_DIR/config.toml"
[tasks.bootstrap]
run = [{ task = "{{ 'finish' }}" }, "touch $HOME/global_task_ran"]
EOF
rm -f "$HOME"/*_ran
mkdir -p tool-shadow
cat <<'EOF' >tool-shadow/mise.toml
[tasks.finish]
tools = { dummy = "2.0.0" }
run = "touch $HOME/project_task_ran"
EOF
assert_fail "cd tool-shadow && MISE_YES=0 mise bootstrap" "not trusted"
assert_fail "test -f $HOME/project_task_ran"
assert_fail "test -e $MISE_DATA_DIR/installs/dummy/2.0.0"

# a `run` entry with templated args renders before the task it names resolves
# its dependencies, so the preflight leaves it to the executor
cat <<'EOF' >"$MISE_CONFIG_DIR/config.toml"
[tasks.real]
run = "touch $HOME/global_real_ran"

[tasks.pick]
usage = 'arg "[target]" default="missing"'
depends = ["{{usage.target}}"]
run = "touch $HOME/global_pick_ran"

[tasks.bootstrap]
run = [{ task = "pick", args = ["{{ 'real' }}"] }, "touch $HOME/global_task_ran"]
EOF
rm -f "$HOME"/*_ran
assert_succeed "cd unrelated && MISE_YES=0 mise bootstrap --skip tools"
assert "test -f $HOME/global_real_ran"
assert "test -f $HOME/global_pick_ran"
assert "test -f $HOME/global_task_ran"
