#!/usr/bin/env bash

if [[ "$(uname -s)" != "Linux" ]] || ! sudo -n true 2>/dev/null; then
  echo "skipping: managed system files require Linux and passwordless sudo"
  exit 0
fi

managed_dir="$PWD/managed-system"
managed_nested="$managed_dir/nested"
managed_file="$managed_nested/example.conf"
managed_owner="$(id -un)"
managed_group="$(id -gn)"

# Dry runs validate ownership before claiming the plan can be applied.
missing_owner="mise-bootstrap-missing-owner-$$"
cat <<EOF >mise.toml
[bootstrap.files."$PWD/missing-owner"]
content = "unreachable"
owner = "$missing_owner"
EOF
assert_fail "mise bootstrap files apply --dry-run --yes" "user '$missing_owner' does not exist"
assert_fail "mise bootstrap plan" "user '$missing_owner' does not exist"
assert_fail "test -e $PWD/missing-owner"

cat <<EOF >mise.toml
[vars]
managed_content = "first"

[bootstrap.directories."$managed_dir"]
owner = "$managed_owner"
group = "$managed_group"
mode = "0755"

[bootstrap.directories."$managed_nested"]
owner = "$managed_owner"
group = "$managed_group"
mode = "0750"

[bootstrap.files."$managed_file"]
content = '{{ vars.managed_content }}'
template = true
owner = "$managed_owner"
group = "$managed_group"
mode = "0640"
EOF

assert_contains "mise bootstrap plan" "directory:$managed_dir"
assert_contains "mise bootstrap plan" "file:$managed_file"
assert_fail "mise bootstrap plan --detailed-exitcode"
assert_contains "mise bootstrap files apply --dry-run --yes" "would create directory $managed_dir"
assert_directory_not_exists "$managed_dir"

assert_succeed "mise bootstrap files apply --yes"
assert "cat $managed_file" "first"
assert "stat -c %a $managed_nested" "750"
assert_succeed "mise bootstrap files status --missing"
assert_succeed "mise bootstrap files status --json | jq -e . >/dev/null"
assert_succeed "mise bootstrap files status --json | jq -e '.[] | select(.id.kind == \"file\") | .origin.config == \"$PWD/mise.toml\" and .origin.config_root == \"$PWD\"'"
assert_succeed "mise bootstrap plan --json | jq -e '.resources[] | select(.id.kind == \"file\") | .origin.config == \"$PWD/mise.toml\" and .origin.config_root == \"$PWD\"'"
assert_contains "mise bootstrap status --json" '"files"'

# Applying the same declarations again is a no-op.
assert_succeed "mise bootstrap files apply --yes"

# Root-only files are compared in one privileged batch and remain idempotent.
root_file="$managed_nested/root-only.conf"
locked_dir="$managed_dir/locked"
locked_nested="$locked_dir/nested"
locked_file="$locked_nested/root-only.conf"
cat <<EOF >mise.toml
[bootstrap.directories."$managed_dir"]
owner = "$managed_owner"
group = "$managed_group"
mode = "0755"

[bootstrap.directories."$managed_nested"]
owner = "$managed_owner"
group = "$managed_group"
mode = "0750"

[bootstrap.files."$managed_file"]
content = "first"
owner = "$managed_owner"
group = "$managed_group"
mode = "0640"

[bootstrap.files."$root_file"]
content = "root-only"
owner = "root"
group = "root"
mode = "0600"

[bootstrap.directories."$locked_dir"]
owner = "root"
group = "root"
mode = "0700"

[bootstrap.directories."$locked_nested"]
owner = "root"
group = "root"
mode = "0750"

[bootstrap.files."$locked_file"]
content = "locked-root-only"
owner = "root"
group = "root"
mode = "0600"
EOF
assert_succeed "mise bootstrap files apply --yes"
assert "sudo cat $root_file" "root-only"
# The invoking user cannot search this parent. Status and planning must fall
# back to the single privileged inspection batch for both metadata and content.
assert "sudo cat $locked_file" "locked-root-only"
assert_succeed "mise bootstrap files status --missing"
assert_succeed "mise bootstrap plan --detailed-exitcode"
root_inode="$(stat -c %i "$root_file")"
locked_inode="$(sudo stat -c %i "$locked_file")"
assert_succeed "mise bootstrap files apply --yes"
assert "stat -c %i $root_file" "$root_inode"
assert "sudo stat -c %i $locked_file" "$locked_inode"

cat <<EOF >mise.toml
[bootstrap.directories."$managed_dir"]
state = "absent"

[bootstrap.directories."$managed_nested"]
state = "absent"

[bootstrap.files."$managed_file"]
state = "absent"

[bootstrap.files."$root_file"]
state = "absent"

[bootstrap.directories."$locked_dir"]
state = "absent"

[bootstrap.directories."$locked_nested"]
state = "absent"

[bootstrap.files."$locked_file"]
state = "absent"
EOF

