#!/bin/bash

# ═══════════════════════════════════════════════════════════
# 🔧 OPHION Universal Instrumentation Script
# Auto-instrument any containerized application
# ═══════════════════════════════════════════════════════════
#
# Usage:
#   ./instrument.sh <container_name> [language] [ophion_server]
#
# Examples:
#   ./instrument.sh myapp                    # Auto-detect language
#   ./instrument.sh myapp nodejs             # Explicit Node.js
#   ./instrument.sh myapp python http://ophion:4318
#
# Supported languages: dotnet, nodejs, python, java, go, php
# ═══════════════════════════════════════════════════════════

set -e

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

print_banner() {
    echo -e "${BLUE}"
    echo "╔═══════════════════════════════════════════════════════════╗"
    echo "║         🐍 OPHION - Universal Instrumentation             ║"
    echo "╚═══════════════════════════════════════════════════════════╝"
    echo -e "${NC}"
}

log_info() { echo -e "${BLUE}[INFO]${NC} $1"; }
log_success() { echo -e "${GREEN}[SUCCESS]${NC} $1"; }
log_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
log_error() { echo -e "${RED}[ERROR]${NC} $1"; }

# Configuration
CONTAINER_NAME="${1:-}"
LANGUAGE="${2:-auto}"
OPHION_SERVER="${3:-http://ophion-otel-collector:4318}"
OTEL_VOLUME="ophion-otel-agents"

if [[ -z "$CONTAINER_NAME" ]]; then
    print_banner
    echo "Usage: $0 <container_name> [language] [ophion_server]"
    echo ""
    echo "Arguments:"
    echo "  container_name  Name or ID of the Docker container"
    echo "  language        One of: dotnet, nodejs, python, java, go, php, auto (default)"
    echo "  ophion_server   OTLP endpoint (default: http://ophion-otel-collector:4318)"
    echo ""
    echo "Examples:"
    echo "  $0 myapp"
    echo "  $0 myapp nodejs"
    echo "  $0 myapp python http://localhost:4318"
    exit 1
fi

print_banner

# Check if container exists
if ! docker inspect "$CONTAINER_NAME" &>/dev/null; then
    log_error "Container '$CONTAINER_NAME' not found"
    exit 1
fi

log_info "Container: $CONTAINER_NAME"

# Detect language if auto
detect_language() {
    local container="$1"
    
    log_info "Auto-detecting language..."
    
    # Check running processes
    local processes
    processes=$(docker exec "$container" ps aux 2>/dev/null || echo "")
    
    # Check for specific runtimes
    if echo "$processes" | grep -qE "dotnet|aspnet"; then
        echo "dotnet"
    elif echo "$processes" | grep -qE "node|npm|yarn"; then
        echo "nodejs"
    elif echo "$processes" | grep -qE "python|gunicorn|uvicorn|flask|django"; then
        echo "python"
    elif echo "$processes" | grep -qE "java|jvm"; then
        echo "java"
    elif docker exec "$container" test -f /usr/local/bin/php 2>/dev/null; then
        echo "php"
    else
        # Check for common files
        if docker exec "$container" test -f package.json 2>/dev/null; then
            echo "nodejs"
        elif docker exec "$container" test -f requirements.txt 2>/dev/null || \
             docker exec "$container" test -f pyproject.toml 2>/dev/null; then
            echo "python"
        elif docker exec "$container" test -f pom.xml 2>/dev/null || \
             docker exec "$container" test -f build.gradle 2>/dev/null; then
            echo "java"
        elif docker exec "$container" find / -name "*.csproj" 2>/dev/null | head -1 | grep -q .; then
            echo "dotnet"
        elif docker exec "$container" test -f composer.json 2>/dev/null; then
            echo "php"
        elif docker exec "$container" test -f go.mod 2>/dev/null; then
            echo "go"
        else
            echo "unknown"
        fi
    fi
}

if [[ "$LANGUAGE" == "auto" ]]; then
    LANGUAGE=$(detect_language "$CONTAINER_NAME")
    if [[ "$LANGUAGE" == "unknown" ]]; then
        log_error "Could not auto-detect language. Please specify: dotnet, nodejs, python, java, go, php"
        exit 1
    fi
    log_success "Detected language: $LANGUAGE"
fi

# Ensure OTEL volume exists
if ! docker volume inspect "$OTEL_VOLUME" &>/dev/null; then
    log_warn "OTEL agents volume not found. Creating..."
    docker run --rm -v "$OTEL_VOLUME":/otel ophion-instrumentation 2>/dev/null || {
        log_error "Failed to create OTEL agents volume. Run 'docker compose up instrumentation' first."
        exit 1
    }
fi

