#!/bin/bash
# Integration tests for Traefik deployment modes
# Tests bundled, upstream, and none modes
#
# Usage:
#   ./traefik.test.sh [bundled|upstream|none|all]
#
# Requirements:
#   - Docker and Docker Compose installed
#   - jq for JSON parsing
#   - curl for HTTP testing

set -e

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

# Test counters
TESTS_RUN=0
TESTS_PASSED=0
TESTS_FAILED=0

# Test result tracking
declare -a FAILED_TESTS=()

# Logging functions
log_info() {
    echo -e "${GREEN}[INFO]${NC} $1"
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

log_test() {
    echo -e "\n${YELLOW}[TEST]${NC} $1"
    ((TESTS_RUN++))
}

log_pass() {
    echo -e "${GREEN}[PASS]${NC} $1"
    ((TESTS_PASSED++))
}

log_fail() {
    echo -e "${RED}[FAIL]${NC} $1"
    ((TESTS_FAILED++))
    FAILED_TESTS+=("$1")
}

# Cleanup function
cleanup() {
    log_info "Cleaning up test environment..."
    docker compose -f docker-compose.test.yml down -v 2>/dev/null || true
    docker network rm traefik-public-test 2>/dev/null || true
    rm -f .env.test docker-compose.test.yml
}

# Trap cleanup on exit
trap cleanup EXIT

# Check prerequisites
check_prerequisites() {
    log_info "Checking prerequisites..."

    if ! command -v docker &> /dev/null; then
        log_error "Docker is not installed"
        exit 1
    fi

    if ! command -v docker compose &> /dev/null; then
        log_error "Docker Compose is not installed"
        exit 1
    fi

    if ! command -v jq &> /dev/null; then
        log_error "jq is not installed (required for JSON parsing)"
        exit 1
    fi

    if ! command -v curl &> /dev/null; then
        log_error "curl is not installed"
        exit 1
    fi

    log_pass "All prerequisites satisfied"
}

# Test bundled Traefik mode
test_bundled_mode() {
    log_info "=========================================="
    log_info "Testing Bundled Traefik Mode"
    log_info "=========================================="

    # Create test environment file
    cat > .env.test <<EOF
TRAEFIK_MODE=bundled
MOSAIC_API_DOMAIN=api.mosaic.test
MOSAIC_WEB_DOMAIN=mosaic.test
MOSAIC_AUTH_DOMAIN=auth.mosaic.test
TRAEFIK_DASHBOARD_ENABLED=true
TRAEFIK_TLS_ENABLED=false
TRAEFIK_ACME_EMAIL=test@example.com
POSTGRES_PASSWORD=test_password
AUTHENTIK_SECRET_KEY=test_secret_key_minimum_50_characters_long_for_testing
AUTHENTIK_POSTGRES_PASSWORD=test_auth_password
AUTHENTIK_BOOTSTRAP_PASSWORD=test_admin
JWT_SECRET=test_jwt_secret_minimum_32_chars
EOF

    # Copy docker-compose.yml to test version
    cp docker-compose.yml docker-compose.test.yml

    log_test "Starting services with bundled Traefik profile"
    if docker compose -f docker-compose.test.yml --env-file .env.test --profile traefik-bundled up -d; then
        log_pass "Services started successfully"
    else
        log_fail "Failed to start services"
        return 1
    fi

    sleep 10  # Wait for services to initialize

    log_test "Verifying Traefik container is running"
    if docker ps --filter "name=mosaic-traefik" --format "{{.Names}}" | grep -q "mosaic-traefik"; then
        log_pass "Traefik container is running"
    else
        log_fail "Traefik container not found"
    fi

    log_test "Verifying Traefik dashboard is accessible"
    if curl -sf http://localhost:8080/dashboard/ > /dev/null; then
        log_pass "Traefik dashboard is accessible"
    else
        log_fail "Traefik dashboard not accessible"
    fi

    log_test "Verifying Traefik API endpoint"
    if curl -sf http://localhost:8080/api/overview | jq -e '.http.routers' > /dev/null; then
        log_pass "Traefik API is responding"
    else
        log_fail "Traefik API not responding correctly"
    fi

    log_test "Verifying API service has Traefik labels"
    LABELS=$(docker inspect mosaic-api --format='{{json .Config.Labels}}')
    if echo "$LABELS" | jq -e '."traefik.enable"' > /dev/null; then
        log_pass "API service has Traefik labels"
    else
        log_fail "API service missing Traefik labels"
    fi

    log_test "Verifying Web service has Traefik labels"
    LABELS=$(docker inspect mosaic-web --format='{{json .Config.Labels}}')
    if echo "$LABELS" | jq -e '."traefik.enable"' > /dev/null; then
        log_pass "Web service has Traefik labels"
    else
        log_fail "Web service missing Traefik labels"
    fi

    log_test "Checking Traefik routes configuration"
    ROUTES=$(curl -sf http://localhost:8080/api/http/routers | jq -r 'keys[]')
    if echo "$ROUTES" | grep -q "mosaic-api"; then
        log_pass "API route registered with Traefik"
    else
        log_fail "API route not found in Traefik"
    fi

    if echo "$ROUTES" | grep -q "mosaic-web"; then
        log_pass "Web route registered with Traefik"
    else
        log_fail "Web route not found in Traefik"
    fi

    # Cleanup
    docker compose -f docker-compose.test.yml --env-file .env.test down -v
}

# Test upstream Traefik mode
test_upstream_mode() {
    log_info "=========================================="
    log_info "Testing Upstream Traefik Mode"
    log_info "=========================================="

    # Create external network for upstream Traefik
    log_test "Creating external Traefik network"
    if docker network create traefik-public-test 2>/dev/null; then
        log_pass "External network created"
    else
        log_warn "Network already exists or creation failed"
    fi

    # Create test environment file
    cat > .env.test <<EOF
TRAEFIK_MODE=upstream
MOSAIC_API_DOMAIN=api.mosaic.test
MOSAIC_WEB_DOMAIN=mosaic.test
MOSAIC_AUTH_DOMAIN=auth.mosaic.test
TRAEFIK_NETWORK=traefik-public-test
POSTGRES_PASSWORD=test_password
AUTHENTIK_SECRET_KEY=test_secret_key_minimum_50_characters_long_for_testing
AUTHENTIK_POSTGRES_PASSWORD=test_auth_password
AUTHENTIK_BOOTSTRAP_PASSWORD=test_admin
JWT_SECRET=test_jwt_secret_minimum_32_chars
EOF

    # Copy docker-compose.yml to test version
    cp docker-compose.yml docker-compose.test.yml

    log_test "Starting services in upstream mode (no bundled Traefik)"
    if docker compose -f docker-compose.test.yml --env-file .env.test up -d; then
        log_pass "Services started successfully"
    else
        log_fail "Failed to start services"
        return 1
    fi

    sleep 10  # Wait for services to initialize

    log_test "Verifying Traefik container is NOT running"
    if ! docker ps --filter "name=mosaic-traefik" --format "{{.Names}}" | grep -q "mosaic-traefik"; then
        log_pass "Bundled Traefik correctly not started"
    else
        log_fail "Bundled Traefik should not be running in upstream mode"
    fi

    log_test "Verifying API service is connected to external network"
    NETWORKS=$(docker inspect mosaic-api --format='{{json .NetworkSettings.Networks}}' | jq -r 'keys[]')
    if echo "$NETWORKS" | grep -q "traefik-public-test"; then
        log_pass "API service connected to external Traefik network"
    else
        log_fail "API service not connected to external Traefik network"
    fi

    log_test "Verifying Web service is connected to external network"
    NETWORKS=$(docker inspect mosaic-web --format='{{json .NetworkSettings.Networks}}' | jq -r 'keys[]')
    if echo "$NETWORKS" | grep -q "traefik-public-test"; then
        log_pass "Web service connected to external Traefik network"
    else
        log_fail "Web service not connected to external Traefik network"
    fi

    log_test "Verifying API service has correct Traefik labels for upstream"
    LABELS=$(docker inspect mosaic-api --format='{{json .Config.Labels}}')
    if echo "$LABELS" | jq -e '."traefik.enable" == "true"' > /dev/null && \
       echo "$LABELS" | jq -e '."traefik.docker.network"' > /dev/null; then
        log_pass "API service has correct upstream Traefik labels"
    else
        log_fail "API service missing or incorrect upstream Traefik labels"
    fi

    # Cleanup
    docker compose -f docker-compose.test.yml --env-file .env.test down -v
    docker network rm traefik-public-test 2>/dev/null || true
}

# Test none mode (direct port exposure)
test_none_mode() {
    log_info "=========================================="
    log_info "Testing None Mode (Direct Ports)"
    log_info "=========================================="

    # Create test environment file
    cat > .env.test <<EOF
TRAEFIK_MODE=none
API_PORT=3001
WEB_PORT=3000
POSTGRES_PASSWORD=test_password
AUTHENTIK_SECRET_KEY=test_secret_key_minimum_50_characters_long_for_testing
AUTHENTIK_POSTGRES_PASSWORD=test_auth_password
AUTHENTIK_BOOTSTRAP_PASSWORD=test_admin
JWT_SECRET=test_jwt_secret_minimum_32_chars
EOF

    # Copy docker-compose.yml to test version
    cp docker-compose.yml docker-compose.test.yml

    log_test "Starting services in none mode (no Traefik)"
    if docker compose -f docker-compose.test.yml --env-file .env.test up -d; then
        log_pass "Services started successfully"
    else
        log_fail "Failed to start services"
        return 1
    fi

    sleep 10  # Wait for services to initialize

    log_test "Verifying Traefik container is NOT running"
    if ! docker ps --filter "name=mosaic-traefik" --format "{{.Names}}" | grep -q "mosaic-traefik"; then
        log_pass "Traefik correctly not started in none mode"
    else
        log_fail "Traefik should not be running in none mode"
    fi

    log_test "Verifying API service Traefik labels are disabled"
    LABELS=$(docker inspect mosaic-api --format='{{json .Config.Labels}}')
    if echo "$LABELS" | jq -e '."traefik.enable" == "false"' > /dev/null; then
        log_pass "API service Traefik labels correctly disabled"
    else
        log_fail "API service Traefik labels should be disabled"
    fi

    log_test "Verifying direct port access to API"
    MAX_RETRIES=30
    RETRY_COUNT=0
    while [ $RETRY_COUNT -lt $MAX_RETRIES ]; do
        if curl -sf http://localhost:3001/health > /dev/null 2>&1; then
            log_pass "API accessible via direct port 3001"
            break
        fi
        ((RETRY_COUNT++))
        sleep 2
    done

    if [ $RETRY_COUNT -eq $MAX_RETRIES ]; then
        log_fail "API not accessible via direct port 3001"
    fi

    # Cleanup
    docker compose -f docker-compose.test.yml --env-file .env.test down -v
}

# Print test summary
print_summary() {
    echo ""
    log_info "=========================================="
    log_info "Test Summary"
    log_info "=========================================="
    echo "Total tests run: $TESTS_RUN"
    echo -e "${GREEN}Passed: $TESTS_PASSED${NC}"
    echo -e "${RED}Failed: $TESTS_FAILED${NC}"

    if [ $TESTS_FAILED -gt 0 ]; then
        echo ""
        log_error "Failed tests:"
        for test in "${FAILED_TESTS[@]}"; do
            echo "  - $test"
        done
        echo ""
        exit 1
    else
        echo ""
        log_pass "All tests passed!"
        exit 0
    fi
}

# Main execution
main() {
    TEST_MODE="${1:-all}"

    log_info "Mosaic Stack - Traefik Integration Tests"
    log_info "Test mode: $TEST_MODE"
    echo ""

    check_prerequisites

    case "$TEST_MODE" in
        bundled)
            test_bundled_mode
            ;;
        upstream)
            test_upstream_mode
            ;;
        none)
            test_none_mode
            ;;
        all)
            test_bundled_mode
            test_upstream_mode
            test_none_mode
            ;;
        *)
            log_error "Invalid test mode: $TEST_MODE"
            log_info "Usage: $0 [bundled|upstream|none|all]"
            exit 1
            ;;
    esac

    print_summary
}

main "$@"
