#!/bin/bash
# Use "system bash" (v3.2) to validate compatibility
set -Eeuo pipefail
START_TIME=$(date +%s.%N)

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CLOD_BASE="$SCRIPT_DIR/../clod"

[[ "${VERBOSE:-0}" =~ ^[0-9]+$ ]] && VERBOSE="${VERBOSE:-0}" || VERBOSE=1

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m'

TESTS_RUN=0
TESTS_PASSED=0
TESTS_FAILED=0
TMP_DIR=""
TEST_WORKSPACE=""
TEST_HOME=""
MOCK_BIN=""

pass() {
    ((TESTS_PASSED++)) || true
    if [[ "$VERBOSE" -ne 0 ]]; then
        echo -e "  ${GREEN}PASS${NC}: $1"
    fi
}

fail() {
    ((TESTS_FAILED++)) || true
    echo -e "  ${RED}FAIL${NC}: $1"
    if [[ -n "${2:-}" ]]; then
        echo "    Expected: $2"
    fi
    if [[ -n "${3:-}" ]]; then
        echo "    Got: $3"
    fi
}

run_test() {
    local name="$1"
    shift
    ((TESTS_RUN++)) || true

    if "$@"; then
        pass "$name"
    else
        fail "$name" "success" "failed"
    fi
}

create_stub() {
    local command_name="$1"
    local path="$MOCK_BIN/$command_name"
    cat > "$path" <<'STUB'
#!/bin/bash
exit 0
STUB
    chmod +x "$path"
}

setup_workspace() {
    if [[ ! -x "$CLOD_BASE" ]]; then
        echo "Missing clod executable at $CLOD_BASE" >&2
        exit 1
    fi

    TMP_DIR="$(mktemp -d)"
    TEST_WORKSPACE="$TMP_DIR/clodpod"
    TEST_HOME="$TMP_DIR/home"
    MOCK_BIN="$TMP_DIR/bin"

    mkdir -p "$TEST_WORKSPACE" "$TEST_HOME/.ssh" "$MOCK_BIN"
    cp "$CLOD_BASE" "$TEST_WORKSPACE/clod"
    chmod +x "$TEST_WORKSPACE/clod"

    # Copy guest source so refresh_guest_home has a source tree
    if [[ -d "$SCRIPT_DIR/../guest" ]]; then
        cp -R "$SCRIPT_DIR/../guest" "$TEST_WORKSPACE/guest"
    fi

    # Copy lib/ so clod can source its modules
    if [[ -d "$SCRIPT_DIR/../lib" ]]; then
        cp -R "$SCRIPT_DIR/../lib" "$TEST_WORKSPACE/lib"
    fi

    # Prevent install_tools from trying to install dependencies.
    create_stub brew
    create_stub netcat
    create_stub rush

    # sysctl stub: return a plausible host RAM value
    cat > "$MOCK_BIN/sysctl" <<'STUB'
#!/bin/bash
case "$2" in
    hw.memsize) echo "17179869184" ;;
    hw.ncpu)    echo "8" ;;
    *)          echo "0" ;;
esac
STUB
    chmod +x "$MOCK_BIN/sysctl"

    # tart stub: returns JSON for --format json, exit 0 otherwise
    cat > "$MOCK_BIN/tart" <<'STUB'
#!/bin/bash
for arg in "$@"; do
    [[ "$arg" == "json" ]] && { echo "[]"; exit 0; }
done
exit 0
STUB
    chmod +x "$MOCK_BIN/tart"

    # jq stub: only for commands that don't need real jq parsing
    create_stub jq

    # ssh-keyscan stub: benign fake output
    cat > "$MOCK_BIN/ssh-keyscan" <<'STUB'
#!/bin/bash
echo "github.com ssh-rsa AAAAFAKE"
exit 0
STUB
    chmod +x "$MOCK_BIN/ssh-keyscan"
}

cleanup() {
    if [[ -n "$TMP_DIR" && -d "$TMP_DIR" ]]; then
        rm -rf "$TMP_DIR"
    fi
}
trap cleanup EXIT

run_clod() {
    (
        cd "$TEST_WORKSPACE"
        HOME="$TEST_HOME" PATH="$MOCK_BIN:$PATH" VERBOSE=0 /bin/bash "$TEST_WORKSPACE/clod" "$@"
    )
}

# Reset workspace and home for test isolation
reset_test_env() {
    rm -rf "$TEST_WORKSPACE" "$TEST_HOME"
    mkdir -p "$TEST_WORKSPACE" "$TEST_HOME/.ssh" "$MOCK_BIN"
    cp "$CLOD_BASE" "$TEST_WORKSPACE/clod"
    chmod +x "$TEST_WORKSPACE/clod"
    if [[ -d "$SCRIPT_DIR/../guest" ]]; then
        cp -R "$SCRIPT_DIR/../guest" "$TEST_WORKSPACE/guest"
    fi
    if [[ -d "$SCRIPT_DIR/../lib" ]]; then
        cp -R "$SCRIPT_DIR/../lib" "$TEST_WORKSPACE/lib"
    fi
}

