#!/usr/bin/env bash
#
# Unit tests for resolve-jvm-opts.sh password resolution
#
# Usage: bash test_resolve_jvm_opts.sh
#

set -euo pipefail

# Colors for output
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m' # No Color

TESTS_PASSED=0
TESTS_FAILED=0
TESTS_TOTAL=0

# Setup test directory
TEST_DIR="/tmp/resolve-jvm-opts-test-$$"
mkdir -p "$TEST_DIR"

# Source the script under test
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
source "$SCRIPT_DIR/resolve-jvm-opts.sh"

# Test helper functions
pass() {
  echo -e "${GREEN}✓ PASS${NC}: $1"
  TESTS_PASSED=$((TESTS_PASSED + 1))
  TESTS_TOTAL=$((TESTS_TOTAL + 1))
  return 0
}

fail() {
  echo -e "${RED}✗ FAIL${NC}: $1"
  echo -e "  ${RED}Expected:${NC} $2"
  echo -e "  ${RED}Got:${NC} $3"
  TESTS_FAILED=$((TESTS_FAILED + 1))
  TESTS_TOTAL=$((TESTS_TOTAL + 1))
  return 0
}

cleanup() {
  rm -rf "$TEST_DIR"
}

trap cleanup EXIT

# ============================================================================
# TEST SUITE: Success Cases
# ============================================================================

test_resolve_single_password() {
  local test_name="Resolve single JVM password reference"

  # Setup
  local pwd_file="$TEST_DIR/jksPassword.txt"
  echo "jksPassword=mySecret123" > "$pwd_file"

  # Test
  local jvm_opts="-Djavax.net.ssl.keyStorePassword=\${file:${pwd_file}:jksPassword}"
  local result
  result=$(resolve_jvm_file_refs "$jvm_opts" 2>&1 | tail -1)

  # Assert
  if [[ "$result" == "-Djavax.net.ssl.keyStorePassword=mySecret123" ]]; then
    pass "$test_name"
  else
    fail "$test_name" "-Djavax.net.ssl.keyStorePassword=mySecret123" "$result"
  fi
}

test_resolve_multiple_passwords() {
  local test_name="Resolve multiple JVM password references"

  # Setup
  local pwd_file="$TEST_DIR/jksPassword.txt"
  echo "jksPassword=testpass456" > "$pwd_file"

  # Test
  local jvm_opts="-Djavax.net.ssl.keyStorePassword=\${file:${pwd_file}:jksPassword} -Djavax.net.ssl.trustStorePassword=\${file:${pwd_file}:jksPassword}"
  local result
  result=$(resolve_jvm_file_refs "$jvm_opts" 2>&1 | tail -1)

  # Assert
  if [[ "$result" == *"keyStorePassword=testpass456"* ]] && \
     [[ "$result" == *"trustStorePassword=testpass456"* ]]; then
    pass "$test_name"
  else
    fail "$test_name" "Both passwords resolved to testpass456" "$result"
  fi
}

test_resolve_password_with_special_chars() {
  local test_name="JVM password with special characters"

  # Setup
  local pwd_file="$TEST_DIR/jksPassword.txt"
  echo "jksPassword=P@ssw0rd!#\$%^&*()_+-={}[]|:;<>,.?/~\`" > "$pwd_file"

  # Test
  local jvm_opts="-Djavax.net.ssl.keyStorePassword=\${file:${pwd_file}:jksPassword}"
  local result
  result=$(resolve_jvm_file_refs "$jvm_opts" 2>&1 | tail -1)

  # Assert
  if [[ "$result" == "-Djavax.net.ssl.keyStorePassword=P@ssw0rd!#\$%^&*()_+-={}[]|:;<>,.?/~\`" ]]; then
    pass "$test_name"
  else
    fail "$test_name" "Password with special chars" "$result"
  fi
}

