#!/usr/bin/env bash

# A plugin's MiseInstallSatisfied hook can report that an installed version is
# missing components requested through tool options. mise then reruns
# PostInstall on the existing install instead of reinstalling it.

PLUGIN_DIR="$PWD/vfox-install-satisfied"
PRE_COUNT="$MISE_DATA_DIR/install-satisfied-pre"
POST_COUNT="$MISE_DATA_DIR/install-satisfied-post"
TOOL_POSTINSTALL="$MISE_DATA_DIR/install-satisfied-tool-postinstall"

mkdir -p "$PLUGIN_DIR/hooks"

cat >"$PLUGIN_DIR/metadata.lua" <<'LUA'
PLUGIN = {}
PLUGIN.name = "install-satisfied"
PLUGIN.version = "0.1.0"
PLUGIN.homepage = "https://example.com/install-satisfied"
PLUGIN.license = "MIT"
PLUGIN.description = "install satisfied test plugin"
LUA

cat >"$PLUGIN_DIR/hooks/available.lua" <<'LUA'
function PLUGIN:Available(ctx)
	return {
		{ version = "1.0.0" },
	}
end
LUA

cat >"$PLUGIN_DIR/hooks/pre_install.lua" <<LUA
function PLUGIN:PreInstall(ctx)
	local count_file = io.open([[$PRE_COUNT]], "a")
	count_file:write("x")
	count_file:close()
	return { version = ctx.version }
end
LUA

cat >"$PLUGIN_DIR/hooks/post_install.lua" <<LUA
function PLUGIN:PostInstall(ctx)
	local count_file = io.open([[$POST_COUNT]], "a")
	count_file:write("x")
	count_file:close()

	os.execute("mkdir -p \"" .. ctx.rootPath .. "/components\"")
	for _, name in ipairs(ctx.options.components or {}) do
		if name ~= ctx.options.unavailable then
			local component = io.open(ctx.rootPath .. "/components/" .. name, "w")
			component:close()
		end
	end
end
LUA

cat >"$PLUGIN_DIR/hooks/env_keys.lua" <<'LUA'
function PLUGIN:EnvKeys(ctx)
	return {}
end
LUA

cat >"$PLUGIN_DIR/hooks/mise_install_satisfied.lua" <<'LUA'
function PLUGIN:MiseInstallSatisfied(ctx)
	if ctx.options.broken then
		error("check exploded")
	end
	local exploded = io.open(ctx.path .. "/components/explode", "r")
	if exploded then
		exploded:close()
		error("check exploded after repair")
	end
	for _, name in ipairs(ctx.options.components or {}) do
		local component = io.open(ctx.path .. "/components/" .. name, "r")
		if not component then
			return { satisfied = false, reason = "missing component " .. name }
		end
		component:close()
	end
	return true
end
LUA

git -C "$PLUGIN_DIR" init -q
git -C "$PLUGIN_DIR" add .
git -C "$PLUGIN_DIR" -c user.name=mise -c user.email=mise@example.com commit -qm "test(e2e): initialize plugin fixture"

TOOL="vfox:file://$PLUGIN_DIR"

cat >mise.toml <<EOF
[tools]
"$TOOL" = { version = "1.0.0", components = ["alpha"] }
EOF
mise install
assert "cat '$PRE_COUNT'" "x"
assert "cat '$POST_COUNT'" "x"
INSTALL_PATH="$(mise where "$TOOL")"
touch "$INSTALL_PATH/sentinel"

# All requested components are present, so nothing runs again.
mise install
assert "cat '$POST_COUNT'" "x"

cat >mise.toml <<EOF
[tools]
"$TOOL" = { version = "1.0.0", components = ["alpha", "beta"], postinstall = "echo ran >> '$TOOL_POSTINSTALL'" }
EOF
assert_contains "MISE_DEBUG=1 mise install --dry-run 2>&1" "missing component beta"
assert "cat '$POST_COUNT'" "x"

# PostInstall runs again on the existing install: no PreInstall, no wipe.
mise install
assert "cat '$PRE_COUNT'" "x"
assert "cat '$POST_COUNT'" "xx"
assert "ls '$INSTALL_PATH/components'" "alpha
beta"
assert "ls '$INSTALL_PATH/sentinel'" "$INSTALL_PATH/sentinel"
# The tool-level postinstall script runs after a repair too.
assert "cat '$TOOL_POSTINSTALL'" "ran"

# Auto-install repairs the same way.
cat >mise.toml <<EOF
[tools]
"$TOOL" = { version = "1.0.0", components = ["alpha", "beta", "gamma"] }
EOF
mise x -- true
assert "cat '$PRE_COUNT'" "x"
assert "cat '$POST_COUNT'" "xxx"
assert "ls '$INSTALL_PATH/components'" "alpha
beta
gamma"

# If PostInstall does not fix the install, mise fails and keeps it.
cat >mise.toml <<EOF
[tools]
"$TOOL" = { version = "1.0.0", components = ["alpha", "delta"], unavailable = "delta" }
EOF
assert_fail "mise install" "still does not satisfy its tool options after PostInstall: missing component delta"
assert "ls '$INSTALL_PATH/sentinel'" "$INSTALL_PATH/sentinel"

# The recheck runs after the tool-level postinstall script, which can supply
# what PostInstall did not.
cat >mise.toml <<EOF
[tools]
"$TOOL" = { version = "1.0.0", components = ["alpha", "zeta"], unavailable = "zeta", postinstall = "touch '$INSTALL_PATH/components/zeta'" }
EOF
mise install
assert "ls '$INSTALL_PATH/components/zeta'" "$INSTALL_PATH/components/zeta"

# A check that fails after PostInstall warns instead of failing the install.
cat >mise.toml <<EOF
[tools]
"$TOOL" = { version = "1.0.0", components = ["alpha", "explode"] }
EOF
assert_contains "mise install 2>&1" "MiseInstallSatisfied hook failed after PostInstall, treating install as current"

# A failing check warns and keeps the existing install.
cat >mise.toml <<EOF
[tools]
"$TOOL" = { version = "1.0.0", components = ["alpha", "epsilon"], broken = true }
EOF
assert_contains "mise install 2>&1" "MiseInstallSatisfied hook failed, treating install as current"
assert "cat '$PRE_COUNT'" "x"
