#!/usr/bin/env bash
#clear
#export PS4='\e[90m+${LINENO} in ${#BASH_SOURCE[@]}>${FUNCNAME[0]}:${BASH_SOURCE[@]##*/} \e[0m'
#set -x

level=$1
if [ -z "$level" ]; then
  level=1
else 
  # remove the level parameter
  shift
fi
echo "starting: ${BASH_SOURCE[@]##*/} <LOG_LEVEL=$1>"

#echo "sourcing init"
source this
source test.suite

log.level $level

competionArray=(once config list file ite)
source oo

test.ossh() 
{
((TEST_COUNTER++))
console.log "


Test 0: ossh \"$*\"
===================================================================="
ossh.start "$@"
console.log "RETURN: $RETURN_VALUE  Result: $RESULT 
===================================================================="
}

test.case - "ossh shows usage when no args" \
   ossh $*
expect 1 "*" "ossh with no args returns 1 (usage)"

source ossh

### test.method

# ============================================================================
# Test ossh.config.create - user@host parsing (bug fix #1)
# ============================================================================

test.case $level "ossh.config.create parses user@host correctly" \
  ossh.config.create test@testhost

# Verify Host line uses hostname only (not user@host)
HOST_LINE=$(grep "^Host " "$CONFIG_PATH/result.txt" 2>/dev/null | head -1)
if echo "$HOST_LINE" | grep -q "^Host testhost$"; then
  expect.pass "Host line is 'Host testhost' (no @ symbol)"
else
  expect.fail "Host line should be 'Host testhost', got: $HOST_LINE"
fi

test.case $level "ossh.config.create parses User from user@host" \
  ossh.config.create test@testhost

USER_LINE=$(grep "User " "$CONFIG_PATH/result.txt" 2>/dev/null | head -1)
if echo "$USER_LINE" | grep -q "User test$"; then
  expect.pass "User parsed as 'test'"
else
  expect.fail "User should be 'test', got: $USER_LINE"
fi

test.case $level "ossh.config.create parses HostName from user@host" \
  ossh.config.create test@testhost

HOSTNAME_LINE=$(grep "HostName " "$CONFIG_PATH/result.txt" 2>/dev/null | head -1)
if echo "$HOSTNAME_LINE" | grep -q "HostName testhost$"; then
  expect.pass "HostName parsed as 'testhost'"
else
  expect.fail "HostName should be 'testhost', got: $HOSTNAME_LINE"
fi

# ============================================================================
# Test private.config.create sets RESULT via create.result (bug fix #4)
# ============================================================================

test.case $level "config.create sets RESULT via create.result" \
  ossh.config.create test@testhost

if [ -n "$RESULT" ] && [ "$RESULT" = "testhost" ]; then
  expect.pass "RESULT set to 'testhost'"
else
  expect.fail "RESULT should be 'testhost', got: '$RESULT'"
fi

# ============================================================================
# Test ossh.config.save.last - writes clean entry (bug fix #2, #3)
# ============================================================================

# Use a temp file to avoid polluting real ~/.ssh/config
TEST_SSH_CONFIG=$(mktemp "${TMPDIR:-/tmp}/test.ossh.config.XXXXXX")

# First, create a config entry
ossh.config.create test@testhost

test.case $level "ossh.config.save.last writes clean entry to file" \
  ossh.config.save.last "$TEST_SSH_CONFIG"

if grep -q "^Host testhost$" "$TEST_SSH_CONFIG" 2>/dev/null; then
  expect.pass "config.save.last wrote 'Host testhost' to file"
else
  expect.fail "config.save.last should write 'Host testhost' to file"
fi

# ============================================================================
# Test ossh.config.save.last - duplicate detection (bug fix #3)
# ============================================================================

# config.save.last was already called once above, so calling again should detect duplicate
test.case $level "ossh.config.save.last detects duplicate Host entry" \
  ossh.config.save.last "$TEST_SSH_CONFIG"

if [ "${RETURN_VALUE:-0}" -eq 1 ]; then
  expect.pass "config.save.last returned 1 for duplicate Host"
else
  expect.fail "config.save.last should return 1 for duplicate, got: ${RETURN_VALUE:-empty}"
fi

# Verify only one entry exists (no duplicate appended)
ENTRY_COUNT=$(grep -c "^Host testhost$" "$TEST_SSH_CONFIG" 2>/dev/null || echo 0)
test.case $level "config.save.last did not append duplicate entry" \
  echo "$ENTRY_COUNT"

if [ "${ENTRY_COUNT:-0}" -eq 1 ]; then
  expect.pass "only 1 'Host testhost' entry in file"
else
  expect.fail "should have exactly 1 entry, found: $ENTRY_COUNT"
fi

# ============================================================================
# Cleanup config.create tests
# ============================================================================

rm -f "$TEST_SSH_CONFIG" 2>/dev/null

# ============================================================================
# Test ossh config.<field>.set / config.<field>.get — per-field OOP methods
# ============================================================================