test_resolve_in_full_jvm_config() {
  local test_name="Password resolution in full JVM config string"

  # Setup
  local pwd_file="$TEST_DIR/jksPassword.txt"
  echo "jksPassword=actualpass789" > "$pwd_file"

  # Test
  local jvm_opts="-Xms1G -Xmx2G -Djavax.net.ssl.keyStore=/path/keystore.jks -Djavax.net.ssl.keyStorePassword=\${file:${pwd_file}:jksPassword} -Djavax.net.ssl.trustStore=/path/truststore.jks -Djavax.net.ssl.trustStorePassword=\${file:${pwd_file}:jksPassword}"
  local result
  result=$(resolve_jvm_file_refs "$jvm_opts" 2>&1 | tail -1)

  # Assert
  if [[ "$result" == *"keyStorePassword=actualpass789"* ]] && \
     [[ "$result" == *"trustStorePassword=actualpass789"* ]] && \
     [[ "$result" == *"-Xms1G"* ]] && \
     [[ "$result" == *"-Xmx2G"* ]]; then
    pass "$test_name"
  else
    fail "$test_name" "Full JVM config with resolved passwords" "$result"
  fi
}

test_no_resolution_when_no_file_reference() {
  local test_name="No resolution when no file reference present"

  # Test - JVM opts without ${file:...} syntax
  local jvm_opts="-Djavax.net.ssl.keyStorePassword=plainpassword -Xms1G"
  local result
  result=$(resolve_jvm_file_refs "$jvm_opts" 2>&1 | tail -1)

  # Assert - should return unchanged
  if [[ "$result" == "-Djavax.net.ssl.keyStorePassword=plainpassword -Xms1G" ]]; then
    pass "$test_name"
  else
    fail "$test_name" "Unchanged JVM opts" "$result"
  fi
}

test_resolve_password_strips_newlines() {
  local test_name="Strip trailing newlines from password"

  # Setup
  local pwd_file="$TEST_DIR/jksPassword.txt"
  printf "jksPassword=testpass\n\n" > "$pwd_file"

  # Test
  local jvm_opts="-Djavax.net.ssl.keyStorePassword=\${file:${pwd_file}:jksPassword}"
  local result
  result=$(resolve_jvm_file_refs "$jvm_opts" 2>&1 | tail -1)

  # Assert - should not have trailing newline
  if [[ "$result" == "-Djavax.net.ssl.keyStorePassword=testpass" ]]; then
    pass "$test_name"
  else
    fail "$test_name" "-Djavax.net.ssl.keyStorePassword=testpass (no newline)" "$result"
  fi
}

test_resolve_password_strips_carriage_returns() {
  local test_name="Strip carriage returns from password"

  # Setup
  local pwd_file="$TEST_DIR/jksPassword.txt"
  printf "jksPassword=testpass\r\n" > "$pwd_file"

  # Test
  local jvm_opts="-Djavax.net.ssl.keyStorePassword=\${file:${pwd_file}:jksPassword}"
  local result
  result=$(resolve_jvm_file_refs "$jvm_opts" 2>&1 | tail -1)

  # Assert
  if [[ "$result" == "-Djavax.net.ssl.keyStorePassword=testpass" ]]; then
    pass "$test_name"
  else
    fail "$test_name" "-Djavax.net.ssl.keyStorePassword=testpass (no CR/LF)" "$result"
  fi
}

# ============================================================================
# TEST SUITE: Error Handling - File Missing (Graceful) vs Malformed (Fail-Fast)
# ============================================================================

test_fallback_when_file_not_found() {
  local test_name="Fallback to default password when file does not exist"

  # Test
  local jvm_opts="-Djavax.net.ssl.keyStorePassword=\${file:/nonexistent/path/jksPassword.txt:jksPassword}"
  local result
  local exit_code=0
  result=$(resolve_jvm_file_refs "$jvm_opts" 2>&1) || exit_code=$?

  # Assert - should return default password with warning, not exit with error
  if [[ $exit_code -eq 0 ]] && [[ "$result" == *"WARN"* ]] && [[ "$result" == *"does not exist"* ]] && [[ "$result" == *"keyStorePassword=mystorepassword"* ]]; then
    pass "$test_name"
  else
    fail "$test_name" "Exit code 0 with WARN message and default password" "Exit code: $exit_code, Output: $result"
  fi
}

test_fallback_when_key_not_found() {
  local test_name="Error when key not found in password file (fail-fast)"

  # Setup
  local pwd_file="$TEST_DIR/jksPassword.txt"
  echo "wrongKey=password123" > "$pwd_file"

  # Test
  local jvm_opts="-Djavax.net.ssl.keyStorePassword=\${file:${pwd_file}:jksPassword}"
  local result
  local exit_code=0
  result=$(resolve_jvm_file_refs "$jvm_opts" 2>&1) || exit_code=$?

  # Assert - file exists but is malformed, should fail fast
  if [[ $exit_code -ne 0 ]] && [[ "$result" == *"ERROR"* ]] && [[ "$result" == *"not found or empty"* ]]; then
    pass "$test_name"
  else
    fail "$test_name" "Exit code 1 with ERROR message" "Exit code: $exit_code, Output: $result"
  fi
}

