#!/bin/bash
# Tests for .claude/scripts/check-docs-updated.sh

set -uo pipefail
# Note: Not using -e because test failures shouldn't stop execution

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../../../.." && pwd)"
CHECK_DOCS_SCRIPT="$PROJECT_ROOT/.claude/scripts/check-docs-updated.sh"

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

# Preliminary validation: check script exists and is executable
if [ ! -f "$CHECK_DOCS_SCRIPT" ]; then
    echo -e "${RED}ERROR: check-docs-updated script not found: $CHECK_DOCS_SCRIPT${NC}"
    exit 1
fi

if [ ! -x "$CHECK_DOCS_SCRIPT" ]; then
    chmod +x "$CHECK_DOCS_SCRIPT"
fi

# Test counter
TESTS_RUN=0
TESTS_PASSED=0
TESTS_FAILED=0

# Helper functions
pass() {
    echo -e "${GREEN}✓${NC} $1"
    ((TESTS_PASSED++))
    ((TESTS_RUN++))
}

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

skip() {
    echo -e "${YELLOW}⊘${NC} $1 (skipped)"
    ((TESTS_RUN++))
}

# Setup: Create a temporary git repository for testing
setup_test_repo() {
    local temp_dir="$1"

    mkdir -p "$temp_dir"
    cd "$temp_dir" || return 1

    git init > /dev/null 2>&1
    git config user.name "Test User" > /dev/null 2>&1
    git config user.email "test@example.com" > /dev/null 2>&1

    # Create main branch with initial commit
    echo "# Test Project" > README.md
    git add README.md
    git commit -m "Initial commit" > /dev/null 2>&1
    git branch -M main > /dev/null 2>&1

    # Create a feature branch
    git checkout -b feature/test > /dev/null 2>&1
}

# Cleanup: Remove temporary repository
cleanup_test_repo() {
    local temp_dir="$1"
    if [ -d "$temp_dir" ]; then
        cd /
        rm -rf "$temp_dir"
    fi
}

# Test 1: Script detects when not in a git repository
test_not_git_repo() {
    local temp_dir
    temp_dir=$(mktemp -d)
    local exit_code

    cd "$temp_dir" || return 1

    "$CHECK_DOCS_SCRIPT" main > /dev/null 2>&1 || exit_code=$?

    cleanup_test_repo "$temp_dir"

    if [ "${exit_code:-0}" -eq 1 ]; then
        pass "Script detects when not in git repository"
    else
        fail "Script detects when not in git repository" "exit code 1" "exit code ${exit_code:-0}"
    fi
}

# Test 2: Script exits successfully when no files changed
test_no_files_changed() {
    local temp_dir
    temp_dir=$(mktemp -d)
    local exit_code

    setup_test_repo "$temp_dir"

    "$CHECK_DOCS_SCRIPT" main > /dev/null 2>&1 || exit_code=$?

    cleanup_test_repo "$temp_dir"

    if [ "${exit_code:-0}" -eq 0 ]; then
        pass "Script exits successfully when no files changed"
    else
        fail "Script exits successfully when no files changed" "exit code 0" "exit code ${exit_code:-0}"
    fi
}

# Test 3: Script warns when code changed without docs
test_code_without_docs_warning() {
    local temp_dir
    temp_dir=$(mktemp -d)
    local output

    setup_test_repo "$temp_dir"

    # Add a Python file without updating docs
    echo "def hello(): pass" > test.py
    git add test.py
    git commit -m "Add test.py" > /dev/null 2>&1

    output=$("$CHECK_DOCS_SCRIPT" main 2>&1)

    cleanup_test_repo "$temp_dir"

    if [[ "$output" =~ "WARNING" ]] && [[ "$output" =~ "no documentation files updated" ]]; then
        pass "Script warns when code changed without docs"
    else
        fail "Script warns when code changed without docs" "warning about missing docs" "no warning"
    fi
}

# Test 4: Script detects Python files with missing docstrings
test_python_missing_docstrings() {
    local temp_dir
    temp_dir=$(mktemp -d)
    local output

    setup_test_repo "$temp_dir"

    # Add Python file without docstrings
    cat > test.py << 'EOF'
def function_without_docstring():
    return "test"

class ClassWithoutDocstring:
    def method(self):
        return "test"
EOF
    git add test.py
    git commit -m "Add test.py" > /dev/null 2>&1

    output=$("$CHECK_DOCS_SCRIPT" main 2>&1)

    cleanup_test_repo "$temp_dir"

    if [[ "$output" =~ "may be missing docstrings" ]]; then
        pass "Script detects Python files with missing docstrings"
    else
        fail "Script detects Python files with missing docstrings" "warning about missing docstrings" "no warning"
    fi
}

