#!/bin/bash

# TankStopp Dockerfile Validation Script
# This script validates Dockerfile syntax and best practices without requiring Docker daemon

set -e

# 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
DOCKERFILE_PATH="Dockerfile"
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
VALIDATION_ERRORS=0
VALIDATION_WARNINGS=0

# 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"
    ((VALIDATION_WARNINGS++))
}

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

check_dockerfile_exists() {
    log_info "Checking Dockerfile existence..."

    if [ ! -f "$PROJECT_ROOT/$DOCKERFILE_PATH" ]; then
        log_error "Dockerfile not found at $PROJECT_ROOT/$DOCKERFILE_PATH"
        return 1
    fi

    log_success "Dockerfile found"
    return 0
}

validate_dockerfile_syntax() {
    log_info "Validating Dockerfile syntax..."

    local dockerfile="$PROJECT_ROOT/$DOCKERFILE_PATH"

    # Check for FROM instruction
    if ! grep -q "^FROM " "$dockerfile"; then
        log_error "No FROM instruction found"
    else
        log_success "FROM instruction found"
    fi

    # Check for multi-stage build
    local from_count=$(grep -c "^FROM " "$dockerfile")
    if [ "$from_count" -gt 1 ]; then
        log_success "Multi-stage build detected ($from_count stages)"
    else
        log_warning "Single-stage build detected. Consider multi-stage for optimization"
    fi

    # Check for WORKDIR
    if grep -q "^WORKDIR " "$dockerfile"; then
        log_success "WORKDIR instruction found"
    else
        log_warning "No WORKDIR instruction found"
    fi

    # Check for USER instruction
    if grep -q "^USER " "$dockerfile"; then
        log_success "USER instruction found (security best practice)"
    else
        log_warning "No USER instruction found. Running as root is not recommended"
    fi

    # Check for EXPOSE instruction
    if grep -q "^EXPOSE " "$dockerfile"; then
        log_success "EXPOSE instruction found"
    else
        log_warning "No EXPOSE instruction found"
    fi

    # Check for HEALTHCHECK
    if grep -q "^HEALTHCHECK " "$dockerfile"; then
        log_success "HEALTHCHECK instruction found"
    else
        log_warning "No HEALTHCHECK instruction found"
    fi

    # Check for VOLUME
    if grep -q "^VOLUME " "$dockerfile"; then
        log_success "VOLUME instruction found"
    else
        log_warning "No VOLUME instruction found"
    fi
}

validate_base_images() {
    log_info "Validating base images..."

    local dockerfile="$PROJECT_ROOT/$DOCKERFILE_PATH"

    # Extract base images
    local base_images=$(grep "^FROM " "$dockerfile" | awk '{print $2}')

    for image in $base_images; do
        if [[ "$image" == *":latest" ]]; then
            log_warning "Base image '$image' uses 'latest' tag. Consider pinning to specific version"
        elif [[ "$image" =~ :[0-9]+\.[0-9]+ ]]; then
            log_success "Base image '$image' uses versioned tag"
        else
            log_info "Base image: $image"
        fi

        # Check for official images
        if [[ "$image" =~ ^(alpine|golang|node|nginx|redis|postgres|mysql):.* ]]; then
            log_success "Using official base image: $image"
        fi
    done
}

validate_security_practices() {
    log_info "Validating security practices..."

    local dockerfile="$PROJECT_ROOT/$DOCKERFILE_PATH"

    # Check if running as root
    if grep -q "USER root" "$dockerfile"; then
        log_error "Explicitly running as root user"
    fi

    # Check for package manager cache cleanup
    if grep -q "rm -rf /var/cache/apk/\*\|apt-get clean\|yum clean" "$dockerfile"; then
        log_success "Package manager cache cleanup found"
    else
        log_warning "Consider cleaning package manager cache to reduce image size"
    fi

    # Check for unnecessary packages
    if grep -q "curl\|wget" "$dockerfile"; then
        local curl_line=$(grep -n "curl\|wget" "$dockerfile" | head -1)
        log_info "Network tools found: $curl_line"
    fi

    # Check for secrets in Dockerfile
    if grep -qi "password\|secret\|key\|token" "$dockerfile"; then
        log_warning "Potential secrets found in Dockerfile. Ensure no hardcoded credentials"
    fi
}