test_fallback_when_password_value_empty() {
  local test_name="Error when password value is empty (fail-fast)"

  # Setup
  local pwd_file="$TEST_DIR/jksPassword.txt"
  echo "jksPassword=" > "$pwd_file"

  # Test
  local jvm_opts="-Djavax.net.ssl.keyStorePassword=\${file:${pwd_file}:jksPassword}"
  local result
  local exit_code=0
  result=$(resolve_jvm_file_refs "$jvm_opts" 2>&1) || exit_code=$?

  # Assert - file exists but password is empty, should fail fast
  if [[ $exit_code -ne 0 ]] && [[ "$result" == *"ERROR"* ]] && [[ "$result" == *"not found or empty"* ]]; then
    pass "$test_name"
  else
    fail "$test_name" "Exit code 1 with ERROR message" "Exit code: $exit_code, Output: $result"
  fi
}

test_fallback_when_file_empty() {
  local test_name="Error when password file is empty (fail-fast)"

  # Setup
  local pwd_file="$TEST_DIR/jksPassword.txt"
  touch "$pwd_file"  # Create empty file

  # Test
  local jvm_opts="-Djavax.net.ssl.keyStorePassword=\${file:${pwd_file}:jksPassword}"
  local result
  local exit_code=0
  result=$(resolve_jvm_file_refs "$jvm_opts" 2>&1) || exit_code=$?

  # Assert - file exists but is empty, should fail fast
  if [[ $exit_code -ne 0 ]] && [[ "$result" == *"ERROR"* ]]; then
    pass "$test_name"
  else
    fail "$test_name" "Exit code 1 with ERROR message" "Exit code: $exit_code, Output: $result"
  fi
}

test_fallback_messages_are_helpful() {
  local test_name="Warning messages contain helpful information"

  # Test
  local pwd_file="$TEST_DIR/missing.txt"
  local jvm_opts="-Djavax.net.ssl.keyStorePassword=\${file:${pwd_file}:jksPassword}"
  local result
  result=$(resolve_jvm_file_refs "$jvm_opts" 2>&1) || true

  # Assert - warning message should mention the file path and default password fallback
  if [[ "$result" == *"${pwd_file}"* ]] && [[ "$result" == *"WARN"* ]] && [[ "$result" == *"does not exist"* ]] && [[ "$result" == *"default password"* ]]; then
    pass "$test_name"
  else
    fail "$test_name" "Warning with file path and default password fallback" "$result"
  fi
}

test_fallback_multiple_missing_files() {
  local test_name="Fallback for multiple missing password files (files don't exist)"

  # Test - both password files missing (not malformed, just missing)
  local jvm_opts="-Djavax.net.ssl.keyStorePassword=\${file:/nonexistent/keystore.txt:jksPassword} -Djavax.net.ssl.trustStorePassword=\${file:/nonexistent/truststore.txt:jksPassword}"
  local result
  local exit_code=0
  result=$(resolve_jvm_file_refs "$jvm_opts" 2>&1) || exit_code=$?

  # Assert - both should fall back to default password (files don't exist, graceful fallback)
  if [[ $exit_code -eq 0 ]] && \
     [[ "$result" == *"keyStorePassword=mystorepassword"* ]] && \
     [[ "$result" == *"trustStorePassword=mystorepassword"* ]]; then
    pass "$test_name"
  else
    fail "$test_name" "Both passwords fallback to default when files don't exist" "Exit code: $exit_code, Output: $result"
  fi
}

# ============================================================================
# TEST SUITE: Edge Cases
# ============================================================================

