#!/bin/bash

# GitLab CI/CD Configuration Validator
# Validates .gitlab-ci.yml files for syntax and common issues

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

# Default file
CONFIG_FILE="${1:-.gitlab-ci.yml}"

echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}GitLab CI/CD Configuration Validator${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""

# Check if file exists
if [ ! -f "$CONFIG_FILE" ]; then
    echo -e "${RED}Error: File not found: $CONFIG_FILE${NC}"
    exit 1
fi

echo -e "${GREEN}Validating: $CONFIG_FILE${NC}"
echo ""

# Counter for issues
ERRORS=0
WARNINGS=0

# Function to report error
report_error() {
    echo -e "${RED}✗ ERROR: $1${NC}"
    ((ERRORS++))
}

# Function to report warning
report_warning() {
    echo -e "${YELLOW}⚠ WARNING: $1${NC}"
    ((WARNINGS++))
}

# Function to report success
report_success() {
    echo -e "${GREEN}✓ $1${NC}"
}

# 1. YAML Syntax Validation
echo -e "${BLUE}1. Checking YAML syntax...${NC}"
if command -v python3 &> /dev/null; then
    if python3 -c "import yaml; yaml.safe_load(open('$CONFIG_FILE'))" 2>/dev/null; then
        report_success "YAML syntax is valid"
    else
        report_error "Invalid YAML syntax"
    fi
elif command -v ruby &> /dev/null; then
    if ruby -ryaml -e "YAML.load_file('$CONFIG_FILE')" 2>/dev/null; then
        report_success "YAML syntax is valid"
    else
        report_error "Invalid YAML syntax"
    fi
else
    report_warning "Python3 or Ruby not found - skipping YAML syntax check"
fi
echo ""

# 2. Check for required fields
echo -e "${BLUE}2. Checking required fields...${NC}"

if grep -q "^stages:" "$CONFIG_FILE"; then
    report_success "Stages defined"
else
    report_warning "No stages defined (using default stages)"
fi

# Count jobs (lines that end with : and are not comments or keywords)
JOB_COUNT=$(grep -E "^[a-zA-Z0-9_-]+:" "$CONFIG_FILE" | grep -v "^stages:" | grep -v "^variables:" | grep -v "^include:" | grep -v "^default:" | wc -l)
if [ "$JOB_COUNT" -gt 0 ]; then
    report_success "Found $JOB_COUNT job(s)"
else
    report_error "No jobs defined"
fi
echo ""

# 3. Check for common issues
echo -e "${BLUE}3. Checking for common issues...${NC}"

# Check for tabs (YAML should use spaces)
if grep -P '\t' "$CONFIG_FILE" > /dev/null 2>&1; then
    report_error "File contains tabs - YAML requires spaces for indentation"
elif grep $'\t' "$CONFIG_FILE" > /dev/null 2>&1; then
    report_error "File contains tabs - YAML requires spaces for indentation"
else
    report_success "No tabs found (proper YAML indentation)"
fi

# Check for trailing whitespace
if grep -E ' +$' "$CONFIG_FILE" > /dev/null 2>&1; then
    report_warning "File contains trailing whitespace"
else
    report_success "No trailing whitespace"
fi

# Check for very long lines
MAX_LINE_LENGTH=$(awk '{print length}' "$CONFIG_FILE" | sort -rn | head -1)
if [ "$MAX_LINE_LENGTH" -gt 120 ]; then
    report_warning "Some lines exceed 120 characters (max: $MAX_LINE_LENGTH)"
else
    report_success "All lines within reasonable length"
fi
echo ""

# 4. Check for security best practices
echo -e "${BLUE}4. Checking security best practices...${NC}"

# Check for hardcoded secrets (basic check)
if grep -iE "(password|secret|token|api_key|private_key).*[:=].*['\"]" "$CONFIG_FILE" | grep -v "\$CI_" | grep -v "\$\{" > /dev/null 2>&1; then
    report_error "Possible hardcoded secrets found - use CI/CD variables instead"
else
    report_success "No obvious hardcoded secrets"
fi

# Check for use of latest tag
if grep -E "image:.*:latest" "$CONFIG_FILE" > /dev/null 2>&1; then
    report_warning "Using 'latest' tag for Docker images - consider pinning to specific versions"
else
    report_success "No 'latest' tags found in image definitions"
fi

# Check for protected branch deployments
if grep -E "only:.*- main|only:.*- master" "$CONFIG_FILE" > /dev/null 2>&1; then
    report_success "Production deployments appear to be restricted to main/master branch"
fi

# Check for manual deployments to production
if grep -B 10 "environment:.*production" "$CONFIG_FILE" | grep -q "when: manual"; then
    report_success "Production deployments require manual approval"
else
    if grep -q "environment:.*production" "$CONFIG_FILE"; then
        report_warning "Production deployment does not require manual approval"
    fi
fi
echo ""

# 5. Check for performance optimizations
echo -e "${BLUE}5. Checking performance optimizations...${NC}"

# Check for caching
if grep -q "^cache:" "$CONFIG_FILE" || grep -q "  cache:" "$CONFIG_FILE"; then
    report_success "Caching configured"
else
    report_warning "No caching configured - consider adding cache for dependencies"
fi

# Check for FastZip
if grep -q "FF_USE_FASTZIP" "$CONFIG_FILE"; then
    report_success "FastZip compression enabled"
else
    report_warning "Consider enabling FF_USE_FASTZIP for faster artifact handling"
fi

# Check for artifacts expiration
if grep -q "expire_in:" "$CONFIG_FILE"; then
    report_success "Artifact expiration configured"
else
    report_warning "No artifact expiration set - artifacts will be kept indefinitely"
fi

# Check for parallel jobs
if grep -q "parallel:" "$CONFIG_FILE"; then
    report_success "Parallel job execution configured"
fi
echo ""

# 6. Check for proper stage usage
echo -e "${BLUE}6. Checking stage definitions...${NC}"

# Extract defined stages
if grep -q "^stages:" "$CONFIG_FILE"; then
    DEFINED_STAGES=$(sed -n '/^stages:/,/^[a-zA-Z]/p' "$CONFIG_FILE" | grep "  -" | sed 's/.*- //' | tr '\n' ' ')
    report_success "Defined stages: $DEFINED_STAGES"

    # Check if jobs reference undefined stages
    # This is a simplified check and may have false positives
    USED_STAGES=$(grep "  stage:" "$CONFIG_FILE" | sed 's/.*stage: //' | sort -u | tr '\n' ' ')
    report_success "Used stages: $USED_STAGES"
fi
echo ""

# 7. GitLab CI Lint API validation (optional)
echo -e "${BLUE}7. GitLab CI Lint API validation...${NC}"

if [ -n "$CI_PROJECT_ID" ] && [ -n "$CI_JOB_TOKEN" ]; then
    echo "Running GitLab CI Lint API validation..."
    RESPONSE=$(curl -s --header "Content-Type: application/json" \
        --header "PRIVATE-TOKEN: $CI_JOB_TOKEN" \
        --data "{\"content\": $(jq -Rs . < "$CONFIG_FILE")}" \
        "$CI_API_V4_URL/projects/$CI_PROJECT_ID/ci/lint")

    VALID=$(echo "$RESPONSE" | jq -r '.valid')
    if [ "$VALID" = "true" ]; then
        report_success "GitLab CI Lint API validation passed"
    else
        ERRORS_JSON=$(echo "$RESPONSE" | jq -r '.errors[]')
        report_error "GitLab CI Lint API validation failed: $ERRORS_JSON"
    fi
else
    report_warning "GitLab CI Lint API validation skipped (not running in GitLab CI)"
    echo "           To validate online, visit: https://gitlab.com/[your-project]/-/ci/lint"
fi
echo ""

# 8. Check for Docker-in-Docker configuration
echo -e "${BLUE}8. Checking Docker configuration...${NC}"

if grep -q "docker:.*-dind" "$CONFIG_FILE"; then
    if grep -q "DOCKER_TLS_CERTDIR" "$CONFIG_FILE"; then
        report_success "Docker-in-Docker with TLS properly configured"
    else
        report_warning "Docker-in-Docker found but DOCKER_TLS_CERTDIR not set"
    fi

    if grep -q "DOCKER_DRIVER.*overlay2" "$CONFIG_FILE"; then
        report_success "Docker overlay2 driver configured"
    fi
fi
echo ""

# 9. Summary
echo -e "${BLUE}========================================${NC}"
echo -e "${BLUE}Validation Summary${NC}"
echo -e "${BLUE}========================================${NC}"
echo ""

if [ $ERRORS -eq 0 ] && [ $WARNINGS -eq 0 ]; then
    echo -e "${GREEN}✓ Configuration is valid with no issues!${NC}"
    exit 0
elif [ $ERRORS -eq 0 ]; then
    echo -e "${YELLOW}⚠ Configuration is valid but has $WARNINGS warning(s)${NC}"
    echo -e "${YELLOW}  Review warnings above and consider addressing them.${NC}"
    exit 0
else
    echo -e "${RED}✗ Configuration has $ERRORS error(s) and $WARNINGS warning(s)${NC}"
    echo -e "${RED}  Fix errors before using this configuration.${NC}"
    exit 1
fi
