#!/usr/bin/env bash
set -euo pipefail

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)"
DECKCTL="$ROOT/bin/deckctl"
TMP="$(mktemp -d)"
trap 'rm -rf "$TMP"' EXIT

pass=0
fail=0

ok() {
  pass=$((pass + 1))
  printf 'PASS %s\n' "$1"
}

bad() {
  fail=$((fail + 1))
  printf 'FAIL %s\n' "$1"
}

write_fixture_registry() {
  cat >"$1" <<'EOF'
{
  "schema_version": 1,
  "hosts": [
    {
      "name": "registry-stub",
      "ssh_alias": "registry-stub",
      "state": "reachable",
      "machine_id": null,
      "roles": ["builder"],
      "access": {
        "lan": null,
        "tailscale_ip": { "host": "100.64.0.9", "port": 2222, "user": "user", "identity_file": null },
        "tailscale_ssh": null
      },
      "rustdesk": null,
      "notes": "registry-stub fixture"
    },
    {
      "name": "debian1",
      "ssh_alias": "debian1",
      "state": "unreachable",
      "machine_id": null,
      "roles": ["builder"],
      "access": {
        "lan": null,
        "tailscale_ip": { "host": "100.64.0.1", "port": 2222, "user": "user", "identity_file": null },
        "tailscale_ssh": null
      },
      "rustdesk": null,
      "notes": "debian1 fixture"
    },
    {
      "name": "debian2",
      "ssh_alias": "debian2",
      "state": "unreachable",
      "machine_id": null,
      "roles": ["builder"],
      "access": {
        "lan": null,
        "tailscale_ip": { "host": "100.64.0.2", "port": 2222, "user": "user", "identity_file": null },
        "tailscale_ssh": null
      },
      "rustdesk": null,
      "notes": "debian2 fixture"
    },
    {
      "name": "debian3",
      "ssh_alias": "debian3",
      "state": "unreachable",
      "machine_id": null,
      "roles": ["builder"],
      "access": {
        "lan": null,
        "tailscale_ip": { "host": "100.64.0.3", "port": 2222, "user": "user", "identity_file": null },
        "tailscale_ssh": null
      },
      "rustdesk": null,
      "notes": "debian3 fixture"
    }
  ],
  "orders": {
    "build": ["registry-stub"]
  }
}
EOF
}

write_fixture_fleet() {
  cat >"$1" <<'EOF'
{
  "schema_version": 1,
  "nodes": {
    "workstation": {
      "transport": "local",
      "roles": ["control", "agent-runtime"],
      "profiles": ["shared-agent-tools", "workstation"],
      "execution": "last-resort"
    },
    "debian1": {
      "transport": "ssh",
      "host_ref": "debian1",
      "roles": ["builder", "agent-runtime"],
      "profiles": ["shared-agent-tools", "buildbox", "buildbox-root"],
      "execution": "normal"
    },
    "debian2": {
      "transport": "ssh",
      "host_ref": "debian2",
      "roles": ["builder", "agent-runtime"],
      "profiles": ["shared-agent-tools", "buildbox", "buildbox-root"],
      "execution": "normal"
    },
    "debian3": {
      "transport": "ssh",
      "host_ref": "debian3",
      "roles": ["builder", "agent-runtime"],
      "profiles": ["shared-agent-tools", "buildbox", "buildbox-root"],
      "execution": "normal"
    }
  },
  "fallback": {
    "enabled": true,
    "node": "workstation",
    "requires_all_unavailable": ["debian1", "debian2", "debian3"],
    "max_concurrent_local_jobs": 1,
    "activation_windows": 3,
    "health_window_sec": 60,
    "lease_ttl_sec": 900
  }
}
EOF
}

setup_poison_path() {
  mkdir -p "$TMP/bin"
  cat >"$TMP/bin/ssh" <<'EOF'
#!/usr/bin/env bash
echo "poison ssh invoked: $*" >&2
touch "${DECKCTL_FLEET_POISON_MARKER:?}"
exit 99
EOF
  cat >"$TMP/bin/scp" <<'EOF'
#!/usr/bin/env bash
echo "poison scp invoked: $*" >&2
touch "${DECKCTL_FLEET_POISON_MARKER:?}"
exit 99
EOF
  chmod +x "$TMP/bin/ssh" "$TMP/bin/scp"
}