# Setup: temp SSH config with two Host blocks
TEST_CONFIG_FILE=$(mktemp "${TMPDIR:-/tmp}/test.ossh.fieldops.XXXXXX")
cat > "$TEST_CONFIG_FILE" << 'SSHEOF'
Host alpha.example
 User alice
 Port 22
 HostName alpha.example.com
 IdentityFile ~/.ssh/id_ed25519

Host beta.example
 User bob
 Port 2222
 HostName beta.example.com
 IdentityFile ~/.ssh/ids/betakey/id_ed25519
SSHEOF

ORIGINAL_CURRENT_SSH_DIR="$CURRENT_SSH_DIR"
TEST_SSH_DIR=$(mktemp -d "${TMPDIR:-/tmp}/test.ossh.sshdir.XXXXXX")
cp "$TEST_CONFIG_FILE" "$TEST_SSH_DIR/config"
export CURRENT_SSH_DIR="$TEST_SSH_DIR"

# --- Verify config.set/config.get exist as per-field dispatchers ---

if grep -q '^ossh\.config\.set()' "$OOSH_DIR/ossh"; then
  expect.pass "ossh.config.set() exists (per-field dispatcher)"
else
  expect.fail "ossh.config.set() missing"
fi

if grep -q '^ossh\.config\.get()' "$OOSH_DIR/ossh"; then
  expect.pass "ossh.config.get() exists (per-field dispatcher)"
else
  expect.fail "ossh.config.get() missing"
fi

# --- config.<field>.get: missing params → usage ---

test.case $level "config.user.get missing params returns error" \
  private.ossh.config.user.get
if [ "$RETURN_VALUE" -ne 0 ]; then
  expect.pass "returns non-zero on missing params"
else
  expect.fail "should return non-zero when no configItem given"
fi

test.case $level "config.port.set missing value returns error" \
  private.ossh.config.port.set alpha.example
if [ "$RETURN_VALUE" -ne 0 ]; then
  expect.pass "returns non-zero without value"
else
  expect.fail "should return non-zero when value missing"
fi

# --- Helper: capture stdout from .get methods (they echo, not create.result) ---
# The private.ossh.config.field.get helper echoes to stdout.
# test.case doesn't capture stdout into RESULT for these, so we capture manually.

# --- config.<field>.get: read existing fields ---

GOT=$(private.ossh.config.user.get alpha.example 2>/dev/null)
test.case $level "config.user.get reads User from alpha" echo "$GOT"
if [ "$GOT" = "alice" ]; then
  expect.pass "User=alice"
else
  expect.fail "expected 'alice', got: '$GOT'"
fi

GOT=$(private.ossh.config.port.get beta.example 2>/dev/null)
test.case $level "config.port.get reads Port from beta" echo "$GOT"
if [ "$GOT" = "2222" ]; then
  expect.pass "Port=2222"
else
  expect.fail "expected '2222', got: '$GOT'"
fi

GOT=$(private.ossh.config.hostName.get alpha.example 2>/dev/null)
test.case $level "config.hostName.get reads HostName from alpha" echo "$GOT"
if [ "$GOT" = "alpha.example.com" ]; then
  expect.pass "HostName=alpha.example.com"
else
  expect.fail "expected 'alpha.example.com', got: '$GOT'"
fi

GOT=$(private.ossh.config.identityFile.get beta.example 2>/dev/null)
test.case $level "config.identityFile.get reads IdentityFile from beta" echo "$GOT"
if [ "$GOT" = "~/.ssh/ids/betakey/id_ed25519" ]; then
  expect.pass "IdentityFile correct"
else
  expect.fail "expected '~/.ssh/ids/betakey/id_ed25519', got: '$GOT'"
fi

# --- config.<field>.get: non-existent host → error ---

test.case $level "config.user.get non-existent host returns error" \
  private.ossh.config.user.get nonExistentHost
if [ "$RETURN_VALUE" -ne 0 ]; then
  expect.pass "non-zero for missing host"
else
  expect.fail "should return non-zero for non-existent host"
fi

# --- config.<field>.get: missing field in block → error ---

test.case $level "config.proxyJump.get returns error when field absent" \
  private.ossh.config.proxyJump.get alpha.example
if [ "$RETURN_VALUE" -ne 0 ]; then
  expect.pass "non-zero for missing ProxyJump"
else
  expect.fail "should return non-zero for absent field"
fi

# --- config.<field>.set: replace existing field ---

test.case $level "config.port.set replaces Port in alpha" \
  private.ossh.config.port.set alpha.example 443
if [ "$RETURN_VALUE" -eq 0 ]; then
  expect.pass "config.port.set returned 0"
else
  expect.fail "should return 0, got: $RETURN_VALUE"
fi

GOT=$(private.ossh.config.port.get alpha.example 2>/dev/null)
test.case $level "config.port.get confirms Port changed to 443" echo "$GOT"
if [ "$GOT" = "443" ]; then
  expect.pass "Port=443 after set"
else
  expect.fail "expected '443', got: '$GOT'"
fi

# --- config.<field>.set: isolation — other Host block untouched ---