assert_contains "mise bootstrap plan" "remove"
assert_succeed "mise bootstrap files apply --yes"
assert_directory_not_exists "$managed_dir"

# Wrong node types require explicit replacement and never recursively destroy.
collision="$PWD/type-collision"
pending_collision="$PWD/pending-next-to-collision"
mkdir "$collision"
cat <<EOF >mise.toml
[bootstrap.files."$collision"]
content = "replacement"

[bootstrap.files."$pending_collision"]
content = "pending"
EOF
assert_contains "mise bootstrap plan" "unknown"
assert_contains "mise bootstrap files apply --dry-run --yes" "would write file $pending_collision"
assert_contains "mise bootstrap files apply --dry-run --yes 2>&1" "manual action required"
assert_fail "mise bootstrap files apply --yes" "set replace = true"
cat <<EOF >mise.toml
[bootstrap.files."$collision"]
content = "replacement"
replace = true
EOF
assert_succeed "mise bootstrap files apply --yes"
assert "cat $collision" "replacement"

cat <<EOF >mise.toml
[bootstrap.directories."$collision"]
replace = true
EOF
assert_succeed "mise bootstrap files apply --yes"
assert_directory_exists "$collision"

cat <<EOF >mise.toml
[bootstrap.files."$collision"]
state = "absent"
EOF
assert_contains "mise bootstrap plan" "unknown"
assert_fail "mise bootstrap files apply --yes" "declare it in [bootstrap.directories]"
cat <<EOF >mise.toml
[bootstrap.directories."$collision"]
state = "absent"
EOF
assert_succeed "mise bootstrap files apply --yes"

# Missing ancestors are created with mkdir -p semantics.
undeclared_parent="$PWD/undeclared-parent"
undeclared_nested="$undeclared_parent/one/two"
cat <<EOF >mise.toml
[bootstrap.directories."$undeclared_nested"]
mode = "0750"
EOF
assert_succeed "mise bootstrap files apply --yes"
assert_directory_exists "$undeclared_parent/one"
assert_directory_exists "$undeclared_nested"
assert "stat -c %a $undeclared_nested" "750"
cat <<EOF >mise.toml
[bootstrap.directories."$undeclared_parent"]
state = "absent"
recursive = true
EOF
assert_succeed "mise bootstrap files apply --yes"

# The nearest configuration wins when the same target is declared in layers.
layered_dir="$PWD/layered"
layered_child="$layered_dir/child"
layered_file="$layered_dir/config"
mkdir -p "$layered_child"
cat <<EOF >"$layered_dir/mise.toml"
[bootstrap.files."$layered_dir/./config"]
content = "outer"
EOF
cat <<EOF >"$layered_child/mise.toml"
[bootstrap.files."$layered_dir/nested/../config"]
content = "inner"
EOF
assert_succeed "mise -C $layered_child bootstrap files apply --yes"
assert "cat $layered_file" "inner"
cat <<EOF >"$layered_child/mise.toml"
[bootstrap.files."$layered_file"]
state = "absent"
EOF
assert_succeed "mise -C $layered_child bootstrap files apply --yes"

# Lexical aliases in one config layer are ambiguous and rejected.
duplicate_file="$PWD/duplicate-target"
cat <<EOF >mise.toml
[bootstrap.files."$duplicate_file"]
content = "first"

[bootstrap.files."$PWD/alias/../duplicate-target"]
content = "second"
EOF
assert_fail "mise bootstrap plan" "normalize to the same target"
cat <<EOF >mise.toml
[bootstrap.directories."$PWD/duplicate-directory"]

[bootstrap.directories."$PWD/alias/../duplicate-directory"]
EOF
assert_fail "mise bootstrap plan" "normalize to the same target"

# Destructive directory removal requires an explicit recursive opt-in.
mkdir -p "$managed_dir"
echo "unmanaged" >"$managed_dir/child"
cat <<EOF >mise.toml
[bootstrap.directories."$managed_dir"]
state = "absent"
EOF
assert_fail "mise bootstrap files apply --yes" "Directory not empty"
assert_directory_exists "$managed_dir"

cat <<EOF >mise.toml
[bootstrap.directories."$managed_dir"]
state = "absent"
recursive = true
EOF
assert_succeed "mise bootstrap files apply --yes"
assert_directory_not_exists "$managed_dir"

# Changes made as root never follow a parent symlink in a directory another
# user can write; changes made as the invoking user still follow it. CI runs
# this as root in a root-owned directory, which root trusts, so make the
# symlink's directory writable by others.
chmod o+w "$PWD"
linked_real="$PWD/linked-real"
linked="$PWD/linked"
mkdir "$linked_real"
ln -s "$linked_real" "$linked"
cat <<EOF >mise.toml
[bootstrap.files."$linked/owned.conf"]
content = "owned"
owner = "$managed_owner"
EOF
assert_contains "mise bootstrap plan" "crosses symlink $linked"
assert_contains "mise bootstrap files apply --dry-run --yes 2>&1" "manual action required"
assert_fail "mise bootstrap files apply --yes" "refusing to write file $linked/owned.conf"
assert_fail "test -e $linked_real/owned.conf"