# Test 5: Script recognizes when docs are updated alongside code
test_code_with_docs_success() {
    local temp_dir
    temp_dir=$(mktemp -d)
    local output

    setup_test_repo "$temp_dir"

    # Add code and docs together
    echo "def hello(): pass" > test.py
    echo "# Documentation" > docs.md
    git add test.py docs.md
    git commit -m "Add code and docs" > /dev/null 2>&1

    output=$("$CHECK_DOCS_SCRIPT" main 2>&1)

    cleanup_test_repo "$temp_dir"

    if [[ "$output" =~ "Documentation files also changed" ]]; then
        pass "Script recognizes docs updated with code"
    else
        fail "Script recognizes docs updated with code" "success message about docs" "no success message"
    fi
}

# Test 6: Script detects API-related file changes
test_api_file_detection() {
    local temp_dir
    temp_dir=$(mktemp -d)
    local output

    setup_test_repo "$temp_dir"

    # Add API file
    echo "# API endpoint" > api.py
    git add api.py
    git commit -m "Add API" > /dev/null 2>&1

    output=$("$CHECK_DOCS_SCRIPT" main 2>&1)

    cleanup_test_repo "$temp_dir"

    if [[ "$output" =~ "API-related files changed" ]] || [[ "$output" =~ "docs/api.md" ]]; then
        pass "Script detects API-related file changes"
    else
        fail "Script detects API-related file changes" "API-related suggestion" "no suggestion"
    fi
}

# Test 7: Script recognizes test-only changes
test_test_only_changes() {
    local temp_dir
    temp_dir=$(mktemp -d)
    local output

    setup_test_repo "$temp_dir"

    # Add test file only
    echo "def test_something(): assert True" > test_feature.py
    git add test_feature.py
    git commit -m "Add tests" > /dev/null 2>&1

    output=$("$CHECK_DOCS_SCRIPT" main 2>&1)

    cleanup_test_repo "$temp_dir"

    if [[ "$output" =~ "Only test files changed" ]] || [[ "$output" =~ "minimal documentation" ]]; then
        pass "Script recognizes test-only changes"
    else
        fail "Script recognizes test-only changes" "test-only message" "no message"
    fi
}

# Test 8: Script recognizes docs-only changes
test_docs_only_changes() {
    local temp_dir
    temp_dir=$(mktemp -d)
    local output

    setup_test_repo "$temp_dir"

    # Add docs only
    echo "# Updated docs" > README.md
    git add README.md
    git commit -m "Update README" > /dev/null 2>&1

    output=$("$CHECK_DOCS_SCRIPT" main 2>&1)

    cleanup_test_repo "$temp_dir"

    if [[ "$output" =~ "Documentation-only changes" ]]; then
        pass "Script recognizes docs-only changes"
    else
        fail "Script recognizes docs-only changes" "docs-only message" "no message"
    fi
}

# Test 9: Script handles files with docstrings correctly
test_python_with_docstrings() {
    local temp_dir
    temp_dir=$(mktemp -d)
    local output

    setup_test_repo "$temp_dir"

    # Add Python file WITH docstrings
    cat > test.py << 'EOF'
"""Module docstring."""

def function_with_docstring():
    """Function docstring."""
    return "test"
EOF
    git add test.py
    git commit -m "Add test.py with docstrings" > /dev/null 2>&1

    output=$("$CHECK_DOCS_SCRIPT" main 2>&1)

    cleanup_test_repo "$temp_dir"

    # Should still warn about missing external docs, but not about docstrings
    if [[ ! "$output" =~ "may be missing docstrings" ]]; then
        pass "Script handles Python files with docstrings correctly"
    else
        fail "Script handles Python files with docstrings correctly" "no docstring warning" "false positive"
    fi
}

# Test 10: Script detects indented function definitions
test_indented_definitions() {
    local temp_dir
    temp_dir=$(mktemp -d)
    local output

    setup_test_repo "$temp_dir"

    # Add Python file with indented methods (no docstrings)
    cat > test.py << 'EOF'
class MyClass:
    def method_one(self):
        return "test"

    def method_two(self):
        return "test"
EOF
    git add test.py
    git commit -m "Add class" > /dev/null 2>&1

    output=$("$CHECK_DOCS_SCRIPT" main 2>&1)

    cleanup_test_repo "$temp_dir"

    if [[ "$output" =~ "may be missing docstrings" ]]; then
        pass "Script detects indented function definitions"
    else
        fail "Script detects indented function definitions" "docstring warning" "no warning"
    fi
}