GOT=$(private.ossh.config.port.get beta.example 2>/dev/null)
test.case $level "isolation: beta Port unchanged after alpha set" echo "$GOT"
if [ "$GOT" = "2222" ]; then
  expect.pass "beta Port still 2222"
else
  expect.fail "beta Port should be 2222, got: '$GOT'"
fi

GOT=$(private.ossh.config.user.get beta.example 2>/dev/null)
test.case $level "isolation: beta User unchanged after alpha set" echo "$GOT"
if [ "$GOT" = "bob" ]; then
  expect.pass "beta User still bob"
else
  expect.fail "beta User should be 'bob', got: '$GOT'"
fi

# --- config.<field>.set: replace User ---

test.case $level "config.user.set replaces User in beta" \
  private.ossh.config.user.set beta.example deploy
GOT=$(private.ossh.config.user.get beta.example 2>/dev/null)
test.case $level "config.user.get confirms User changed" echo "$GOT"
if [ "$GOT" = "deploy" ]; then
  expect.pass "User=deploy after set"
else
  expect.fail "expected 'deploy', got: '$GOT'"
fi

# --- config.<field>.set: add new field ---

test.case $level "config.proxyJump.set adds ProxyJump to alpha" \
  private.ossh.config.proxyJump.set alpha.example bastion
if [ "$RETURN_VALUE" -eq 0 ]; then
  expect.pass "returned 0 for new field"
else
  expect.fail "should return 0, got: $RETURN_VALUE"
fi

GOT=$(private.ossh.config.proxyJump.get alpha.example 2>/dev/null)
test.case $level "config.proxyJump.get confirms ProxyJump added" echo "$GOT"
if [ "$GOT" = "bastion" ]; then
  expect.pass "ProxyJump=bastion"
else
  expect.fail "expected 'bastion', got: '$GOT'"
fi

# --- config.<field>.set: forwardAgent yes/no ---

test.case $level "config.forwardAgent.set adds ForwardAgent" \
  private.ossh.config.forwardAgent.set alpha.example yes
if [ "$RETURN_VALUE" -eq 0 ]; then
  expect.pass "returned 0"
else
  expect.fail "should return 0, got: $RETURN_VALUE"
fi

GOT=$(private.ossh.config.forwardAgent.get alpha.example 2>/dev/null)
test.case $level "config.forwardAgent.get confirms value" echo "$GOT"
if [ "$GOT" = "yes" ]; then
  expect.pass "ForwardAgent=yes"
else
  expect.fail "expected 'yes', got: '$GOT'"
fi

# --- config.<field>.set: identitiesOnly yes/no ---

test.case $level "config.identitiesOnly.set adds IdentitiesOnly" \
  private.ossh.config.identitiesOnly.set beta.example yes
if [ "$RETURN_VALUE" -eq 0 ]; then
  expect.pass "returned 0"
else
  expect.fail "should return 0, got: $RETURN_VALUE"
fi

GOT=$(private.ossh.config.identitiesOnly.get beta.example 2>/dev/null)
test.case $level "config.identitiesOnly.get confirms value" echo "$GOT"
if [ "$GOT" = "yes" ]; then
  expect.pass "IdentitiesOnly=yes"
else
  expect.fail "expected 'yes', got: '$GOT'"
fi

# --- config.<field>.set: non-existent host → error ---

test.case $level "config.port.set non-existent host returns error" \
  private.ossh.config.port.set nonExistentHost 22
if [ "$RETURN_VALUE" -ne 0 ]; then
  expect.pass "non-zero for missing host"
else
  expect.fail "should return non-zero"
fi

# --- config.<field>.set: round-trip restore ---

test.case $level "config.port.set round-trip: restore Port to 22" \
  private.ossh.config.port.set alpha.example 22
GOT=$(private.ossh.config.port.get alpha.example 2>/dev/null)
test.case $level "config.port.get confirms Port restored to 22" echo "$GOT"
if [ "$GOT" = "22" ]; then
  expect.pass "Port=22 after restore"
else
  expect.fail "expected '22', got: '$GOT'"
fi

# --- config.<field>.set: replace HostName ---

test.case $level "config.hostName.set replaces HostName" \
  private.ossh.config.hostName.set alpha.example newhost.example.com
GOT=$(private.ossh.config.hostName.get alpha.example 2>/dev/null)
test.case $level "config.hostName.get confirms change" echo "$GOT"
if [ "$GOT" = "newhost.example.com" ]; then
  expect.pass "HostName=newhost.example.com"
else
  expect.fail "expected 'newhost.example.com', got: '$GOT'"
fi

# --- config.<field>.set: replace IdentityFile ---

test.case $level "config.identityFile.set replaces IdentityFile" \
  private.ossh.config.identityFile.set alpha.example /root/.ssh/ids/newkey/id_ed25519
GOT=$(private.ossh.config.identityFile.get alpha.example 2>/dev/null)
test.case $level "config.identityFile.get confirms change" echo "$GOT"
if [ "$GOT" = "/root/.ssh/ids/newkey/id_ed25519" ]; then
  expect.pass "IdentityFile updated"
else
  expect.fail "expected '/root/.ssh/ids/newkey/id_ed25519', got: '$GOT'"