validate_build_optimization() {
    log_info "Validating build optimization..."

    local dockerfile="$PROJECT_ROOT/$DOCKERFILE_PATH"

    # Check for layer optimization
    local run_count=$(grep -c "^RUN " "$dockerfile")
    if [ "$run_count" -gt 10 ]; then
        log_warning "Many RUN instructions ($run_count). Consider combining for fewer layers"
    else
        log_success "Reasonable number of RUN instructions ($run_count)"
    fi

    # Check for COPY vs ADD
    if grep -q "^ADD " "$dockerfile"; then
        log_warning "ADD instruction found. Consider using COPY unless URL/tar extraction is needed"
    fi

    # Check for .dockerignore
    if [ -f "$PROJECT_ROOT/.dockerignore" ]; then
        log_success ".dockerignore file found"
    else
        log_warning ".dockerignore file not found. Consider adding to reduce build context"
    fi

    # Check for build args
    if grep -q "^ARG " "$dockerfile"; then
        log_success "Build arguments found for flexibility"
    fi
}

validate_required_files() {
    log_info "Validating required files..."

    # Check for Go module files
    if [ -f "$PROJECT_ROOT/go.mod" ]; then
        log_success "go.mod found"
    else
        log_error "go.mod not found"
    fi

    if [ -f "$PROJECT_ROOT/go.sum" ]; then
        log_success "go.sum found"
    else
        log_warning "go.sum not found"
    fi

    # Check for main.go or cmd directory
    if [ -f "$PROJECT_ROOT/main.go" ] || [ -d "$PROJECT_ROOT/cmd" ]; then
        log_success "Main Go file or cmd directory found"
    else
        log_error "No main.go or cmd directory found"
    fi

    # Check for static files
    if [ -d "$PROJECT_ROOT/static" ]; then
        log_success "Static directory found"
    else
        log_warning "Static directory not found"
    fi

    # Check for configuration files
    if ls "$PROJECT_ROOT"/config*.yaml >/dev/null 2>&1; then
        log_success "Configuration files found"
    else
        log_warning "No configuration files found"
    fi
}

validate_environment_variables() {
    log_info "Validating environment variables..."

    local dockerfile="$PROJECT_ROOT/$DOCKERFILE_PATH"

    # Check for ENV instructions
    local env_count=$(grep -c "^ENV " "$dockerfile")
    if [ "$env_count" -gt 0 ]; then
        log_success "Environment variables defined ($env_count)"

        # Show environment variables
        grep "^ENV " "$dockerfile" | while read line; do
            log_info "  $line"
        done
    else
        log_info "No ENV instructions found"
    fi

    # Check for common application variables
    if grep -q "TANKSTOPP_" "$dockerfile"; then
        log_success "Application-specific environment variables found"
    fi
}

validate_ports_and_volumes() {
    log_info "Validating ports and volumes..."

    local dockerfile="$PROJECT_ROOT/$DOCKERFILE_PATH"

    # Check exposed ports
    if grep -q "^EXPOSE " "$dockerfile"; then
        local ports=$(grep "^EXPOSE " "$dockerfile" | awk '{print $2}')
        log_success "Exposed ports: $ports"
    fi

    # Check volumes
    if grep -q "^VOLUME " "$dockerfile"; then
        local volumes=$(grep "^VOLUME " "$dockerfile" | sed 's/VOLUME //')
        log_success "Volumes defined: $volumes"
    fi
}

validate_labels_and_metadata() {
    log_info "Validating labels and metadata..."

    local dockerfile="$PROJECT_ROOT/$DOCKERFILE_PATH"

    # Check for labels
    if grep -q "^LABEL " "$dockerfile"; then
        log_success "Labels found"
        grep "^LABEL " "$dockerfile" | while read line; do
            log_info "  $line"
        done
    else
        log_warning "No labels found. Consider adding metadata labels"
    fi
}

validate_entrypoint_and_cmd() {
    log_info "Validating entrypoint and command..."

    local dockerfile="$PROJECT_ROOT/$DOCKERFILE_PATH"

    # Check for ENTRYPOINT
    if grep -q "^ENTRYPOINT " "$dockerfile"; then
        log_success "ENTRYPOINT instruction found"
    fi

    # Check for CMD
    if grep -q "^CMD " "$dockerfile"; then
        log_success "CMD instruction found"
        local cmd=$(grep "^CMD " "$dockerfile" | tail -1)
        log_info "  $cmd"
    else
        log_warning "No CMD instruction found"
    fi
}

check_docker_compose_files() {
    log_info "Checking Docker Compose files..."

    if [ -f "$PROJECT_ROOT/docker-compose.yml" ]; then
        log_success "docker-compose.yml found"
    else
        log_warning "docker-compose.yml not found"
    fi

    if [ -f "$PROJECT_ROOT/docker-compose.prod.yml" ]; then
        log_success "docker-compose.prod.yml found"
    fi

    if [ -f "$PROJECT_ROOT/docker-compose.override.yml" ]; then
        log_info "docker-compose.override.yml found"
    fi
}

