#!/bin/bash

# OpenCrates Comprehensive Testing and Build Script
# This script runs all tests, checks code quality, and prepares for deployment

set -euo pipefail

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

# Configuration
RUST_LOG=${RUST_LOG:-info}
CARGO_INCREMENTAL=${CARGO_INCREMENTAL:-1}
RUST_BACKTRACE=${RUST_BACKTRACE:-1}

# Functions
log_info() {
    echo -e "${BLUE}[INFO]${NC} $1"
}

log_success() {
    echo -e "${GREEN}[SUCCESS]${NC} $1"
}

log_warning() {
    echo -e "${YELLOW}[WARNING]${NC} $1"
}

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

check_prerequisites() {
    log_info "Checking prerequisites..."
    
    # Check Rust version
    if ! command -v cargo &> /dev/null; then
        log_error "Cargo is not installed. Please install Rust."
        exit 1
    fi
    
    # Check minimum Rust version
    RUST_VERSION=$(rustc --version | cut -d' ' -f2)
    log_info "Rust version: $RUST_VERSION"
    
    # Check for required tools
    REQUIRED_TOOLS=("clippy" "rustfmt" "cargo-audit" "cargo-outdated" "cargo-tarpaulin")
    for tool in "${REQUIRED_TOOLS[@]}"; do
        if ! cargo "$tool" --version &> /dev/null; then
            log_warning "$tool is not installed. Installing..."
            cargo install cargo-"$tool" || log_warning "Failed to install $tool"
        fi
    done
    
    log_success "Prerequisites check completed"
}

format_code() {
    log_info "Formatting code..."
    cargo fmt --all -- --check || {
        log_warning "Code formatting issues found. Fixing..."
        cargo fmt --all
        log_success "Code formatted"
    }
}

lint_code() {
    log_info "Running linter (clippy)..."
    cargo clippy --all-targets --all-features -- -D warnings
    log_success "Linting completed"
}

security_audit() {
    log_info "Running security audit..."
    cargo audit || {
        log_warning "Security vulnerabilities found. Please review."
        return 0  # Don't fail the build for this
    }
    log_success "Security audit completed"
}

check_outdated() {
    log_info "Checking for outdated dependencies..."
    cargo outdated || {
        log_warning "Some dependencies are outdated. Consider updating."
        return 0  # Don't fail the build for this
    }
    log_success "Dependency check completed"
}

run_unit_tests() {
    log_info "Running unit tests..."
    RUST_LOG=$RUST_LOG cargo test --lib --all-features --verbose
    log_success "Unit tests passed"
}

run_integration_tests() {
    log_info "Running integration tests..."
    RUST_LOG=$RUST_LOG cargo test --test comprehensive_test --all-features --verbose
    log_success "Integration tests passed"
}

run_doc_tests() {
    log_info "Running documentation tests..."
    cargo test --doc --all-features --verbose
    log_success "Documentation tests passed"
}

run_benchmarks() {
    log_info "Running benchmarks..."
    cargo bench --all-features || {
        log_warning "Benchmarks failed or not available"
        return 0
    }
    log_success "Benchmarks completed"
}

generate_coverage() {
    log_info "Generating test coverage..."
    if command -v cargo-tarpaulin &> /dev/null; then
        cargo tarpaulin --all-features --out Html --output-dir coverage/ || {
            log_warning "Coverage generation failed"
            return 0
        }
        log_success "Coverage report generated in coverage/"
    else
        log_warning "cargo-tarpaulin not available, skipping coverage"
    fi
}

build_release() {
    log_info "Building release binary..."
    cargo build --release --all-features
    log_success "Release build completed"
}

build_docker() {
    log_info "Building Docker image..."
    if command -v docker &> /dev/null; then
        docker build -t opencrates:latest . || {
            log_warning "Docker build failed"
            return 0
        }
        log_success "Docker image built successfully"
    else
        log_warning "Docker not available, skipping Docker build"
    fi
}

test_docker() {
    log_info "Testing Docker image..."
    if command -v docker &> /dev/null; then
        # Build test image
        docker build --target testing -t opencrates:test . || {
            log_warning "Docker test build failed"
            return 0
        }
        
        # Run tests in container
        docker run --rm opencrates:test || {
            log_warning "Docker tests failed"
            return 0
        }
        
        log_success "Docker tests passed"
    else
        log_warning "Docker not available, skipping Docker tests"
    fi
}

validate_cargo_toml() {
    log_info "Validating Cargo.toml..."
    cargo metadata --quiet > /dev/null
    log_success "Cargo.toml is valid"
}

check_documentation() {
    log_info "Building documentation..."
    cargo doc --all-features --no-deps --document-private-items
    log_success "Documentation built successfully"
}

clean_artifacts() {
    log_info "Cleaning build artifacts..."
    cargo clean
    log_success "Artifacts cleaned"
}

run_all_tests() {
    log_info "Running comprehensive test suite..."
    
    run_unit_tests
    run_integration_tests
    run_doc_tests
    
    log_success "All tests completed successfully"
}

prepare_for_crates_io() {
    log_info "Preparing for crates.io publication..."
    
    # Check package
    cargo package --allow-dirty
    
    # Dry run publish
    cargo publish --dry-run --allow-dirty
    
    log_success "Package is ready for crates.io publication"
    log_info "To publish, run: cargo publish"
}

show_help() {
    cat << EOF
OpenCrates Testing and Build Script

Usage: $0 [COMMAND]

Commands:
    check       Run all checks (format, lint, audit)
    test        Run all tests
    build       Build release binary
    docker      Build and test Docker image
    coverage    Generate test coverage report
    bench       Run benchmarks
    package     Prepare package for crates.io
    publish     Prepare for crates.io publication
    clean       Clean build artifacts
    all         Run everything (default)
    help        Show this help message

Examples:
    $0              # Run all checks and tests
    $0 test         # Run tests only
    $0 docker       # Build and test Docker
    $0 package      # Prepare for publication
EOF
}

main() {
    local command=${1:-all}
    
    case $command in
        check)
            check_prerequisites
            validate_cargo_toml
            format_code
            lint_code
            security_audit
            check_outdated
            ;;
        test)
            run_all_tests
            ;;
        build)
            build_release
            ;;
        docker)
            build_docker
            test_docker
            ;;
        coverage)
            generate_coverage
            ;;
        bench)
            run_benchmarks
            ;;
        package)
            validate_cargo_toml
            format_code
            lint_code
            run_all_tests
            build_release
            ;;
        publish)
            prepare_for_crates_io
            ;;
        clean)
            clean_artifacts
            ;;
        all)
            log_info "Running comprehensive build and test pipeline..."
            check_prerequisites
            validate_cargo_toml
            format_code
            lint_code
            security_audit
            check_outdated
            run_all_tests
            generate_coverage
            build_release
            check_documentation
            build_docker
            test_docker
            run_benchmarks
            log_success "All tasks completed successfully!"
            log_info "Project is ready for deployment and publication"
            ;;
        help)
            show_help
            ;;
        *)
            log_error "Unknown command: $command"
            show_help
            exit 1
            ;;
    esac
}

# Trap to ensure cleanup on exit
trap 'log_info "Script interrupted"; exit 130' INT

# Set environment variables
export RUST_LOG
export CARGO_INCREMENTAL
export RUST_BACKTRACE

# Run main function
main "$@" 