fi

# --- Tab completion: per-field stubs exist ---

for field in identityFile user port hostName proxyJump forwardAgent identitiesOnly; do
  if grep -q "private.ossh.config.${field}.set.completion" "$OOSH_DIR/ossh" 2>/dev/null; then
    expect.pass "config.${field}.set completion exists"
  else
    expect.fail "config.${field}.set completion missing"
  fi
  if grep -q "private.ossh.config.${field}.get.completion" "$OOSH_DIR/ossh" 2>/dev/null; then
    expect.pass "config.${field}.get completion exists"
  else
    expect.fail "config.${field}.get completion missing"
  fi
done

# ============================================================================
# Cleanup per-field config tests
# ============================================================================

export CURRENT_SSH_DIR="$ORIGINAL_CURRENT_SSH_DIR"
rm -f "$TEST_CONFIG_FILE" 2>/dev/null
rm -rf "$TEST_SSH_DIR" 2>/dev/null

# ============================================================================
# REGRESSION: ossh login/exec/push completion must not reference deleted functions
# Bug: per-field refactor deleted ossh.config.get.completion but 14 callers
# still referenced it, breaking Tab completion for login, exec, push, etc.
# ============================================================================

# ossh.login.completion must exist and return host entries
if this.functionExists ossh.login.completion; then
  expect.pass "ossh.login.completion function exists"
else
  expect.fail "ossh.login.completion function missing"
fi

# ossh.config.get.completion must exist (provides host completion for config.get)
if grep -q 'ossh\.config\.get\.completion' "$OOSH_DIR/ossh" 2>/dev/null; then
  expect.pass "ossh.config.get.completion exists"
else
  expect.fail "ossh.config.get.completion missing"
fi

# ossh.login.completion must actually produce output (host list)
GOT=$(ossh.login.completion 2>/dev/null)
test.case $level "ossh.login.completion returns host entries" echo "$GOT"
if [ -n "$GOT" ]; then
  expect.pass "login completion returns hosts"
else
  expect.fail "login completion returned empty — Tab broken"
fi

# ossh.exec.completion must also work (same pattern)
if this.functionExists ossh.exec.completion; then
  GOT=$(ossh.exec.completion 2>/dev/null)
  if [ -n "$GOT" ]; then
    expect.pass "ossh.exec.completion returns hosts"
  else
    expect.fail "ossh.exec.completion returned empty"
  fi
else
  expect.fail "ossh.exec.completion function missing"
fi

# ============================================================================
# REGRESSION: ossh.config.get — lost from prod, must be restored
# Used by ossh.config.parse (line 782) which breaks without it
# ============================================================================

source ossh

# --- config.get function exists ---
test.case $level "ossh.config.get function exists" \
  type -t ossh.config.get
if type -t ossh.config.get &>/dev/null; then
  expect.pass "ossh.config.get is defined"
else
  expect.fail "ossh.config.get missing — restore from prod"
fi

# --- config.get completion exists ---
test.case $level "ossh.config.get.completion exists" \
  type -t ossh.config.get.completion
if type -t ossh.config.get.completion &>/dev/null; then
  expect.pass "ossh.config.get.completion is defined"
else
  expect.fail "ossh.config.get.completion missing"
fi

# --- config.get returns Host block from SSH config ---
# Create temp SSH config with known content
CG_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/__test_config_get_$$.XXXXXX")
cat > "$CG_TMPDIR/config" <<CGEOF
Host test-server-$$
  HostName 10.20.30.40
  User testuser
  Port 2222

Host other-server
  HostName 1.2.3.4
CGEOF

test.case $level "ossh.config.get returns host block" \
  echo "testing with mock config"
if type -t ossh.config.get &>/dev/null; then
  CG_OUTPUT=$(ossh.config.get "test-server-$$" "$CG_TMPDIR/config" 2>/dev/null)
  if echo "$CG_OUTPUT" | grep -q "HostName 10.20.30.40"; then
    expect.pass "config.get returns correct Host block"
  else
    expect.fail "config.get should return Host block with HostName; got: $CG_OUTPUT"
  fi
else
  expect.fail "skipped — config.get not defined"
fi

# --- config.get returns error for unknown host ---
test.case $level "ossh.config.get returns error for unknown host" \
  echo "testing unknown host"
if type -t ossh.config.get &>/dev/null; then
  ossh.config.get "nonexistent-host-$$" "$CG_TMPDIR/config" 2>/dev/null
  CG_RC=$?
  if [ "$CG_RC" -ne 0 ]; then
    expect.pass "config.get returns non-zero for unknown host (rc=$CG_RC)"
  else
    expect.fail "config.get should return non-zero for unknown host"
  fi
else
  expect.fail "skipped — config.get not defined"
fi

rm -rf "$CG_TMPDIR"

echo ""
echo "=== ossh.config.get regression tests complete ==="
echo ""

# ============================================================================
# REGRESSION: ossh.config.set — public wrapper for per-field private helpers
# Private helpers have typed completions per field (OOP pattern)
# Public config.set <configItem> <field> <value> delegates to them
# ============================================================================