validate_build_scripts() {
    log_info "Checking build scripts..."

    if [ -f "$PROJECT_ROOT/scripts/docker/build.sh" ]; then
        log_success "Docker build script found"
        if [ -x "$PROJECT_ROOT/scripts/docker/build.sh" ]; then
            log_success "Build script is executable"
        else
            log_warning "Build script is not executable"
        fi
    else
        log_warning "Docker build script not found"
    fi

    if [ -f "$PROJECT_ROOT/scripts/docker/deploy.sh" ]; then
        log_success "Docker deploy script found"
        if [ -x "$PROJECT_ROOT/scripts/docker/deploy.sh" ]; then
            log_success "Deploy script is executable"
        else
            log_warning "Deploy script is not executable"
        fi
    else
        log_warning "Docker deploy script not found"
    fi
}

generate_recommendations() {
    log_info "Generating recommendations..."

    echo ""
    echo "=== RECOMMENDATIONS ==="

    if [ "$VALIDATION_ERRORS" -eq 0 ] && [ "$VALIDATION_WARNINGS" -eq 0 ]; then
        log_success "Dockerfile validation passed with no issues!"
    else
        echo ""
        echo "Consider the following improvements:"
        echo ""

        if grep -q "USER root" "$PROJECT_ROOT/$DOCKERFILE_PATH" 2>/dev/null; then
            echo "• Create and use a non-root user for security"
        fi

        if ! grep -q "HEALTHCHECK" "$PROJECT_ROOT/$DOCKERFILE_PATH" 2>/dev/null; then
            echo "• Add HEALTHCHECK instruction for container monitoring"
        fi

        if ! grep -q "LABEL" "$PROJECT_ROOT/$DOCKERFILE_PATH" 2>/dev/null; then
            echo "• Add metadata labels (version, maintainer, description)"
        fi

        if [ ! -f "$PROJECT_ROOT/.dockerignore" ]; then
            echo "• Create .dockerignore to optimize build context"
        fi

        if ! grep -q "VOLUME" "$PROJECT_ROOT/$DOCKERFILE_PATH" 2>/dev/null; then
            echo "• Consider adding VOLUME for data persistence"
        fi

        echo "• Pin base image versions for reproducible builds"
        echo "• Minimize the number of layers by combining RUN commands"
        echo "• Remove package manager caches to reduce image size"
        echo "• Use multi-stage builds to reduce final image size"
    fi
}

show_summary() {
    echo ""
    echo "=== VALIDATION SUMMARY ==="
    echo "Dockerfile: $PROJECT_ROOT/$DOCKERFILE_PATH"
    echo "Errors: $VALIDATION_ERRORS"
    echo "Warnings: $VALIDATION_WARNINGS"

    if [ "$VALIDATION_ERRORS" -eq 0 ]; then
        log_success "Dockerfile validation completed successfully!"
        echo "Your Dockerfile is ready for building."
    else
        log_error "Dockerfile validation failed with $VALIDATION_ERRORS errors"
        echo "Please fix the errors before building the Docker image."
        exit 1
    fi
}

main() {
    echo "TankStopp Dockerfile Validation"
    echo "==============================="
    echo ""

    cd "$PROJECT_ROOT"

    # Run all validations
    check_dockerfile_exists || exit 1
    validate_dockerfile_syntax
    validate_base_images
    validate_security_practices
    validate_build_optimization
    validate_required_files
    validate_environment_variables
    validate_ports_and_volumes
    validate_labels_and_metadata
    validate_entrypoint_and_cmd
    check_docker_compose_files
    validate_build_scripts

    # Generate recommendations and summary
    generate_recommendations
    show_summary
}

# Show help if requested
if [ "$1" = "--help" ] || [ "$1" = "-h" ]; then
    cat << EOF
TankStopp Dockerfile Validation Script

Usage: $0 [OPTIONS]

This script validates your Dockerfile against best practices and security guidelines.
It checks syntax, security practices, optimization, and required files.

OPTIONS:
    -h, --help          Show this help message

CHECKS PERFORMED:
    • Dockerfile syntax validation
    • Base image version pinning
    • Security best practices
    • Build optimization
    • Required files presence
    • Environment variables
    • Port and volume configuration
    • Labels and metadata
    • Entrypoint and command setup
    • Docker Compose files
    • Build scripts

EXAMPLES:
    # Run validation
    $0

    # Show help
    $0 --help

EOF
    exit 0
fi

# Run main function
main "$@"