cat <<EOF >mise.toml
[bootstrap.files."$linked/owned.conf"]
content = "owned"
EOF
if [[ "$(id -u)" == 0 ]]; then
  # As root, every change is made as root.
  assert_fail "mise bootstrap files apply --yes" "refusing to write file $linked/owned.conf"
  echo owned >"$linked_real/owned.conf"
else
  assert_succeed "mise bootstrap files apply --yes"
fi
assert "cat $linked_real/owned.conf" "owned"

cat <<EOF >mise.toml
[bootstrap.files."$linked/owned.conf"]
state = "absent"
EOF
if [[ "$(id -u)" == 0 ]]; then
  assert_contains "mise bootstrap plan" "crosses symlink $linked"
  assert_fail "mise bootstrap files apply --yes" "refusing to remove file $linked/owned.conf"
  assert "cat $linked_real/owned.conf" "owned"
else
  # A removal the user cannot make runs as root, which refuses the link too.
  chmod 0555 "$linked_real"
  assert_contains "mise bootstrap plan" "crosses symlink $linked"
  assert_fail "mise bootstrap files apply --yes" "refusing to remove file $linked/owned.conf"
  chmod 0755 "$linked_real"
  assert "cat $linked_real/owned.conf" "owned"
  assert_succeed "mise bootstrap files apply --yes"
  assert_fail "test -e $linked_real/owned.conf"

  # A path only root can inspect is inspected without following the link.
  sudo mkdir -m 0700 "$linked_real/locked"
  sudo sh -c "echo secret >'$linked_real/locked/root.conf'"
  cat <<EOF >mise.toml
[bootstrap.files."$linked/locked/root.conf"]
content = "secret"
EOF
  assert_contains "mise bootstrap plan" "crosses symlink $linked"
  assert_fail "mise bootstrap files apply --yes" "refusing to write file"
  cat <<EOF >mise.toml
[bootstrap.files."$linked_real/locked/root.conf"]
content = "replaced"
EOF
  assert_succeed "mise bootstrap files apply --yes"
  assert "sudo cat $linked_real/locked/root.conf" "replaced"
  # Only root can remove these, which it does relative to the resolved path.
  cat <<EOF >mise.toml
[bootstrap.files."$linked_real/locked/root.conf"]
state = "absent"

[bootstrap.directories."$linked_real/locked"]
state = "absent"
EOF
  assert_succeed "mise bootstrap files apply --yes"
  assert_directory_not_exists "$linked_real/locked"
fi

# Directories follow the same rule. Creating one walks its path strictly as
# any user, so status, dry-run, and apply all refuse the link.
mkdir "$linked_real/tree" "$linked_real/tree/inner" "$linked_real/empty" "$PWD/escape"
echo kept >"$PWD/escape/keep"
echo inner >"$linked_real/tree/inner/file"
ln -s "$PWD/escape" "$linked_real/tree/inner/escape"
echo file >"$linked_real/was-file"
cat <<EOF >mise.toml
[bootstrap.directories."$linked/created"]
owner = "$managed_owner"

[bootstrap.directories."$linked/was-file"]
replace = true
EOF
assert_contains "mise bootstrap plan" "crosses symlink $linked"
assert_contains "mise bootstrap files apply --dry-run --yes 2>&1" "manual action required"
assert_fail "mise bootstrap files apply --yes" "refusing to create directory $linked/created"
assert_directory_not_exists "$linked_real/created"
assert "cat $linked_real/was-file" "file"

# A recursive removal runs as root, which refuses the link and leaves the
# tree intact.
cat <<EOF >mise.toml
[bootstrap.directories."$linked/tree"]
state = "absent"
recursive = true
EOF
assert_contains "mise bootstrap plan" "crosses symlink $linked"
assert_fail "mise bootstrap files apply --yes" "refusing to remove directory $linked/tree"
assert "cat $linked_real/tree/inner/file" "inner"

# Declared by its resolved path, the tree is removed without following the
# symlink inside it.
cat <<EOF >mise.toml
[bootstrap.directories."$linked_real/tree"]
state = "absent"
recursive = true
EOF
assert_succeed "mise bootstrap files apply --yes"
assert_directory_not_exists "$linked_real/tree"
assert "cat $PWD/escape/keep" "kept"

# A single rmdir the user can make still follows the link.
cat <<EOF >mise.toml
[bootstrap.directories."$linked/empty"]
state = "absent"
EOF
if [[ "$(id -u)" == 0 ]]; then
  assert_fail "mise bootstrap files apply --yes" "refusing to remove directory $linked/empty"
  assert_directory_exists "$linked_real/empty"
else
  assert_succeed "mise bootstrap files apply --yes"
  assert_directory_not_exists "$linked_real/empty"
fi
chmod o-w "$PWD"