# --- ossh.config.set function exists ---
test.case $level "ossh.config.set function exists" \
  type -t ossh.config.set
if type -t ossh.config.set &>/dev/null; then
  expect.pass "ossh.config.set is defined"
else
  expect.fail "ossh.config.set missing — needs public wrapper"
fi

# --- config.set completion for field lists known fields ---
test.case $level "ossh.config.set.completion.field lists SSH fields" \
  echo "checking field completion"
if type -t ossh.config.set.completion.field &>/dev/null; then
  FIELD_LIST=$(ossh.config.set.completion.field 2>/dev/null)
  if echo "$FIELD_LIST" | grep -q "user" && echo "$FIELD_LIST" | grep -q "port"; then
    expect.pass "field completion includes user and port"
  else
    expect.fail "field completion should list SSH fields, got: $FIELD_LIST"
  fi
else
  expect.fail "ossh.config.set.completion.field missing"
fi

# --- config.set writes value to SSH config ---
CS_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/__test_config_set_$$.XXXXXX")
cat > "$CS_TMPDIR/config" <<CSEOF
Host test-set-$$
  HostName 10.20.30.40
  User olduser
  Port 22

Host other-server
  HostName 1.2.3.4
CSEOF

test.case $level "ossh.config.set updates field value" echo "testing"
if type -t ossh.config.set &>/dev/null; then
  # Override sshDir so config.set finds our mock config
  ORIG_CURRENT_SSH_DIR="${CURRENT_SSH_DIR:-}"
  export CURRENT_SSH_DIR="$CS_TMPDIR"
  ossh.config.set "test-set-$$" "user" "newuser" 2>/dev/null
  CS_RC=$?
  export CURRENT_SSH_DIR="$ORIG_CURRENT_SSH_DIR"
  CS_AFTER=$(grep "User\|user" "$CS_TMPDIR/config" 2>/dev/null)
  if echo "$CS_AFTER" | grep -qi "newuser"; then
    expect.pass "config.set changed user to newuser"
  else
    expect.fail "config.set should update user; file has: $CS_AFTER (rc=$CS_RC)"
  fi
else
  expect.fail "skipped — config.set not defined"
fi

# --- private per-field helpers exist (backing the public method) ---
PRIVATE_HELPERS=$(grep -c '^private\.ossh\.config\.field\.\(get\|set\)' "$OOSH_DIR/ossh" | tr -d ' ')
test.case $level "private per-field helpers exist" echo "count=$PRIVATE_HELPERS"
if [ "$PRIVATE_HELPERS" -ge 2 ]; then
  expect.pass "$PRIVATE_HELPERS private field helpers (get+set)"
else
  expect.fail "expected private.ossh.config.field.get and .set"
fi

rm -rf "$CS_TMPDIR"

echo ""
echo "=== ossh.config.set tests complete ==="
echo ""

# ============================================================================
# REGRESSION: object.verb naming consistency
# Old verb.object (push.config, pull.config) → new object.verb (config.push, config.pull)
# Old methods kept as deprecated wrappers for backward compat
# ============================================================================

OSSH_SRC="$OOSH_DIR/ossh"

# --- New object.verb methods exist ---
for method in config.push config.pull dir.push dir.pull key.push key.pull id.push id.pull; do
  test.case $level "ossh.$method function exists" \
    type -t "ossh.$method"
  if type -t "ossh.$method" &>/dev/null; then
    expect.pass "ossh.$method exists"
  else
    expect.fail "ossh.$method should be defined"
  fi
done

# --- Deprecated verb.object wrappers still exist (backward compat) ---
for method in push.config pull.config push.dir pull.dir push.key pull.key push.id pull.id; do
  test.case $level "deprecated ossh.$method still exists" \
    type -t "ossh.$method"
  if type -t "ossh.$method" &>/dev/null; then
    expect.pass "ossh.$method exists (deprecated)"
  else
    expect.fail "ossh.$method should still exist as deprecated wrapper"
  fi
done

# --- Deprecated get.config wrapper exists (private — hidden from c2 to prevent sub-method pollution) ---
test.case $level "deprecated private.ossh.get.config exists" \
  type -t private.ossh.get.config
if type -t private.ossh.get.config &>/dev/null; then
  expect.pass "private.ossh.get.config exists (deprecated wrapper)"
else
  expect.fail "private.ossh.get.config should exist as deprecated wrapper for config.get"
fi

# --- Internal callers use new names, not old ---
# Grep for internal calls (not function definitions, not comments, not deprecated wrappers)
INTERNAL_OLD=$(grep -n 'ossh\.push\.\|ossh\.pull\.\|ossh\.get\.config\|ossh push\.\|ossh pull\.\|ossh get\.config' "$OSSH_SRC" | grep -v '# DEPRECATED\|^#\|() {' | grep -v 'completion()')
# Filter to only actual callers (not definitions)
CALLER_OLD=$(echo "$INTERNAL_OLD" | grep -v '() ' | grep -v '^[[:space:]]*#')
CALLER_COUNT=0
[ -n "$CALLER_OLD" ] && CALLER_COUNT=$(echo "$CALLER_OLD" | wc -l | tr -d ' ')
test.case $level "zero internal callers use old verb.object names" \
  echo "old_callers=$CALLER_COUNT"
