#!/bin/bash
set -e

# add-service.sh - Fully automated service addition for zero-trust WebAuthn stack
# Generated by @vmenon25/mcp-server-webauthn-client
#
# This script automates:
# - Service directory scaffolding
# - mTLS certificate generation
# - Envoy sidecar configuration
# - docker-compose.yml updates (automated with yq)
# - envoy-gateway.yaml routing (automated with yq)

# Check for yq (required for YAML manipulation)
if ! command -v yq &> /dev/null; then
    echo "❌ Error: yq is required but not installed"
    echo ""
    echo "📦 Install yq v4+:"
    echo ""
    echo "   Using Homebrew:"
    echo "     brew install yq"
    echo ""
    echo "   Using wget (Linux):"
    echo "     wget https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 -O /tmp/yq"
    echo "     sudo mv /tmp/yq /usr/local/bin/yq && sudo chmod +x /usr/local/bin/yq"
    echo ""
    echo "   Using wget (macOS):"
    echo "     wget https://github.com/mikefarah/yq/releases/latest/download/yq_darwin_amd64 -O /tmp/yq"
    echo "     sudo mv /tmp/yq /usr/local/bin/yq && sudo chmod +x /usr/local/bin/yq"
    echo ""
    echo "   See all options:"
    echo "     https://github.com/mikefarah/yq#install"
    echo ""
    echo "Then re-run this script: $0 $*"
    exit 1
fi

# Default values
APP_PORT=""
ROUTE_PREFIX=""
SERVICE_NAME=""

# Show help
show_help() {
    cat <<EOF
add-service.sh - Add new services to zero-trust WebAuthn stack

Usage: ./scripts/add-service.sh <service-name> [OPTIONS]

Arguments:
  service-name              Name of the service (alphanumeric and hyphens only)

Options:
  -p, --app-port <port>     Application port (default: auto-detect next available)
  -r, --route <prefix>      API route prefix (default: /api/<service-name>)
  -h, --help                Show this help message

Examples:
  # Auto-detect port (finds next available: 9002, 9003, etc.)
  ./scripts/add-service.sh my-service

  # Specify custom port
  ./scripts/add-service.sh my-service --app-port 9005

  # Custom route prefix
  ./scripts/add-service.sh billing-api --route /api/billing

  # Both custom port and route
  ./scripts/add-service.sh payment-svc -p 9010 -r /api/payments

EOF
}

# Parse arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        -p|--app-port)
            APP_PORT="$2"
            shift 2
            ;;
        -r|--route)
            ROUTE_PREFIX="$2"
            shift 2
            ;;
        -h|--help)
            show_help
            exit 0
            ;;
        -*)
            echo "❌ Error: Unknown option $1"
            echo ""
            show_help
            exit 1
            ;;
        *)
            if [ -z "$SERVICE_NAME" ]; then
                SERVICE_NAME="$1"
            else
                echo "❌ Error: Multiple service names provided: '$SERVICE_NAME' and '$1'"
                exit 1
            fi
            shift
            ;;
    esac
done

# Validate service name required
if [ -z "$SERVICE_NAME" ]; then
    echo "❌ Error: Service name is required"
    echo ""
    show_help
    exit 1
fi

# Validate service name format
if ! echo "$SERVICE_NAME" | grep -qE '^[a-zA-Z0-9-]+$'; then
    echo "❌ Error: Service name must contain only alphanumeric characters and hyphens"
    exit 1
fi

# Function to auto-detect next available APP_PORT
detect_next_app_port() {
    local compose_file="docker/docker-compose.yml"

    if [ ! -f "$compose_file" ]; then
        echo "9001"
        return
    fi

    # Extract all APP_PORT values using yq
    local ports=$(yq eval '.. | select(has("APP_PORT")) | .APP_PORT' "$compose_file" 2>/dev/null | sort -n)

    if [ -z "$ports" ]; then
        echo "9001"
        return
    fi

    # Find highest port and increment
    local max_port=$(echo "$ports" | tail -1)
    echo $((max_port + 1))
}

# Auto-detect port if not specified
if [ -z "$APP_PORT" ]; then
    APP_PORT=$(detect_next_app_port)
    echo "📊 Auto-detected next available port: $APP_PORT"
fi

# Validate APP_PORT
if ! [[ "$APP_PORT" =~ ^[0-9]+$ ]] || [ "$APP_PORT" -lt 1 ] || [ "$APP_PORT" -gt 65535 ]; then
    echo "❌ Error: Invalid port '$APP_PORT' (must be 1-65535)"
    exit 1
fi