# Test 11: Script detects async function definitions
test_async_functions() {
    local temp_dir
    temp_dir=$(mktemp -d)
    local output

    setup_test_repo "$temp_dir"

    # Add Python file with async functions
    cat > test.py << 'EOF'
async def async_function():
    return "test"

class MyClass:
    async def async_method(self):
        return "test"
EOF
    git add test.py
    git commit -m "Add async functions" > /dev/null 2>&1

    output=$("$CHECK_DOCS_SCRIPT" main 2>&1)

    cleanup_test_repo "$temp_dir"

    if [[ "$output" =~ "may be missing docstrings" ]]; then
        pass "Script detects async function definitions"
    else
        fail "Script detects async function definitions" "docstring warning" "no warning"
    fi
}

# Test 12: Script exits with 0 even with warnings (informational)
test_exit_code_with_warnings() {
    local temp_dir
    temp_dir=$(mktemp -d)
    local exit_code

    setup_test_repo "$temp_dir"

    # Add code without docs (should warn but exit 0)
    echo "def hello(): pass" > test.py
    git add test.py
    git commit -m "Add test.py" > /dev/null 2>&1

    "$CHECK_DOCS_SCRIPT" main > /dev/null 2>&1 || exit_code=$?

    cleanup_test_repo "$temp_dir"

    if [ "${exit_code:-0}" -eq 0 ]; then
        pass "Script exits with 0 even with warnings (informational)"
    else
        fail "Script exits with 0 even with warnings" "exit code 0" "exit code ${exit_code:-0}"
    fi
}

# Test 13: Script handles various file extensions
test_various_code_extensions() {
    local temp_dir
    temp_dir=$(mktemp -d)
    local output

    setup_test_repo "$temp_dir"

    # Add various code files
    echo "function test() {}" > test.js
    echo "fn main() {}" > test.rs
    echo "func main() {}" > test.go
    git add test.js test.rs test.go
    git commit -m "Add various code files" > /dev/null 2>&1

    output=$("$CHECK_DOCS_SCRIPT" main 2>&1)

    cleanup_test_repo "$temp_dir"

    if [[ "$output" =~ "Source code files changed" ]]; then
        pass "Script handles various file extensions"
    else
        fail "Script handles various file extensions" "detection of source files" "no detection"
    fi
}

# Test 14: Script handles detached HEAD state gracefully
test_detached_head() {
    local temp_dir
    temp_dir=$(mktemp -d)
    local exit_code

    setup_test_repo "$temp_dir"

    # Create a commit and detach HEAD
    echo "test" > test.txt
    git add test.txt
    git commit -m "Test commit" > /dev/null 2>&1
    local commit_hash=$(git rev-parse HEAD)
    git checkout "$commit_hash" > /dev/null 2>&1

    # Try to run the script (should handle gracefully)
    "$CHECK_DOCS_SCRIPT" main > /dev/null 2>&1 || exit_code=$?

    cleanup_test_repo "$temp_dir"

    # Should exit 0 (no changes) or handle gracefully
    if [ "${exit_code:-0}" -eq 0 ] || [ "${exit_code:-0}" -eq 1 ]; then
        pass "Script handles detached HEAD state gracefully"
    else
        fail "Script handles detached HEAD state" "graceful handling" "unexpected error"
    fi
}

# Test 15: Script provides helpful suggestions
test_helpful_suggestions() {
    local temp_dir
    temp_dir=$(mktemp -d)
    local output

    setup_test_repo "$temp_dir"

    # Add code without docs
    echo "def hello(): pass" > test.py
    git add test.py
    git commit -m "Add test.py" > /dev/null 2>&1

    output=$("$CHECK_DOCS_SCRIPT" main 2>&1)

    cleanup_test_repo "$temp_dir"

    if [[ "$output" =~ "Consider updating" ]] || [[ "$output" =~ "README" ]]; then
        pass "Script provides helpful suggestions"
    else
        fail "Script provides helpful suggestions" "suggestions for docs to update" "no suggestions"
    fi
}

# Run all tests
echo "Running check-docs-updated.sh tests..."
echo "======================================"

test_not_git_repo
test_no_files_changed
test_code_without_docs_warning
test_python_missing_docstrings
test_code_with_docs_success
test_api_file_detection
test_test_only_changes
test_docs_only_changes
test_python_with_docstrings
test_indented_definitions
test_async_functions
test_exit_code_with_warnings
test_various_code_extensions
test_detached_head
test_helpful_suggestions

# Summary
echo ""
echo "======================================"
echo "Tests run: $TESTS_RUN"
echo -e "${GREEN}Passed: $TESTS_PASSED${NC}"
if [ $TESTS_FAILED -gt 0 ]; then
    echo -e "${RED}Failed: $TESTS_FAILED${NC}"
    exit 1
else
    echo "All tests passed!"
    exit 0
fi