if [ "$CALLER_COUNT" -eq 0 ]; then
  expect.pass "all internal callers migrated to object.verb"
else
  expect.fail "$CALLER_COUNT internal callers still use old names"
fi

# --- config.get stdout has NO log messages (clean output for piping) ---
NV_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/__test_naming_$$.XXXXXX")
cat > "$NV_TMPDIR/config" <<NVEOF
Host naming-test-$$
  HostName 10.0.0.99
  User testuser
NVEOF

test.case $level "config.get stdout is clean (no log messages)" \
  echo "checking stdout purity"
if type -t ossh.config.get &>/dev/null; then
  CG_STDOUT=$(ossh.config.get "naming-test-$$" "$NV_TMPDIR/config" 2>/dev/null)
  # Stdout should contain ONLY the Host block, no log prefixes
  if echo "$CG_STDOUT" | grep -qE 'ERROR>|WARNING>|INFO>|IMPORTANT>|SUCCESS>'; then
    expect.fail "config.get stdout has log messages — will corrupt config files"
  else
    expect.pass "config.get stdout is clean"
  fi
else
  expect.fail "skipped — config.get not defined"
fi
rm -rf "$NV_TMPDIR"

# --- hiveMind callers use new names ---
HIVEMIND_SRC="$OOSH_DIR/hiveMind"
HM_OLD=$(grep -n 'ossh.*push\.dir\|ossh.*pull\.dir\|ossh.*push\.config\|ossh.*pull\.config' "$HIVEMIND_SRC" | grep -v '# DEPRECATED\|^#')
HM_OLD_COUNT=0
[ -n "$HM_OLD" ] && HM_OLD_COUNT=$(echo "$HM_OLD" | wc -l | tr -d ' ')
test.case $level "hiveMind callers use new ossh names" \
  echo "old_hm_callers=$HM_OLD_COUNT"
if [ "$HM_OLD_COUNT" -eq 0 ]; then
  expect.pass "hiveMind uses object.verb ossh names"
else
  expect.fail "$HM_OLD_COUNT hiveMind callers still use old names"
fi

echo ""
echo "=== object.verb naming tests complete ==="
echo ""

# ============================================================================
# FEATURE: Generic verb dispatchers (get, set, list)
# ossh get <object> dispatches to ossh.object.get
# ossh set <object> dispatches to ossh.object.set
# ossh list shows all available objects
# ============================================================================

# --- T-DISPATCH-1: ossh.get dispatcher exists ---
test.case $level "T-DISPATCH-1: ossh.get function exists" \
  type -t ossh.get
if type -t ossh.get &>/dev/null; then
  expect.pass "ossh.get dispatcher exists"
else
  expect.fail "ossh.get dispatcher should be defined"
fi

# --- T-DISPATCH-2: ossh.set dispatcher exists ---
test.case $level "T-DISPATCH-2: ossh.set function exists" \
  type -t ossh.set
if type -t ossh.set &>/dev/null; then
  expect.pass "ossh.set dispatcher exists"
else
  expect.fail "ossh.set dispatcher should be defined"
fi

# --- T-DISPATCH-3: ossh.list dispatcher exists ---
test.case $level "T-DISPATCH-3: ossh.list function exists" \
  type -t ossh.list
if type -t ossh.list &>/dev/null; then
  expect.pass "ossh.list dispatcher exists"
else
  expect.fail "ossh.list dispatcher should be defined"
fi

# --- T-DISPATCH-4: ossh get config.port dispatches to ossh.config.port.get ---
# Object param is FULL dotted path: config.port, config.user, config.identityFile
# ossh get config.port <host> → ossh.config.port.get <host>
test.case $level "T-DISPATCH-4: ossh get config.port dispatches correctly" \
  echo "testing get dispatch with dotted object"
if type -t ossh.get &>/dev/null; then
  VD_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/__test_dispatch_$$.XXXXXX")
  cat > "$VD_TMPDIR/config" <<VDEOF
Host dispatch-test-$$
  HostName 10.0.0.42
  User dispatcher
  Port 2222
VDEOF
  # ossh get config.port should dispatch to ossh.config.port.get
  VD_OUT=$(CURRENT_SSH_DIR="$VD_TMPDIR" ossh.get config.port "dispatch-test-$$" 2>/dev/null)
  if echo "$VD_OUT" | grep -q "2222"; then
    expect.pass "ossh get config.port returned port 2222"
  else
    expect.fail "ossh get config.port should return 2222; got: $VD_OUT"
  fi
  rm -rf "$VD_TMPDIR"
else
  expect.pass "skipped — ossh.get not yet implemented"
fi

# --- T-DISPATCH-4b: ossh get config dispatches to ossh.config.get ---
test.case $level "T-DISPATCH-4b: ossh get config dispatches to config.get" \
  echo "testing get dispatch with short object"