# Helper: create a legacy DB with the full init_db schema
create_legacy_db() {
    sqlite3 "$TEST_WORKSPACE/.clodpod.sqlite" <<'SQL'
CREATE TABLE IF NOT EXISTS projects (path TEXT UNIQUE NOT NULL, name TEXT UNIQUE NOT NULL, date_added TEXT, active INTEGER DEFAULT 0);
CREATE TABLE IF NOT EXISTS settings (key TEXT PRIMARY KEY, value TEXT NOT NULL, updated_at TEXT);
CREATE TABLE IF NOT EXISTS instances (name TEXT PRIMARY KEY, vm_name TEXT UNIQUE NOT NULL, ram_mb INTEGER, created_at TEXT);
CREATE TABLE IF NOT EXISTS instance_dirs (instance_name TEXT NOT NULL, dir_name TEXT NOT NULL, dir_path TEXT NOT NULL, is_primary INTEGER NOT NULL DEFAULT 0, PRIMARY KEY (instance_name, dir_name));
SQL
}

###############################################################################
# Original tests
###############################################################################

test_version() {
    local output status
    set +e
    output="$(run_clod --version 2>&1)"
    status=$?
    set -e

    [[ "$status" -eq 0 && "$output" == *"clod version"* ]]
}

test_help() {
    local output status
    set +e
    output="$(run_clod --help 2>&1)"
    status=$?
    set -e

    [[ "$status" -eq 0 && "$output" == *"Usage: clod"* && "$output" == *"Commands:"* ]]
}

test_unknown_option() {
    local output status
    set +e
    output="$(run_clod --bogus 2>&1)"
    status=$?
    set -e

    [[ "$status" -ne 0 && "$output" == *"Unknown option"* ]]
}

test_add_list_remove_project() {
    local project_dir output status
    project_dir="$TMP_DIR/project-alpha"
    mkdir -p "$project_dir"

    set +e
    output="$(run_clod add "$project_dir" alpha 2>&1)"
    status=$?
    set -e
    if [[ "$status" -ne 0 || "$output" != *"Added project alpha"* ]]; then
        return 1
    fi

    set +e
    output="$(run_clod list 2>&1)"
    status=$?
    set -e
    if [[ "$status" -ne 0 || "$output" != *"alpha"* || "$output" != *"$project_dir"* ]]; then
        return 1
    fi

    set +e
    output="$(run_clod remove alpha 2>&1)"
    status=$?
    set -e
    if [[ "$status" -ne 0 || "$output" != *"Removed project alpha"* ]]; then
        return 1
    fi

    set +e
    output="$(run_clod list 2>&1)"
    status=$?
    set -e
    if [[ "$status" -ne 0 || "$output" == *"alpha"* ]]; then
        return 1
    fi

    return 0
}

test_add_missing_directory_fails() {
    local output status
    set +e
    output="$(run_clod add "$TMP_DIR/does-not-exist" nope 2>&1)"
    status=$?
    set -e

    [[ "$status" -ne 0 && "$output" == *"Error: directory not found"* ]]
}

test_remove_missing_project_fails() {
    local output status
    set +e
    output="$(run_clod remove missing-project 2>&1)"
    status=$?
    set -e

    [[ "$status" -ne 0 && "$output" == *"Error: Project not found"* ]]
}

###############################################################################
# Guest-home guard tests
###############################################################################

# clod ls in legacy-DB state must not trigger network or guest-home mutation
test_ls_legacy_db_no_side_effects() {
    reset_test_env
    create_legacy_db

    # Replace ssh-keyscan with a failing stub to detect unwanted calls
    cat > "$MOCK_BIN/ssh-keyscan" <<'STUB'
#!/bin/bash
echo "TEST-FAIL: ssh-keyscan called" >&2; exit 92
STUB
    chmod +x "$MOCK_BIN/ssh-keyscan"

    local output status
    set +e
    output="$(run_clod ls 2>&1)"
    status=$?
    set -e

    # Restore benign stub
    cat > "$MOCK_BIN/ssh-keyscan" <<'STUB'
#!/bin/bash
echo "github.com ssh-rsa AAAAFAKE"; exit 0
STUB
    chmod +x "$MOCK_BIN/ssh-keyscan"

    [[ "$status" -eq 0 ]] || return 1
    [[ ! -d "$TEST_HOME/.local/share/clodpod/guest" ]] || return 1
    [[ "$output" == *"Run 'clod migrate'"* ]] || return 1
}

# clod migrate must create SSH key and populate guest on a no-key machine
test_migrate_creates_guest() {
    reset_test_env
    create_legacy_db

    local output status
    set +e
    output="$(run_clod migrate 2>&1)"
    status=$?
    set -e

    [[ "$status" -eq 0 ]] || return 1
    [[ -f "$TEST_HOME/.local/share/clodpod/clodpod.sqlite" ]] || return 1
    [[ ! -f "$TEST_WORKSPACE/.clodpod.sqlite" ]] || return 1
    [[ -f "$TEST_HOME/.ssh/id_ed25519_clodpod.pub" ]] || return 1
    [[ -f "$TEST_HOME/.local/share/clodpod/guest/.populated" ]] || return 1
}