# Check for port conflicts
COMPOSE_FILE="docker/docker-compose.yml"
if [ -f "$COMPOSE_FILE" ]; then
    if yq eval ".. | select(has(\"APP_PORT\")) | select(.APP_PORT == ${APP_PORT})" "$COMPOSE_FILE" 2>/dev/null | grep -q "APP_PORT"; then
        echo "❌ Error: Port $APP_PORT is already in use by another service"
        echo "   Use --app-port to specify a different port"
        exit 1
    fi
fi

# Set default route prefix
if [ -z "$ROUTE_PREFIX" ]; then
    ROUTE_PREFIX="/api/${SERVICE_NAME}"
fi

# Validate route prefix format
if ! [[ "$ROUTE_PREFIX" =~ ^/api/ ]]; then
    echo "❌ Error: Route prefix must start with /api/"
    exit 1
fi

echo "🚀 Adding new service: $SERVICE_NAME"
echo ""

# Check if example-service exists
if [ ! -d "example-service" ]; then
    echo "❌ Error: example-service directory not found"
    echo "   Make sure you're running this script from the project root"
    exit 1
fi

# Check if service already exists
if [ -d "$SERVICE_NAME" ]; then
    echo "❌ Error: Service '$SERVICE_NAME' already exists"
    exit 1
fi

# Step 1: Copy example-service as template
echo "📋 Step 1: Copying example-service template..."
cp -r example-service "$SERVICE_NAME"
echo "   ✅ Created $SERVICE_NAME/ directory"
echo ""

# Step 2: Generate mTLS certificates
echo "🔐 Step 2: Generating mTLS certificates..."

CERTS_DIR="docker/certs"
if [ ! -d "$CERTS_DIR" ]; then
    echo "❌ Error: docker/certs directory not found"
    exit 1
fi

cd "$CERTS_DIR"

# Check if CA exists
if [ ! -f "ca-cert.pem" ] || [ ! -f "ca-key.pem" ]; then
    echo "❌ Error: CA certificates not found in docker/certs/"
    echo "   Make sure ca-cert.pem and ca-key.pem exist"
    exit 1
fi

# Generate service key and CSR
echo "   Generating private key and CSR..."
openssl req -newkey rsa:2048 -nodes \
    -keyout "${SERVICE_NAME}-key.pem" \
    -out "${SERVICE_NAME}-csr.pem" \
    -subj "/CN=${SERVICE_NAME}/O=WebAuthn/OU=Services" 2>/dev/null

# Sign with CA
echo "   Signing certificate with CA..."
openssl x509 -req \
    -in "${SERVICE_NAME}-csr.pem" \
    -CA ca-cert.pem \
    -CAkey ca-key.pem \
    -CAcreateserial \
    -out "${SERVICE_NAME}-cert.pem" \
    -days 365 2>/dev/null

# Cleanup CSR
rm "${SERVICE_NAME}-csr.pem"

echo "   ✅ Generated certificates:"
echo "      - ${SERVICE_NAME}-cert.pem (certificate)"
echo "      - ${SERVICE_NAME}-key.pem (private key)"
echo ""

cd ../..

# Step 3: Create Envoy sidecar configuration
echo "⚙️  Step 3: Creating Envoy sidecar configuration..."

SIDECAR_CONFIG="docker/istio/${SERVICE_NAME}-envoy.yaml"
cp docker/istio/example-service-envoy.yaml "$SIDECAR_CONFIG"

# Update certificate paths in sidecar config (portable across all platforms)
sed "s|service-cert.pem|${SERVICE_NAME}-cert.pem|g; s|service-key.pem|${SERVICE_NAME}-key.pem|g" \
  "$SIDECAR_CONFIG" > "${SIDECAR_CONFIG}.tmp"
mv "${SIDECAR_CONFIG}.tmp" "$SIDECAR_CONFIG"

echo "   ✅ Created $SIDECAR_CONFIG"
echo ""

# Step 4: Add service to docker-compose.yml
echo "📝 Step 4: Adding service to docker-compose.yml..."

# Backup original file
cp "$COMPOSE_FILE" "${COMPOSE_FILE}.backup"

# Add sidecar service
yq eval ".services.\"${SERVICE_NAME}-sidecar\" = {
  \"image\": \"envoyproxy/envoy:v1.29-latest\",
  \"container_name\": \"${SERVICE_NAME}-sidecar\",
  \"volumes\": [
    \"./istio/${SERVICE_NAME}-envoy.yaml:/etc/envoy/envoy.yaml:ro\",
    \"./certs:/etc/certs:ro\"
  ],
  \"command\": [\"-c\", \"/etc/envoy/envoy.yaml\", \"--service-cluster\", \"${SERVICE_NAME}\"],
  \"restart\": \"unless-stopped\"
}" -i "$COMPOSE_FILE"