if type -t ossh.get &>/dev/null; then
  VD_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/__test_dispatch_$$.XXXXXX")
  cat > "$VD_TMPDIR/config" <<VDEOF
Host dispatch-test-$$
  HostName 10.0.0.42
  User dispatcher
VDEOF
  VD_OUT=$(ossh.get config "dispatch-test-$$" "$VD_TMPDIR/config" 2>/dev/null)
  if echo "$VD_OUT" | grep -q "HostName 10.0.0.42"; then
    expect.pass "ossh get config dispatched correctly"
  else
    expect.fail "ossh get config should return Host block; got: $VD_OUT"
  fi
  rm -rf "$VD_TMPDIR"
else
  expect.pass "skipped — ossh.get not yet implemented"
fi

# --- T-DISPATCH-5: ossh get unknown-object returns error ---
test.case $level "T-DISPATCH-5: ossh get unknown returns error" \
  echo "testing unknown object"
if type -t ossh.get &>/dev/null; then
  ossh.get "nonexistent.object.$$" 2>/dev/null
  VD_RC=$?
  if [ "$VD_RC" -ne 0 ]; then
    expect.pass "ossh get unknown returns error (rc=$VD_RC)"
  else
    expect.fail "ossh get unknown should return non-zero"
  fi
else
  expect.pass "skipped — ossh.get not yet implemented"
fi

# --- T-DISPATCH-6: ossh get with no args returns error ---
test.case $level "T-DISPATCH-6: ossh get with no args returns error" \
  echo "testing no args"
if type -t ossh.get &>/dev/null; then
  ossh.get 2>/dev/null
  VD_RC=$?
  if [ "$VD_RC" -ne 0 ]; then
    expect.pass "ossh get with no args returns error (rc=$VD_RC)"
  else
    expect.fail "ossh get with no args should return non-zero"
  fi
else
  expect.pass "skipped — ossh.get not yet implemented"
fi

# --- T-DISPATCH-7: ossh list shows full dotted object paths ---
test.case $level "T-DISPATCH-7: ossh list shows dotted object paths" \
  echo "testing list"
if type -t ossh.list &>/dev/null; then
  VD_LIST=$(ossh.list 2>/dev/null)
  # Must show full dotted paths like config.identityFile, config.user, config.port
  if echo "$VD_LIST" | grep -q "config\.identityFile" && \
     echo "$VD_LIST" | grep -q "config\.user" && \
     echo "$VD_LIST" | grep -q "config\.port"; then
    expect.pass "ossh list shows dotted paths: config.identityFile, config.user, config.port"
  else
    expect.fail "ossh list should show full dotted paths; got: $VD_LIST"
  fi
else
  expect.pass "skipped — ossh.list not yet implemented"
fi

# --- T-DISPATCH-8: ossh list with filter narrows to matching paths ---
test.case $level "T-DISPATCH-8: ossh list with filter" \
  echo "testing list filter"
if type -t ossh.list &>/dev/null; then
  VD_FILTERED=$(ossh.list "port" 2>/dev/null)
  VD_ALL=$(ossh.list 2>/dev/null)
  FILTERED_COUNT=$(echo "$VD_FILTERED" | grep -c . | tr -d ' ')
  ALL_COUNT=$(echo "$VD_ALL" | grep -c . | tr -d ' ')
  if [ "$FILTERED_COUNT" -lt "$ALL_COUNT" ] && [ "$FILTERED_COUNT" -gt 0 ]; then
    if echo "$VD_FILTERED" | grep -q "port"; then
      expect.pass "list filter matches 'port' ($FILTERED_COUNT of $ALL_COUNT)"
    else
      expect.fail "filter 'port' should match port objects; got: $VD_FILTERED"
    fi
  else
    expect.fail "filter should narrow; filtered=$FILTERED_COUNT all=$ALL_COUNT"
  fi
else
  expect.pass "skipped — ossh.list not yet implemented"
fi

# --- T-DISPATCH-9: get completion returns full dotted object paths ---
test.case $level "T-DISPATCH-9: get completion returns dotted paths" \
  type -t ossh.get.completion.object
if type -t ossh.get.completion.object &>/dev/null; then
  VD_COMP=$(ossh.get.completion.object 2>/dev/null)
  if echo "$VD_COMP" | grep -q "config\.port"; then
    expect.pass "get completion includes 'config.port'"
  else
    expect.fail "get completion should include 'config.port'; got: $VD_COMP"
  fi
else
  expect.fail "ossh.get.completion.object should be defined"
fi

# --- T-DISPATCH-10: set completion returns full dotted object paths ---
test.case $level "T-DISPATCH-10: set completion returns dotted paths" \
  type -t ossh.set.completion.object
if type -t ossh.set.completion.object &>/dev/null; then
  VD_COMP=$(ossh.set.completion.object 2>/dev/null)
  if echo "$VD_COMP" | grep -q "config\."; then
    expect.pass "set completion returns dotted paths"
  else
    expect.fail "set completion should return objects"
  fi
else
  expect.fail "ossh.set.completion.object should be defined"
fi