# Get container image to determine if it's Alpine (musl) or glibc
IS_ALPINE=false
if docker exec "$CONTAINER_NAME" cat /etc/os-release 2>/dev/null | grep -qi alpine; then
    IS_ALPINE=true
fi

# Service name from container name
SERVICE_NAME="${CONTAINER_NAME//-/_}"

log_info "Language: $LANGUAGE"
log_info "OTLP Endpoint: $OPHION_SERVER"
log_info "Service Name: $SERVICE_NAME"

# Common environment variables
COMMON_ENV=(
    -e "OTEL_EXPORTER_OTLP_ENDPOINT=$OPHION_SERVER"
    -e "OTEL_SERVICE_NAME=$SERVICE_NAME"
    -e "OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production,service.namespace=ophion"
    -e "OTEL_TRACES_EXPORTER=otlp"
    -e "OTEL_METRICS_EXPORTER=otlp"
    -e "OTEL_LOGS_EXPORTER=otlp"
)

generate_docker_compose() {
    local lang="$1"
    local output_file="docker-compose.instrumented.yml"
    
    cat > "$output_file" << EOF
# Auto-generated by OPHION instrument.sh
# Add these settings to your docker-compose.yml

version: '3.8'

services:
  ${CONTAINER_NAME}:
    # Add these environment variables:
    environment:
      - OTEL_EXPORTER_OTLP_ENDPOINT=${OPHION_SERVER}
      - OTEL_SERVICE_NAME=${SERVICE_NAME}
      - OTEL_RESOURCE_ATTRIBUTES=deployment.environment=production
EOF

    case "$lang" in
        dotnet)
            local dotnet_path="/otel/dotnet"
            [[ "$IS_ALPINE" == true ]] && dotnet_path="/otel/dotnet-musl"
            cat >> "$output_file" << EOF
      - CORECLR_ENABLE_PROFILING=1
      - CORECLR_PROFILER={918728DD-259F-4A6A-AC2B-B85E1B658571}
      - CORECLR_PROFILER_PATH=${dotnet_path}/linux-x64/OpenTelemetry.AutoInstrumentation.Native.so
      - DOTNET_ADDITIONAL_DEPS=${dotnet_path}/AdditionalDeps
      - DOTNET_SHARED_STORE=${dotnet_path}/store
      - DOTNET_STARTUP_HOOKS=${dotnet_path}/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll
      - OTEL_DOTNET_AUTO_HOME=${dotnet_path}
    volumes:
      - ophion-otel-agents:/otel:ro
EOF
            ;;
        nodejs)
            cat >> "$output_file" << EOF
      - NODE_OPTIONS=--require /otel/nodejs/instrument.js
    volumes:
      - ophion-otel-agents:/otel:ro
    # Note: Run 'npm install' in /otel/nodejs first or copy package.json deps
EOF
            ;;
        python)
            cat >> "$output_file" << EOF
    # For Python, modify your entrypoint:
    # command: opentelemetry-instrument python your-app.py
    # Or use PYTHONPATH
    volumes:
      - ophion-otel-agents:/otel:ro
    # Run: pip install opentelemetry-distro opentelemetry-exporter-otlp && opentelemetry-bootstrap -a install
EOF
            ;;
        java)
            cat >> "$output_file" << EOF
      - JAVA_TOOL_OPTIONS=-javaagent:/otel/java/opentelemetry-javaagent.jar
    volumes:
      - ophion-otel-agents:/otel:ro
EOF
            ;;
        php)
            cat >> "$output_file" << EOF
    # For PHP, add to your composer.json:
    # "require": { "open-telemetry/sdk": "^1.0", "open-telemetry/exporter-otlp": "^1.0" }
    # And include /otel/php/otel_init.php in your bootstrap
    volumes:
      - ophion-otel-agents:/otel:ro
EOF
            ;;
        go)
            cat >> "$output_file" << EOF
    # For Go, instrumentation is compile-time.
    # See /otel/go/init.go.example for code to add to your application.
    # No runtime volumes needed.
EOF
            ;;
    esac

    cat >> "$output_file" << EOF

networks:
  default:
    external: true
    name: ophion-network

volumes:
  ophion-otel-agents:
    external: true
EOF

    log_success "Generated $output_file"
}

# Apply instrumentation based on language
case "$LANGUAGE" in
    dotnet)
        log_info "Applying .NET auto-instrumentation..."
        DOTNET_PATH="/otel/dotnet"
        [[ "$IS_ALPINE" == true ]] && DOTNET_PATH="/otel/dotnet-musl"
        
        cat << EOF

📋 Add these environment variables to your container:

docker run \\
  -e CORECLR_ENABLE_PROFILING=1 \\
  -e CORECLR_PROFILER={918728DD-259F-4A6A-AC2B-B85E1B658571} \\
  -e CORECLR_PROFILER_PATH=${DOTNET_PATH}/linux-x64/OpenTelemetry.AutoInstrumentation.Native.so \\
  -e DOTNET_ADDITIONAL_DEPS=${DOTNET_PATH}/AdditionalDeps \\
  -e DOTNET_SHARED_STORE=${DOTNET_PATH}/store \\
  -e DOTNET_STARTUP_HOOKS=${DOTNET_PATH}/net/OpenTelemetry.AutoInstrumentation.StartupHook.dll \\
  -e OTEL_DOTNET_AUTO_HOME=${DOTNET_PATH} \\
  -e OTEL_EXPORTER_OTLP_ENDPOINT=${OPHION_SERVER} \\
  -e OTEL_SERVICE_NAME=${SERVICE_NAME} \\
  -v ${OTEL_VOLUME}:/otel:ro \\
  --network ophion-network \\
  your-image

EOF
        ;;
        
    nodejs)
        log_info "Applying Node.js auto-instrumentation..."
        cat << EOF

📋 Add these to your container:

# Option 1: NODE_OPTIONS (recommended)
docker run \\
  -e NODE_OPTIONS="--require /otel/nodejs/instrument.js" \\
  -e OTEL_EXPORTER_OTLP_ENDPOINT=${OPHION_SERVER} \\
  -e OTEL_SERVICE_NAME=${SERVICE_NAME} \\
  -v ${OTEL_VOLUME}:/otel:ro \\
  --network ophion-network \\
  your-image

# Option 2: Modify your package.json start script
# "start": "node -r /otel/nodejs/instrument.js your-app.js"

# Note: First install deps: cd /otel/nodejs && npm install

EOF
        ;;
        
    python)
        log_info "Applying Python auto-instrumentation..."
        cat << EOF

📋 Add these to your container:

# First, install packages in your Dockerfile:
# RUN pip install opentelemetry-distro opentelemetry-exporter-otlp && \\
#     opentelemetry-bootstrap -a install

# Then run with:
docker run \\
  -e OTEL_EXPORTER_OTLP_ENDPOINT=${OPHION_SERVER} \\
  -e OTEL_SERVICE_NAME=${SERVICE_NAME} \\
  --network ophion-network \\
  your-image \\
  opentelemetry-instrument python your-app.py

# Or use OTEL_PYTHON_CONFIGURATOR=true in your Dockerfile

EOF
        ;;
        
    java)
        log_info "Applying Java auto-instrumentation..."
        cat << EOF

📋 Add these to your container:

docker run \\
  -e JAVA_TOOL_OPTIONS="-javaagent:/otel/java/opentelemetry-javaagent.jar" \\
  -e OTEL_EXPORTER_OTLP_ENDPOINT=${OPHION_SERVER} \\
  -e OTEL_SERVICE_NAME=${SERVICE_NAME} \\
  -v ${OTEL_VOLUME}:/otel:ro \\
  --network ophion-network \\
  your-image

EOF
        ;;
        
    go)
        log_info "Go instrumentation requires code changes..."
        cat << EOF

📋 Go requires compile-time instrumentation. Add this code:

1. Add dependencies:
   go get go.opentelemetry.io/otel
   go get go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp
   go get go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp

2. See example code: /otel/go/init.go.example

3. Set environment:
   OTEL_EXPORTER_OTLP_ENDPOINT=${OPHION_SERVER}
   OTEL_SERVICE_NAME=${SERVICE_NAME}

EOF
        ;;
        
    php)
        log_info "Applying PHP auto-instrumentation..."
        cat << EOF

📋 Add to your PHP application:

1. Add to composer.json:
   {
     "require": {
       "open-telemetry/sdk": "^1.0",
       "open-telemetry/exporter-otlp": "^1.0",
       "php-http/guzzle7-adapter": "^1.0"
     }
   }

2. Run: composer require open-telemetry/sdk open-telemetry/exporter-otlp

3. Include in bootstrap:
   require_once '/otel/php/otel_init.php';

4. Set environment:
   OTEL_EXPORTER_OTLP_ENDPOINT=${OPHION_SERVER}
   OTEL_SERVICE_NAME=${SERVICE_NAME}

EOF
        ;;
        
    *)
        log_error "Unsupported language: $LANGUAGE"
        exit 1
        ;;
esac

# Generate docker-compose snippet
generate_docker_compose "$LANGUAGE"

echo ""
log_success "Instrumentation configuration generated!"
log_info "See docker-compose.instrumented.yml for complete configuration"
echo ""
echo -e "${GREEN}Next steps:${NC}"
echo "1. Apply the environment variables to your container"
echo "2. Ensure container is on the 'ophion-network' network"
echo "3. Restart your container"
echo "4. Check traces at http://localhost:3000 (Ophion Dashboard)"
