#!/usr/bin/env bash
# Test suite for odocker - OOSH Docker wrapper

source test.suite $*

level=1

# ─────────────────────────────────────────────────────────────────────────────
# Test: odocker.ps runs without error
# ─────────────────────────────────────────────────────────────────────────────
test.case $level "odocker.ps lists containers" \
  odocker ps
if [ "$RETURN_VALUE" -eq 0 ]; then
  expect.pass "odocker ps exits 0"
else
  expect.fail "odocker ps exits $RETURN_VALUE (expected 0)"
fi

# ─────────────────────────────────────────────────────────────────────────────
# Test: odocker.list runs without error
# ─────────────────────────────────────────────────────────────────────────────
test.case $level "odocker.list lists images" \
  odocker list
if [ "$RETURN_VALUE" -eq 0 ]; then
  expect.pass "odocker list exits 0"
else
  expect.fail "odocker list exits $RETURN_VALUE (expected 0)"
fi

# ─────────────────────────────────────────────────────────────────────────────
# Test: methods require parameters (error handling)
# ─────────────────────────────────────────────────────────────────────────────
for method in build exec stop log rm rmi; do
  test.case $level "odocker.$method requires parameter" \
    odocker $method
  if [ "$RETURN_VALUE" -eq 1 ]; then
    expect.pass "odocker $method without args exits 1"
  else
    expect.fail "odocker $method without args exits $RETURN_VALUE (expected 1)"
  fi
done

# ─────────────────────────────────────────────────────────────────────────────
# Test: workspace completion returns results
# ─────────────────────────────────────────────────────────────────────────────
source $OOSH_DIR/odocker 2>/dev/null
RESULT=$(private.odocker.workspaces)
if [ -n "$RESULT" ]; then
  expect.pass "workspace completion returns results"
else
  expect.fail "workspace completion returned empty"
fi

# ─────────────────────────────────────────────────────────────────────────────
# Test: nakedUbuntu/20.04.sshd is in workspace list
# ─────────────────────────────────────────────────────────────────────────────
if echo "$RESULT" | grep -q "nakedUbuntu/20.04.sshd"; then
  expect.pass "nakedUbuntu/20.04.sshd found in workspaces"
else
  expect.fail "nakedUbuntu/20.04.sshd not found in workspaces"
fi

# ─────────────────────────────────────────────────────────────────────────────
# Test: image name derivation
# ─────────────────────────────────────────────────────────────────────────────
RESULT=$(private.odocker.image.from.workspace "nakedUbuntu/20.04.sshd")
if [ "$RESULT" = "naked_ubuntu_20_04_sshd" ]; then
  expect.pass "image name derived correctly: $RESULT"
else
  expect.fail "expected naked_ubuntu_20_04_sshd, got: $RESULT"
fi

# ─────────────────────────────────────────────────────────────────────────────
# Test: running container completion
# ─────────────────────────────────────────────────────────────────────────────
RESULT=$(private.odocker.running.containers)
if [ -n "$RESULT" ]; then
  expect.pass "running containers completion returns results: $RESULT"
else
  expect.pass "running containers completion returns empty (no containers or docker not running)"
fi

# ─────────────────────────────────────────────────────────────────────────────
# KNOWN BUG: Dotted method dispatch doubling on error paths
#
# When a dotted method (file.find, run.sshd) returns exit code 1,
# this.start dispatches it twice. The second invocation writes to the
# original file descriptors, bypassing any pipe/redirect. This means:
#   - Visible in terminal (doubled output to user)
#   - NOT capturable via pipe, redirect, tee, expect, or script
#   - Automated tests below verify pipe-captured output (always 1 copy)
#   - Manual verification required for terminal doubling
#
# Single-word methods (ps, build, list) are NOT affected.
# Success paths (exit 0) are NOT affected.
#
# Root cause: this.start retries dispatch on non-zero return, using
# saved file descriptors that bypass the caller's redirections.
#
# Ticket: oosh-expert/this-expert — fix dispatch retry on error paths
# ─────────────────────────────────────────────────────────────────────────────
_DUPTEST="/tmp/odocker_dup_test.$$"

# T-DUP-1: Dotted method success — exits 0 and sets RESULT
test.case $level "dotted method (file.find) success: exits 0" \
  odocker file.find fervent_ritchie
if [ "$RETURN_VALUE" -eq 0 ]; then
  expect.pass "file.find fervent_ritchie exits 0"
else
  expect.fail "file.find fervent_ritchie exits $RETURN_VALUE (expected 0)"
fi

# T-DUP-2: Dotted method error — single output in pipe (doubles in terminal)
odocker file.find nonexistent_thing >"$_DUPTEST" 2>&1 || true
output_count=$(grep -c "Not found" "$_DUPTEST" 2>/dev/null); output_count=${output_count:-0}
if [ "$output_count" -eq 1 ]; then
  expect.pass "file.find error: 'Not found' captured once (terminal doubles — known bug)"