# --- T-DISPATCH-11: get completion shows valid get-able objects ---
# config IS a valid object (ossh.config.get exists as per-field dispatcher).
# Per-field objects like config.port, config.user also appear — both are correct.
test.case $level "T-DISPATCH-11: get completion shows get-able objects" \
  echo "checking get completion objects include config.port"
if type -t ossh.get.completion.object &>/dev/null; then
  VD_COMP=$(ossh.get.completion.object 2>/dev/null)
  # Must show per-field dotted paths (config.port, config.user, etc.)
  if echo "$VD_COMP" | grep -q "config\.port"; then
    expect.pass "completion shows per-field objects (config.port etc.)"
  else
    if echo "$VD_COMP" | grep -q "config"; then
      expect.pass "completion shows config object"
    else
      expect.fail "completion should show config objects; got: $VD_COMP"
    fi
  fi
else
  expect.fail "skipped — ossh.get.completion.object not defined"
fi

# --- T-DISPATCH-12: deprecated wrappers still callable ---
test.case $level "T-DISPATCH-12: deprecated get.config still works" \
  echo "testing backward compat"
if type -t ossh.get.config &>/dev/null; then
  VD_TMPDIR=$(mktemp -d "${TMPDIR:-/tmp}/__test_compat_$$.XXXXXX")
  cat > "$VD_TMPDIR/config" <<VCEOF
Host compat-test-$$
  HostName 10.0.0.88
VCEOF
  VD_OUT=$(ossh.get.config "compat-test-$$" "$VD_TMPDIR/config" 2>/dev/null)
  if echo "$VD_OUT" | grep -q "10.0.0.88"; then
    expect.pass "deprecated get.config still works"
  else
    expect.fail "deprecated get.config should still return config block"
  fi
  rm -rf "$VD_TMPDIR"
else
  expect.pass "get.config removed (acceptable if backward compat not needed)"
fi

echo ""
echo "=== verb dispatcher tests complete ==="
echo ""

# ============================================================================
# REGRESSION: File/directory parameter completions
# Every method with <file>, <dir>, <path>, <sshDir> params must have
# a completion function that returns filesystem paths
# ============================================================================

OSSH_SRC="$OOSH_DIR/ossh"

# Methods that take dir parameters should have dir completion
for method_param in "dir.push:dir" "dir.pull:dir" "key.push:sshDir" "key.pull:dir" "id.pull:sshDir" "id.create:sshDir"; do
  method="${method_param%%:*}"
  param="${method_param##*:}"
  test.case $level "T-COMP-FILE: ossh.$method has $param completion" \
    echo "checking ossh.${method}.completion.${param}"
  if type -t "ossh.${method}.completion.${param}" &>/dev/null; then
    COMP_OUT=$(ossh.${method}.completion.${param} 2>/dev/null)
    expect.pass "ossh.${method}.completion.${param} exists"
  else
    expect.fail "ossh.${method}.completion.${param} missing — user can't Tab-complete ${param}"
  fi
done

# otmux file parameters
source otmux
for method_param in "buffer.load:file" "buffer.save:file"; do
  method="${method_param%%:*}"
  param="${method_param##*:}"
  test.case $level "T-COMP-FILE: otmux.$method has $param completion" \
    echo "checking otmux.${method}.completion.${param}"
  if type -t "otmux.${method}.completion.${param}" &>/dev/null; then
    expect.pass "otmux.${method}.completion.${param} exists"
  else
    expect.fail "otmux.${method}.completion.${param} missing"
  fi
done

# path dir parameters
source path
for method in "add" "push" "rm" "append" "prepend" "remove"; do
  test.case $level "T-COMP-FILE: path.$method has dir completion" \
    echo "checking path.${method}.completion.dir"
  if type -t "path.${method}.completion.dir" &>/dev/null; then
    expect.pass "path.${method}.completion.dir exists"
  else
    expect.fail "path.${method}.completion.dir missing"
  fi
done

# Shared parameter completions return actual filesystem entries
test.case $level "T-COMP-FILE: ossh.parameter.completion.file returns files" \
  echo "checking file completion output"
if type -t ossh.parameter.completion.file &>/dev/null; then
  FILE_COMP=$(ossh.parameter.completion.file 2>/dev/null)
  if [ -n "$FILE_COMP" ]; then
    expect.pass "file completion returns entries"
  else
    expect.fail "file completion returned empty"
  fi
else
  expect.fail "ossh.parameter.completion.file missing"
fi

test.case $level "T-COMP-FILE: ossh.parameter.completion.dir returns directories" \
  echo "checking dir completion output"
if type -t ossh.parameter.completion.dir &>/dev/null; then
  DIR_COMP=$(ossh.parameter.completion.dir 2>/dev/null)
  if [ -n "$DIR_COMP" ]; then
    expect.pass "dir completion returns entries"
  else
    expect.fail "dir completion returned empty"
  fi
else
  expect.fail "ossh.parameter.completion.dir missing"
fi

echo ""
echo "=== file/directory completion tests complete ==="
echo ""

# ============================================================================
# Test Summary
# ============================================================================

test.suite.save.results