test_multiple_keys_in_same_file() {
  local test_name="Multiple keys in same password file"

  # Setup
  local pwd_file="$TEST_DIR/jksPassword.txt"
  cat > "$pwd_file" <<EOF
# Comments are ignored
otherKey=wrongvalue
jksPassword=correctvalue
anotherKey=alsoignored
EOF

  # Test
  local jvm_opts="-Djavax.net.ssl.keyStorePassword=\${file:${pwd_file}:jksPassword}"
  local result
  result=$(resolve_jvm_file_refs "$jvm_opts" 2>&1 | tail -1)

  # Assert - should pick the correct key
  if [[ "$result" == "-Djavax.net.ssl.keyStorePassword=correctvalue" ]]; then
    pass "$test_name"
  else
    fail "$test_name" "-Djavax.net.ssl.keyStorePassword=correctvalue" "$result"
  fi
}

test_duplicate_key_uses_first() {
  local test_name="Duplicate keys - uses first occurrence"

  # Setup
  local pwd_file="$TEST_DIR/jksPassword.txt"
  cat > "$pwd_file" <<EOF
jksPassword=firstvalue
jksPassword=secondvalue
EOF

  # Test
  local jvm_opts="-Djavax.net.ssl.keyStorePassword=\${file:${pwd_file}:jksPassword}"
  local result
  result=$(resolve_jvm_file_refs "$jvm_opts" 2>&1 | tail -1)

  # Assert - should use first occurrence
  if [[ "$result" == "-Djavax.net.ssl.keyStorePassword=firstvalue" ]]; then
    pass "$test_name"
  else
    fail "$test_name" "-Djavax.net.ssl.keyStorePassword=firstvalue" "$result"
  fi
}

test_password_with_spaces() {
  local test_name="Password containing spaces"

  # Setup
  local pwd_file="$TEST_DIR/jksPassword.txt"
  echo "jksPassword=my password with spaces" > "$pwd_file"

  # Test
  local jvm_opts="-Djavax.net.ssl.keyStorePassword=\${file:${pwd_file}:jksPassword}"
  local result
  result=$(resolve_jvm_file_refs "$jvm_opts" 2>&1 | tail -1)

  # Assert
  if [[ "$result" == "-Djavax.net.ssl.keyStorePassword=my password with spaces" ]]; then
    pass "$test_name"
  else
    fail "$test_name" "-Djavax.net.ssl.keyStorePassword=my password with spaces" "$result"
  fi
}

test_password_with_equals() {
  local test_name="Password containing equals sign"

  # Setup
  local pwd_file="$TEST_DIR/jksPassword.txt"
  echo "jksPassword=key=value=test" > "$pwd_file"

  # Test
  local jvm_opts="-Djavax.net.ssl.keyStorePassword=\${file:${pwd_file}:jksPassword}"
  local result
  result=$(resolve_jvm_file_refs "$jvm_opts" 2>&1 | tail -1)

  # Assert - should preserve everything after first =
  if [[ "$result" == "-Djavax.net.ssl.keyStorePassword=key=value=test" ]]; then
    pass "$test_name"
  else
    fail "$test_name" "-Djavax.net.ssl.keyStorePassword=key=value=test" "$result"
  fi
}

# ============================================================================
# RUN ALL TESTS
# ============================================================================

echo ""
echo "=========================================="
echo "  resolve-jvm-opts.sh Unit Test Suite"
echo "=========================================="
echo ""

echo "Running Success Cases..."
test_resolve_single_password
test_resolve_multiple_passwords
test_resolve_password_with_special_chars
test_resolve_in_full_jvm_config
test_no_resolution_when_no_file_reference
test_resolve_password_strips_newlines
test_resolve_password_strips_carriage_returns

echo ""
echo "Running Error Handling Cases..."
test_fallback_when_file_not_found
test_fallback_when_key_not_found
test_fallback_when_password_value_empty
test_fallback_when_file_empty
test_fallback_messages_are_helpful
test_fallback_multiple_missing_files

echo ""
echo "Running Edge Cases..."
test_multiple_keys_in_same_file
test_duplicate_key_uses_first
test_password_with_spaces
test_password_with_equals

# ============================================================================
# SUMMARY
# ============================================================================

echo ""
echo "=========================================="
echo "  Test Results"
echo "=========================================="
echo -e "Total:  $TESTS_TOTAL"
echo -e "${GREEN}Passed: $TESTS_PASSED${NC}"
if [ $TESTS_FAILED -gt 0 ]; then
  echo -e "${RED}Failed: $TESTS_FAILED${NC}"
else
  echo -e "Failed: $TESTS_FAILED"
fi
echo "=========================================="

if [ $TESTS_FAILED -gt 0 ]; then
  exit 1
fi

exit 0