setup_gitleaks_shim() {
  mkdir -p "$TMP/bin"
  cat >"$TMP/bin/gitleaks" <<'EOF'
#!/usr/bin/env bash
for arg in "$@"; do
  case "$arg" in
    *secret-fixture*) exit 9 ;;
  esac
done
exit 0
EOF
  chmod +x "$TMP/bin/gitleaks"
}

TEST_AGENT="syncfix"
FLEET_FILE="$TMP/fleet.json"
REGISTRY_FILE="$TMP/buildbox-hosts.json"
POISON_MARKER="$TMP/poison-touched"
TEST_HOME="$TMP/home"
SANDBOX="$TMP/repo"
AGENT_HOME="$TEST_HOME/.syncfix-agent"
REPO_AGENT="$SANDBOX/modules/workstation/$TEST_AGENT"

write_fixture_fleet "$FLEET_FILE"
write_fixture_registry "$REGISTRY_FILE"
setup_poison_path
setup_gitleaks_shim

mkdir -p "$SANDBOX/modules" "$TEST_HOME/.local/state/overdeck/backups"
cp -a "$ROOT/bin" "$ROOT/lib" "$SANDBOX/"
cp -a "$ROOT/modules/workstation" "$SANDBOX/modules/"
rm -rf "$SANDBOX/modules/workstation"/*/
mkdir -p "$SANDBOX/modules/workstation/manifest" "$SANDBOX/modules/workstation/claude/lib"
cp -a "$ROOT/modules/workstation/claude/lib/." "$SANDBOX/modules/workstation/claude/lib/"
cp "$ROOT/modules/workstation/deny.list" "$SANDBOX/modules/workstation/"
cp -a "$ROOT/modules/fleet" "$SANDBOX/modules/"
mkdir -p "$SANDBOX/modules/buildbox"
cp "$ROOT/modules/buildbox/devtools.json" "$SANDBOX/modules/buildbox/"

cat >"$SANDBOX/modules/workstation/manifest/${TEST_AGENT}.json" <<'EOF'
{
  "home": "~/.syncfix-agent",
  "entries": [
    { "path": "config.json", "strategy": "copy" },
    { "path": "tools", "strategy": "symlink" }
  ]
}
EOF

mkdir -p "$REPO_AGENT/tools"
printf '{"copy":"fixture"}\n' >"$REPO_AGENT/config.json"
printf 'tool-bin\n' >"$REPO_AGENT/tools/bin"
printf '{"token":"local"}\n' >"$REPO_AGENT/credentials.json"
printf 'stripe_key = "sk_live_fixturevalue"\n' >"$REPO_AGENT/secret-fixture.json"

run_deckctl() {
  HOME="$TEST_HOME" DECKCTL_FLEET_FILE="$FLEET_FILE" BUILDBOX_HOSTS_CONFIG="$REGISTRY_FILE" \
    DECKCTL_FLEET_POISON_MARKER="$POISON_MARKER" \
    PATH="$TMP/bin:$PATH" "$SANDBOX/bin/deckctl" "$@"
}

backup_count() {
  find "$TEST_HOME/.local/state/overdeck/backups" -type f 2>/dev/null | wc -l
}

rm -f "$POISON_MARKER"
rm -rf "$AGENT_HOME"
set +e
out=$(run_deckctl sync apply "$TEST_AGENT" config.json 2>&1)
rc=$?
set -e
dest="$AGENT_HOME/config.json"
if [[ $rc -eq 0 && -f "$dest" && "$(cat "$dest")" == "$(cat "$REPO_AGENT/config.json")" ]]; then
  ok 'copy apply writes repo bytes to destination'
else
  bad "copy apply writes repo bytes (rc=$rc dest=$(cat "$dest" 2>/dev/null || echo missing) out=$out)"
fi
if [[ ! -e "$POISON_MARKER" ]]; then
  ok 'copy apply uses local transport only'
else
  bad 'copy apply invoked poisoned ssh/scp'
fi

rm -f "$POISON_MARKER"
rm -f "$AGENT_HOME/tools"
set +e
out=$(run_deckctl sync apply "$TEST_AGENT" tools 2>&1)
rc=$?
set -e
repo_tools="$REPO_AGENT/tools"
if [[ $rc -eq 0 && -L "$AGENT_HOME/tools" && "$(readlink "$AGENT_HOME/tools")" == "$repo_tools" ]]; then
  ok 'symlink apply points home entry at repo path'
else
  bad "symlink apply (rc=$rc link=$(readlink "$AGENT_HOME/tools" 2>/dev/null || echo missing) want=$repo_tools out=$out)"
fi

before_backups=$(backup_count)
before_config_hash=$(sha256sum "$dest" | cut -d' ' -f1)
before_tools_target=$(readlink "$AGENT_HOME/tools")
set +e
out_copy=$(run_deckctl sync apply "$TEST_AGENT" config.json 2>&1)
rc_copy=$?
out_link=$(run_deckctl sync apply "$TEST_AGENT" tools 2>&1)
rc_link=$?
set -e
after_backups=$(backup_count)
after_config_hash=$(sha256sum "$dest" | cut -d' ' -f1)
if [[ $rc_copy -eq 0 && $rc_link -eq 0 && "$before_backups" == "$after_backups" && "$before_config_hash" == "$after_config_hash" && "$(readlink "$AGENT_HOME/tools")" == "$before_tools_target" ]]; then
  ok 'second apply is idempotent without backup or rewrite'
else
  bad "idempotent apply (rc_copy=$rc_copy rc_link=$rc_link backups $before_backups->$after_backups out=$out_copy $out_link)"
fi

rm -rf "$AGENT_HOME"
set +e
out=$(run_deckctl sync apply "$TEST_AGENT" 2>&1)
rc=$?
set -e
if [[ $rc -eq 0 && -f "$AGENT_HOME/config.json" && -L "$AGENT_HOME/tools" ]]; then
  ok 'whole-agent apply converges every manifest entry'
else
  bad "whole-agent apply (rc=$rc config=$([[ -f $AGENT_HOME/config.json ]] && echo yes || echo no) tools=$([[ -L $AGENT_HOME/tools ]] && echo yes || echo no) out=$out)"
fi

cat >"$SANDBOX/modules/workstation/manifest/${TEST_AGENT}.json" <<'EOF'
{
  "home": "~/.syncfix-agent",
  "entries": [
    { "path": "config.json", "strategy": "copy" },
    { "path": "tools", "strategy": "symlink" },
    { "path": "credentials.json", "strategy": "copy" },
    { "path": "secret-fixture.json", "strategy": "copy" }
  ]
}
EOF

rm -f "$AGENT_HOME/credentials.json"
set +e
out=$(run_deckctl sync apply "$TEST_AGENT" credentials.json 2>&1)
rc=$?
set -e
if [[ $rc -ne 0 ]] && printf '%s\n' "$out" | grep -q 'deny-listed path credentials.json' && [[ ! -e "$AGENT_HOME/credentials.json" ]]; then
  ok 'deny-listed apply refuses without writing'
else
  bad "deny-listed apply (rc=$rc exists=$([[ -e $AGENT_HOME/credentials.json ]] && echo yes || echo no) out=$out)"
fi

printf '{"copy":"fixture"}\n' >"$dest"
run_deckctl sync apply "$TEST_AGENT" config.json >/dev/null 2>&1 || true
printf '{"copy":"live-edit"}\n' >"$dest"
set +e
out=$(run_deckctl sync apply "$TEST_AGENT" config.json 2>&1)
rc=$?
set -e
if [[ $rc -ne 0 ]] && printf '%s\n' "$out" | grep -q 'live edits' && [[ "$(cat "$dest")" == '{"copy":"live-edit"}' ]]; then
  ok 'baseline drift apply refuses without writing'
else
  bad "baseline drift apply (rc=$rc content=$(cat "$dest") out=$out)"
fi

rm -f "$AGENT_HOME/secret-fixture.json"
set +e
out=$(run_deckctl sync apply "$TEST_AGENT" secret-fixture.json 2>&1)
rc=$?
set -e
if [[ $rc -ne 0 ]] && printf '%s\n' "$out" | grep -q 'secret pattern detected' && [[ ! -e "$AGENT_HOME/secret-fixture.json" ]]; then
  ok 'secret scan apply refuses without writing'
else
  bad "secret scan apply (rc=$rc exists=$([[ -e $AGENT_HOME/secret-fixture.json ]] && echo yes || echo no) out=$out)"
fi

printf '\n%d passed, %d failed\n' "$pass" "$fail"
[[ "$fail" -eq 0 ]]
