#!/usr/bin/env bash
# A credential store under a tracked directory is dropped from every save;
# the save, the enrollment, and the status must say so rather than only
# `mise dot paths`, and a public key is not a credential store.
require_cmd git
repo="$MISE_STATE_DIR/history/repo.git"
mkdir -p ~/.config/fish/functions ~/.ssh
echo 'function hello; end' >~/.config/fish/functions/hello.fish
echo 'set -x API_TOKEN synthetic' >~/.config/fish/functions/secrets.fish
echo 'ssh-ed25519 AAAA synthetic' >~/.ssh/id_test.pub
echo 'synthetic private key' >~/.ssh/id_test
echo 'synthetic' >~/.ssh/client_secret.pub

# Tracking a directory reports what its baseline leaves out.
assert_contains 'mise dot track ~/.config/fish 2>&1' 'omitted: ~/.config/fish/functions/secrets.fish (credential store'
assert "git --git-dir=$repo show HEAD:home/.config/fish/functions/hello.fish" 'function hello; end'
assert_fail "git --git-dir=$repo cat-file -e HEAD:home/.config/fish/functions/secrets.fish"

# The guard matches by name alone: a public key is reported like the
# private key beside it (un-protecting is the user's call).
assert_contains 'mise dot track ~/.ssh 2>&1' 'omitted: ~/.ssh/id_test (credential store'
assert_contains 'mise dot track ~/.ssh 2>&1' 'omitted: ~/.ssh/id_test.pub (credential store'
assert_fail "git --git-dir=$repo cat-file -e HEAD:home/.ssh/id_test.pub"
assert_fail "git --git-dir=$repo cat-file -e HEAD:home/.ssh/id_test"
assert_fail "git --git-dir=$repo cat-file -e HEAD:home/.ssh/client_secret.pub"
assert_contains 'mise dot paths' 'omitted: ~/.ssh/client_secret.pub (credential store'

# A save reports every omission, even when nothing else changed.
assert_contains 'mise dot save 2>&1' 'omitted: ~/.config/fish/functions/secrets.fish (credential store'
assert_contains 'mise dot save 2>&1' 'omitted: ~/.ssh/id_test (credential store'
echo 'function hello; echo hi; end' >~/.config/fish/functions/hello.fish
assert_contains 'mise dot save 2>&1' 'secrets.fish'
assert "git --git-dir=$repo show HEAD:home/.config/fish/functions/hello.fish" 'function hello; echo hi; end'

# Status names the count in the table and in the history block.
assert_contains 'mise dot status' 'tracked (3 omitted)'
assert_contains 'mise dot status' '4 files omitted from capture (credential store)'
assert "mise dot status --json | jq -r '.history.omitted | length'" 4
assert "mise dot status --json | jq -r '.files[] | select(.target == \"~/.ssh\") | .omitted'" 3
assert_contains 'mise dot paths' 'omitted: ~/.ssh/id_test (credential store; encrypt the file before tracking it)'

# A direct credential-like file needs a deliberate plaintext choice. The
# generic --yes flag does not grant it, including without a terminal.
echo synthetic >~/.api-token
assert_fail_contains 'mise dot track --yes ~/.api-token 2>&1' '--allow-plaintext'
assert_fail "git --git-dir=$repo cat-file -e HEAD:home/.api-token"
assert_succeed 'mise dot track --allow-plaintext ~/.api-token'
assert "git --git-dir=$repo show HEAD:home/.api-token" synthetic
assert "git --git-dir=$repo show HEAD:.mise-history/manifest.json | jq -r '.enrollment[] | select(.path == \"home/.api-token\") | .allow_plaintext'" true
assert_contains 'mise dot paths' 'plaintext: ~/.api-token (explicitly allowed for plaintext tracking)'
echo changed >~/.api-token
assert_succeed 'mise dot save'
assert "git --git-dir=$repo show HEAD:home/.api-token" changed

# An approved future file is captured when it appears. If an approved
# file later becomes a directory, the declaration still tracks its files.
assert_succeed 'mise dot track --allow-plaintext ~/.future-token'
echo future >~/.future-token
assert_succeed 'mise dot save'
assert "git --git-dir=$repo show HEAD:home/.future-token" future
mv ~/.api-token "$MISE_STATE_DIR/old-api-token"
mkdir ~/.api-token
echo 'kept = true' >~/.api-token/app.toml
assert_succeed 'mise dot save'
assert "git --git-dir=$repo show HEAD:home/.api-token/app.toml" 'kept = true'

# A directory the guard would never test by name is not promised the
# protection a file of that name gets: its files are decided one by one.
mkdir -p ~/.app-secrets
echo 'client = "synthetic"' >~/.app-secrets/app.toml
assert_not_contains 'mise dot track ~/.app-secrets 2>&1' 'will be omitted from every save'
assert "git --git-dir=$repo show HEAD:home/.app-secrets/app.toml" 'client = "synthetic"'

# and a path with no kind yet is told what happens to it as a file, not
# promised an omission the directory it becomes would never get
assert_contains 'mise dot track ~/.secrets-later 2>&1' 'if it is created as a file, never as a directory'
mkdir -p ~/.secrets-later
echo 'kept = true' >~/.secrets-later/app.toml
assert_succeed 'mise dot save'
assert "git --git-dir=$repo show HEAD:home/.secrets-later/app.toml" 'kept = true'

# A symlinked home directory is a supported portable root. Its status
# rows must match omissions from the canonical paths used by the walk.
alias_home="$(dirname "$HOME")/alias-home"
ln -s "$HOME" "$alias_home"
assert "env HOME=$alias_home XDG_CONFIG_HOME=$alias_home/.config MISE_CONFIG_DIR=$alias_home/.config/mise mise dot status --json | jq -r '.files[] | select(.target == \"~/.ssh\") | .omitted'" 3
echo ordinary >~/.config/mise/hosts.yml
assert_succeed "env HOME=$alias_home XDG_CONFIG_HOME=$alias_home/.config MISE_CONFIG_DIR=$alias_home/.config/mise mise dot track --allow-plaintext $alias_home/.config/mise/hosts.yml"
assert_contains "git --git-dir=$repo ls-tree -r --name-only HEAD" 'hosts.yml'