# clod start with missing guest heals it via ensure_guest_home guard
test_start_heals_guest() {
    reset_test_env

    # Need real jq for get_vm_state JSON parsing
    if ! command -v jq >/dev/null 2>&1; then
        echo "    (skipped: jq not found)" >&2
        return 0
    fi

    # Pre-seed SSH key so ensure_ssh_key doesn't set REBUILD_DST
    ssh-keygen -t ed25519 -N "" -f "$TEST_HOME/.ssh/id_ed25519_clodpod" -q

    # Pre-create XDG DB (no legacy DB — skip the legacy branch entirely)
    mkdir -p "$TEST_HOME/.local/share/clodpod"
    sqlite3 "$TEST_HOME/.local/share/clodpod/clodpod.sqlite" <<'SQL'
CREATE TABLE IF NOT EXISTS projects (path TEXT UNIQUE NOT NULL, name TEXT UNIQUE NOT NULL, date_added TEXT, active INTEGER DEFAULT 0);
CREATE TABLE IF NOT EXISTS settings (key TEXT PRIMARY KEY, value TEXT NOT NULL, updated_at TEXT);
CREATE TABLE IF NOT EXISTS instances (name TEXT PRIMARY KEY, vm_name TEXT UNIQUE NOT NULL, ram_mb INTEGER, created_at TEXT);
CREATE TABLE IF NOT EXISTS instance_dirs (instance_name TEXT NOT NULL, dir_name TEXT NOT NULL, dir_path TEXT NOT NULL, is_primary INTEGER NOT NULL DEFAULT 0, PRIMARY KEY (instance_name, dir_name));
SQL

    # tart stub that reports both VMs exist and transitions to "running"
    # after tart run is called, uses real jq via --format json
    local tart_state_file="$TMP_DIR/.tart_state"
    echo "stopped" > "$tart_state_file"
    cat > "$MOCK_BIN/tart" <<STUB
#!/bin/bash
for arg in "\$@"; do
    if [[ "\$arg" == "json" ]]; then
        state=\$(cat "$tart_state_file")
        cat <<ENDJSON
[{"Source":"local","Name":"clodpod-xcode-base","State":"\$state"},{"Source":"local","Name":"clodpod-xcode","State":"\$state"}]
ENDJSON
        exit 0
    fi
done
# "tart run" transitions VMs to running
if [[ "\${1:-}" == "run" ]]; then
    echo "running" > "$tart_state_file"
fi
exit 0
STUB
    chmod +x "$MOCK_BIN/tart"
    # Remove jq stub so real jq is used
    rm -f "$MOCK_BIN/jq"
    # Stub out sudo (DHCP lease) and openssl (tmp VM name)
    cat > "$MOCK_BIN/sudo" <<'STUB'
#!/bin/bash
exit 0
STUB
    chmod +x "$MOCK_BIN/sudo"
    cat > "$MOCK_BIN/openssl" <<'STUB'
#!/bin/bash
echo "deadbeef"
STUB
    chmod +x "$MOCK_BIN/openssl"

    local output status
    set +e
    output="$(run_clod start 2>&1)"
    status=$?
    set -e

    # Restore default stubs for subsequent tests
    create_stub tart
    create_stub jq
    rm -f "$MOCK_BIN/sudo" "$MOCK_BIN/openssl" "$tart_state_file"

    [[ -f "$TEST_HOME/.local/share/clodpod/guest/.populated" ]] || return 1
    [[ -f "$TEST_HOME/.local/share/clodpod/guest/home/.ssh/authorized_keys" ]] || return 1
}


###############################################################################
# Run tests
###############################################################################

setup_workspace

if [[ "$VERBOSE" -ne 0 ]]; then
    echo "=== clod CLI tests ==="
fi

run_test "--version shows version" test_version
run_test "--help shows usage" test_help
run_test "unknown option rejected" test_unknown_option
run_test "add/list/remove project works" test_add_list_remove_project
run_test "add rejects missing directory" test_add_missing_directory_fails
run_test "remove rejects missing project" test_remove_missing_project_fails

if [[ "$VERBOSE" -ne 0 ]]; then
    echo ""
    echo "=== guest-home guard tests ==="
fi

run_test "ls with legacy DB has no side effects" test_ls_legacy_db_no_side_effects
run_test "migrate creates SSH key and guest" test_migrate_creates_guest
run_test "start heals missing guest via guard" test_start_heals_guest

if [[ "$VERBOSE" -ne 0 ]]; then
    echo ""
    echo -e "Passed: ${GREEN}$TESTS_PASSED${NC}"
    echo -e "Failed: ${RED}$TESTS_FAILED${NC}"
fi

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

END_TIME=$(date +%s.%N)
ELAPSED_TIME=$(echo "$END_TIME - $START_TIME" | bc --mathlib | xargs printf "%.2f\n")
if [[ "$VERBOSE" -ne 0 ]]; then
    echo -e "✅ Tests completed in $ELAPSED_TIME seconds!"
fi