# Add application service
yq eval ".services.\"${SERVICE_NAME}\" = {
  \"build\": {
    \"context\": \"../${SERVICE_NAME}\",
    \"dockerfile\": \"Dockerfile\"
  },
  \"container_name\": \"${SERVICE_NAME}\",
  \"network_mode\": \"service:${SERVICE_NAME}-sidecar\",
  \"environment\": {
    \"APP_PORT\": ${APP_PORT},
    \"WEBAUTHN_PUBLIC_KEY_URL\": \"http://webauthn-server:8080/public-key\"
  },
  \"depends_on\": [
    \"webauthn-server\",
    \"${SERVICE_NAME}-sidecar\"
  ],
  \"restart\": \"unless-stopped\"
}" -i "$COMPOSE_FILE"

# Update envoy-gateway dependencies
yq eval ".services.\"envoy-gateway\".depends_on += [\"${SERVICE_NAME}-sidecar\"]" -i "$COMPOSE_FILE"

echo "   ✅ Added to docker-compose.yml (APP_PORT: ${APP_PORT})"
echo ""

# Step 5: Add routing to envoy-gateway.yaml
echo "📝 Step 5: Adding routing to envoy-gateway.yaml..."

ENVOY_FILE="docker/envoy-gateway.yaml"

# Backup original file
cp "$ENVOY_FILE" "${ENVOY_FILE}.backup"

# Add route (simple append - no catch-all to worry about!)
ROUTE_PATH=".static_resources.listeners[0].filter_chains[0].filters[0].typed_config.route_config.virtual_hosts[0].routes"

yq eval "${ROUTE_PATH} += [{
  \"match\": {\"prefix\": \"${ROUTE_PREFIX}\"},
  \"route\": {\"cluster\": \"${SERVICE_NAME}\"}
}]" -i "$ENVOY_FILE"

# Add cluster with full mTLS configuration
yq eval ".static_resources.clusters += [{
  \"name\": \"${SERVICE_NAME}\",
  \"connect_timeout\": \"0.25s\",
  \"type\": \"STRICT_DNS\",
  \"lb_policy\": \"ROUND_ROBIN\",
  \"load_assignment\": {
    \"cluster_name\": \"${SERVICE_NAME}\",
    \"endpoints\": [{
      \"lb_endpoints\": [{
        \"endpoint\": {
          \"address\": {
            \"socket_address\": {
              \"address\": \"${SERVICE_NAME}\",
              \"port_value\": 9000
            }
          }
        }
      }]
    }]
  },
  \"transport_socket\": {
    \"name\": \"envoy.transport_sockets.tls\",
    \"typed_config\": {
      \"@type\": \"type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.UpstreamTlsContext\",
      \"common_tls_context\": {
        \"tls_certificates\": [{
          \"certificate_chain\": {\"filename\": \"/etc/envoy/certs/gateway-cert.pem\"},
          \"private_key\": {\"filename\": \"/etc/envoy/certs/gateway-key.pem\"}
        }],
        \"validation_context\": {
          \"trusted_ca\": {\"filename\": \"/etc/envoy/certs/ca-cert.pem\"}
        }
      }
    }
  }
}]" -i "$ENVOY_FILE"

echo "   ✅ Added route: ${ROUTE_PREFIX} → ${SERVICE_NAME} cluster"
echo "   ✅ Added cluster: ${SERVICE_NAME} (port 9000 with mTLS)"
echo ""

# Final summary
echo "✅ Service '$SERVICE_NAME' added successfully!"
echo ""
echo "📋 Configuration Summary:"
echo "   Service Name:    $SERVICE_NAME"
echo "   APP_PORT:        $APP_PORT"
echo "   Route:           ${ROUTE_PREFIX}"
echo "   Sidecar Port:    9000 (mTLS)"
echo "   Certificates:    docker/certs/${SERVICE_NAME}-{cert,key}.pem"
echo ""
echo "🚀 Next Steps:"
echo ""
echo "   1. Customize your service code:"
echo "      cd ${SERVICE_NAME}"
echo "      # Edit main.py or create your own application"
echo "      # Ensure app listens on 127.0.0.1:\$APP_PORT"
echo ""
echo "   2. Build and start your service:"
echo "      cd docker"
echo "      docker compose up -d ${SERVICE_NAME}"
echo ""
echo "   3. Test your service:"
echo "      # Get JWT token first (register + authenticate via web client)"
echo "      curl -H \"Authorization: Bearer \$TOKEN\" \\"
echo "        http://localhost:8000${ROUTE_PREFIX}/your-endpoint"
echo ""
echo "💾 Backups created:"
echo "   - docker/docker-compose.yml.backup"
echo "   - docker/envoy-gateway.yaml.backup"
echo ""
echo "📚 For detailed integration guide, see:"
echo "   docs/INTEGRATION.md"
echo ""