else
  expect.fail "file.find error: 'Not found' captured $output_count times"
fi

# T-DUP-3: Single-word method error — should also be single in pipe
odocker build >"$_DUPTEST" 2>&1 || true
output_count=$(grep -c "Usage:" "$_DUPTEST" 2>/dev/null); output_count=${output_count:-0}
if [ "$output_count" -eq 1 ]; then
  expect.pass "build error: 'Usage:' captured once (single-word method OK)"
else
  expect.fail "build error: 'Usage:' captured $output_count times (expected 1)"
fi

# T-DUP-4: Single-word method success — control test
odocker ps >"$_DUPTEST" 2>&1 || true
output_count=$(grep -c "NAMES" "$_DUPTEST" 2>/dev/null); output_count=${output_count:-0}
if [ "$output_count" -eq 1 ]; then
  expect.pass "ps success: 'NAMES' captured once (single-word method OK)"
else
  expect.fail "ps success: 'NAMES' captured $output_count times (expected 1)"
fi

rm -f "$_DUPTEST"

# ============================================================================
# odocker install: inside-docker vs host detection
# ============================================================================

ODOCKER_SRC="$OOSH_DIR/odocker"

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

# --- T-INSTALL-2: isInsideDocker detection uses .dockerenv and cgroup ---
test.case $level "T-INSTALL-2: inside-docker detection checks .dockerenv" \
  echo "checking detection code"
DETECT_BODY=$(sed -n '/private\.odocker\.isInsideDocker/,/^}/p' "$ODOCKER_SRC")
if echo "$DETECT_BODY" | grep -q '\.dockerenv'; then
  if echo "$DETECT_BODY" | grep -q 'cgroup'; then
    expect.pass "detection checks /.dockerenv AND /proc/1/cgroup"
  else
    expect.fail "detection should also check /proc/1/cgroup"
  fi
else
  expect.fail "detection should check /.dockerenv"
fi

# --- T-INSTALL-3: install uses isInsideDocker to branch ---
test.case $level "T-INSTALL-3: install branches on isInsideDocker" \
  echo "checking install code"
INSTALL_BODY=$(sed -n '/^odocker\.install()/,/^}/p' "$ODOCKER_SRC")
if echo "$INSTALL_BODY" | grep -q 'isInsideDocker'; then
  expect.pass "install calls isInsideDocker for environment detection"
else
  expect.fail "install should use isInsideDocker to choose CLI vs engine"
fi

# --- T-INSTALL-4: inside path installs CLI only (no engine) ---
test.case $level "T-INSTALL-4: inside-docker path installs CLI only" \
  echo "checking CLI-only path"
# Inside path should NOT contain get.docker.com (that installs engine)
INSIDE_SECTION=$(echo "$INSTALL_BODY" | sed -n '/isInsideDocker/,/else/p')
if echo "$INSIDE_SECTION" | grep -q 'get.docker.com'; then
  expect.fail "inside-docker path should NOT install engine (get.docker.com)"
else
  if echo "$INSIDE_SECTION" | grep -qE 'docker.io\|docker-ce-cli\|docker-cli'; then
    expect.pass "inside-docker installs CLI package only"
  else
    expect.fail "inside-docker should install docker.io or docker-ce-cli"
  fi
fi

# --- T-INSTALL-5: host path installs full engine ---
test.case $level "T-INSTALL-5: host path installs full engine" \
  echo "checking engine path"
HOST_SECTION=$(echo "$INSTALL_BODY" | sed -n '/else/,/^}/p')
if echo "$HOST_SECTION" | grep -q 'get.docker.com'; then
  expect.pass "host path uses get.docker.com for full engine"
else
  expect.fail "host path should use get.docker.com for engine install"
fi

# --- T-INSTALL-6: completion lists running containers ---
test.case $level "T-INSTALL-6: install.completion.container exists" \
  type -t odocker.install.completion.container
if type -t odocker.install.completion.container &>/dev/null; then
  expect.pass "odocker.install.completion.container exists"
else
  expect.fail "odocker.install.completion.container should be defined"
fi

# --- T-INSTALL-7: isInsideDocker returns correct result for THIS machine ---
test.case $level "T-INSTALL-7: isInsideDocker correct for this machine" \
  echo "checking live detection"
if type -t private.odocker.isInsideDocker &>/dev/null; then
  if [ -f /.dockerenv ]; then
    # We ARE inside Docker
    if private.odocker.isInsideDocker; then
      expect.pass "correctly detected: inside Docker"
    else
      expect.fail "should detect inside Docker (/.dockerenv exists)"
    fi
  else
    # We are NOT inside Docker
    if private.odocker.isInsideDocker; then
      expect.fail "should detect NOT inside Docker (no /.dockerenv)"
    else
      expect.pass "correctly detected: not inside Docker"
    fi
  fi
else
  expect.fail "private.odocker.isInsideDocker not defined"
fi

echo ""
echo "=== odocker install tests complete ==="
echo ""

### test.method
