#!/bin/bash
# SASI Neural Agent System - Deployment Automation Script
# Version: 2.0.0-phase2b
# Generated by: Documentation Specialist Agent

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
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
LOG_FILE="$PROJECT_ROOT/logs/deployment-$(date +%Y%m%d-%H%M%S).log"

# Ensure logs directory exists
mkdir -p "$PROJECT_ROOT/logs"

# Logging function
log() {
    local level=$1
    shift
    local message="$@"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
    echo -e "$timestamp [$level] $message" | tee -a "$LOG_FILE"
}

info() {
    log "INFO" "${BLUE}$@${NC}"
}

warn() {
    log "WARN" "${YELLOW}$@${NC}"
}

error() {
    log "ERROR" "${RED}$@${NC}"
}

success() {
    log "SUCCESS" "${GREEN}$@${NC}"
}

# Usage function
usage() {
    cat << EOF
SASI Neural Agent System - Deployment Automation

Usage: $0 [OPTIONS] ENVIRONMENT

ENVIRONMENTS:
    development     Deploy to development environment
    staging         Deploy to staging environment
    production      Deploy to production environment

OPTIONS:
    -h, --help          Show this help message
    -v, --version       Show version information
    -f, --force         Force deployment (skip confirmations)
    -d, --dry-run       Show what would be deployed without executing
    -s, --skip-tests    Skip pre-deployment tests
    -b, --backup        Create backup before deployment
    --rollback          Rollback to previous version
    --health-check      Perform health check only
    --build-docs        Build documentation only
    --config FILE       Use custom configuration file

EXAMPLES:
    $0 staging                          # Deploy to staging
    $0 production --backup              # Deploy to production with backup
    $0 staging --dry-run                # Show staging deployment plan
    $0 production --rollback            # Rollback production deployment
    $0 --health-check staging           # Check staging environment health

ENVIRONMENT VARIABLES:
    SASI_ENV            Environment name (overrides argument)
    SASI_CONFIG         Path to configuration file
    DOCKER_REGISTRY     Docker registry URL
    GITHUB_TOKEN        GitHub authentication token
    SLACK_WEBHOOK       Slack notification webhook
    DRY_RUN            Set to 'true' for dry run mode

For more information, see: docs/DEPLOYMENT_GUIDE.md
EOF
}

# Version function
version() {
    echo "SASI Deployment Automation v2.0.0-phase2b"
    echo "Generated by: Documentation Specialist Agent"
    echo "Compatible with: SASI Neural Agent System 2.0.0+"
}

# Parse command line arguments
ENVIRONMENT=""
FORCE=false
DRY_RUN=${DRY_RUN:-false}
SKIP_TESTS=false
BACKUP=false
ROLLBACK=false
HEALTH_CHECK_ONLY=false
BUILD_DOCS_ONLY=false
CONFIG_FILE=""

while [[ $# -gt 0 ]]; do
    case $1 in
        -h|--help)
            usage
            exit 0
            ;;
        -v|--version)
            version
            exit 0
            ;;
        -f|--force)
            FORCE=true
            shift
            ;;
        -d|--dry-run)
            DRY_RUN=true
            shift
            ;;
        -s|--skip-tests)
            SKIP_TESTS=true
            shift
            ;;
        -b|--backup)
            BACKUP=true
            shift
            ;;
        --rollback)
            ROLLBACK=true
            shift
            ;;
        --health-check)
            HEALTH_CHECK_ONLY=true
            shift
            ;;
        --build-docs)
            BUILD_DOCS_ONLY=true
            shift
            ;;
        --config)
            CONFIG_FILE="$2"
            shift 2
            ;;
        development|staging|production)
            ENVIRONMENT="$1"
            shift
            ;;
        *)
            error "Unknown option: $1"
            usage
            exit 1
            ;;
    esac
done

# Use environment variable if no argument provided
ENVIRONMENT=${SASI_ENV:-$ENVIRONMENT}

# Validate environment
if [[ -z "$ENVIRONMENT" && "$HEALTH_CHECK_ONLY" == false && "$BUILD_DOCS_ONLY" == false ]]; then
    error "Environment is required"
    usage
    exit 1
fi

if [[ "$ENVIRONMENT" != "development" && "$ENVIRONMENT" != "staging" && "$ENVIRONMENT" != "production" && -n "$ENVIRONMENT" ]]; then
    error "Invalid environment: $ENVIRONMENT"
    exit 1
fi

# Load configuration
load_config() {
    local config_file=""
    
    if [[ -n "$CONFIG_FILE" ]]; then
        config_file="$CONFIG_FILE"
    elif [[ -f "$PROJECT_ROOT/config/deploy-$ENVIRONMENT.conf" ]]; then
        config_file="$PROJECT_ROOT/config/deploy-$ENVIRONMENT.conf"
    elif [[ -f "$PROJECT_ROOT/config/deploy.conf" ]]; then
        config_file="$PROJECT_ROOT/config/deploy.conf"
    fi
    
    if [[ -n "$config_file" && -f "$config_file" ]]; then
        info "Loading configuration from: $config_file"
        source "$config_file"
    else
        warn "No configuration file found, using defaults"
    fi
}

# Check prerequisites
check_prerequisites() {
    info "Checking prerequisites..."
    
    local missing_tools=()
    
    # Check required tools
    command -v node >/dev/null 2>&1 || missing_tools+=("node")
    command -v npm >/dev/null 2>&1 || missing_tools+=("npm")
    command -v git >/dev/null 2>&1 || missing_tools+=("git")
    command -v curl >/dev/null 2>&1 || missing_tools+=("curl")
    command -v jq >/dev/null 2>&1 || missing_tools+=("jq")
    
    if [[ "$ENVIRONMENT" == "production" || "$ENVIRONMENT" == "staging" ]]; then
        command -v docker >/dev/null 2>&1 || missing_tools+=("docker")
        command -v kubectl >/dev/null 2>&1 || missing_tools+=("kubectl")
    fi
    
    if [[ ${#missing_tools[@]} -gt 0 ]]; then
        error "Missing required tools: ${missing_tools[*]}"
        exit 1
    fi
    
    # Check Node.js version
    local node_version=$(node --version | sed 's/v//')
    local required_version="20.0.0"
    
    if ! npx semver -r ">=$required_version" "$node_version" >/dev/null 2>&1; then
        error "Node.js version $node_version is not supported. Required: >=$required_version"
        exit 1
    fi
    
    success "Prerequisites check passed"
}

# Environment-specific URLs and configurations
get_environment_config() {
    case "$ENVIRONMENT" in
        "development")
            APP_URL="http://localhost:3000"
            DOCKER_TAG="dev"
            NAMESPACE="sasi-dev"
            REPLICAS=1
            ;;
        "staging")
            APP_URL="https://staging.sasi.example.com"
            DOCKER_TAG="staging"
            NAMESPACE="sasi-staging"
            REPLICAS=2
            ;;
        "production")
            APP_URL="https://sasi.example.com"
            DOCKER_TAG="latest"
            NAMESPACE="sasi-production"
            REPLICAS=3
            ;;
    esac
}

# Health check function
perform_health_check() {
    local url="$1"
    local max_attempts=30
    local attempt=0
    
    info "Performing health check on: $url"
    
    while [[ $attempt -lt $max_attempts ]]; do
        if curl -sf "$url/health" >/dev/null 2>&1; then
            local health_response=$(curl -s "$url/health")
            local health_status=$(echo "$health_response" | jq -r '.status // "unknown"')
            
            if [[ "$health_status" == "healthy" ]]; then
                success "Health check passed: $health_status"
                return 0
            else
                warn "Health check returned: $health_status"
            fi
        fi
        
        attempt=$((attempt + 1))
        info "Health check attempt $attempt/$max_attempts failed, retrying in 5 seconds..."
        sleep 5
    done
    
    error "Health check failed after $max_attempts attempts"
    return 1
}

# Build documentation
build_documentation() {
    info "Building documentation..."
    
    if [[ "$DRY_RUN" == true ]]; then
        info "[DRY RUN] Would build documentation"
        return 0
    fi
    
    cd "$PROJECT_ROOT"
    
    # Install dependencies if needed
    if [[ ! -d "node_modules" ]]; then
        info "Installing dependencies..."
        npm ci
    fi
    
    # Build API documentation
    if [[ -f "scripts/build-docs.sh" ]]; then
        info "Building API documentation..."
        chmod +x scripts/build-docs.sh
        ./scripts/build-docs.sh
    else
        warn "Documentation build script not found, skipping"
    fi
    
    success "Documentation build completed"
}

# Run tests
run_tests() {
    if [[ "$SKIP_TESTS" == true ]]; then
        warn "Skipping tests (--skip-tests flag provided)"
        return 0
    fi
    
    info "Running pre-deployment tests..."
    
    if [[ "$DRY_RUN" == true ]]; then
        info "[DRY RUN] Would run test suite"
        return 0
    fi
    
    cd "$PROJECT_ROOT"
    
    # Run different test suites based on environment
    case "$ENVIRONMENT" in
        "development")
            npm run test:unit || { error "Unit tests failed"; return 1; }
            ;;
        "staging")
            npm run test:unit || { error "Unit tests failed"; return 1; }
            npm run test:integration || { error "Integration tests failed"; return 1; }
            ;;
        "production")
            npm run test:unit || { error "Unit tests failed"; return 1; }
            npm run test:integration || { error "Integration tests failed"; return 1; }
            npm run test:security || { error "Security tests failed"; return 1; }
            ;;
    esac
    
    success "Tests passed"
}

# Build application
build_application() {
    info "Building application for $ENVIRONMENT..."
    
    if [[ "$DRY_RUN" == true ]]; then
        info "[DRY RUN] Would build application"
        return 0
    fi
    
    cd "$PROJECT_ROOT"
    
    # Set environment variables
    export NODE_ENV="$ENVIRONMENT"
    export REACT_APP_ENV="$ENVIRONMENT"
    export REACT_APP_API_URL="$APP_URL"
    
    # Build application
    npm run build || { error "Application build failed"; return 1; }
    
    success "Application build completed"
}

# Docker operations
build_docker_image() {
    info "Building Docker image for $ENVIRONMENT..."
    
    if [[ "$DRY_RUN" == true ]]; then
        info "[DRY RUN] Would build Docker image: $DOCKER_REGISTRY/sasi:$DOCKER_TAG"
        return 0
    fi
    
    cd "$PROJECT_ROOT"
    
    local image_tag="${DOCKER_REGISTRY:-ghcr.io/sasi}/sasi:$DOCKER_TAG"
    local git_commit=$(git rev-parse --short HEAD)
    
    # Build Docker image
    docker build \
        --build-arg NODE_ENV="$ENVIRONMENT" \
        --build-arg GIT_COMMIT="$git_commit" \
        --build-arg BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" \
        -t "$image_tag" \
        -f Dockerfile.production \
        . || { error "Docker build failed"; return 1; }
    
    # Push to registry
    if [[ "$ENVIRONMENT" != "development" ]]; then
        info "Pushing Docker image to registry..."
        docker push "$image_tag" || { error "Docker push failed"; return 1; }
    fi
    
    success "Docker image built and pushed: $image_tag"
}

# Kubernetes deployment
deploy_kubernetes() {
    info "Deploying to Kubernetes ($ENVIRONMENT)..."
    
    if [[ "$DRY_RUN" == true ]]; then
        info "[DRY RUN] Would deploy to Kubernetes namespace: $NAMESPACE"
        return 0
    fi
    
    cd "$PROJECT_ROOT"
    
    local k8s_dir="k8s/$ENVIRONMENT"
    
    if [[ ! -d "$k8s_dir" ]]; then
        error "Kubernetes manifests not found: $k8s_dir"
        return 1
    fi
    
    # Create namespace if it doesn't exist
    kubectl create namespace "$NAMESPACE" --dry-run=client -o yaml | kubectl apply -f -
    
    # Apply manifests
    find "$k8s_dir" -name "*.yaml" -exec kubectl apply -f {} -n "$NAMESPACE" \\;
    
    # Wait for deployment to be ready
    info "Waiting for deployment to be ready..."
    kubectl rollout status deployment/sasi-app -n "$NAMESPACE" --timeout=600s || {
        error "Deployment rollout failed"
        return 1
    }
    
    success "Kubernetes deployment completed"
}

# Local deployment
deploy_local() {
    info "Starting local development server..."
    
    if [[ "$DRY_RUN" == true ]]; then
        info "[DRY RUN] Would start local development server"
        return 0
    fi
    
    cd "$PROJECT_ROOT"
    
    # Stop any existing processes
    pkill -f "npm.*start" || true
    pkill -f "node.*server" || true
    
    # Start development server
    npm start &
    local server_pid=$!
    
    info "Development server started (PID: $server_pid)"
    info "Application available at: $APP_URL"
    
    # Save PID for later cleanup
    echo "$server_pid" > "$PROJECT_ROOT/.dev-server.pid"
}

# Create backup
create_backup() {
    if [[ "$BACKUP" != true ]]; then
        return 0
    fi
    
    info "Creating backup before deployment..."
    
    if [[ "$DRY_RUN" == true ]]; then
        info "[DRY RUN] Would create backup"
        return 0
    fi
    
    local backup_dir="$PROJECT_ROOT/backups/$(date +%Y%m%d-%H%M%S)"
    mkdir -p "$backup_dir"
    
    # Backup database
    if [[ -f "$PROJECT_ROOT/data/neural.db" ]]; then
        cp "$PROJECT_ROOT/data/neural.db" "$backup_dir/"
        info "Database backup created: $backup_dir/neural.db"
    fi
    
    # Backup configuration
    if [[ -d "$PROJECT_ROOT/config" ]]; then
        cp -r "$PROJECT_ROOT/config" "$backup_dir/"
        info "Configuration backup created: $backup_dir/config/"
    fi
    
    # Create backup manifest
    cat > "$backup_dir/manifest.json" << EOF
{
  "timestamp": "$(date -u +'%Y-%m-%dT%H:%M:%SZ')",
  "environment": "$ENVIRONMENT",
  "git_commit": "$(git rev-parse HEAD)",
  "git_branch": "$(git rev-parse --abbrev-ref HEAD)",
  "version": "$(npm pkg get version | tr -d '\"')"
}
EOF
    
    success "Backup completed: $backup_dir"
}

# Rollback deployment
rollback_deployment() {
    info "Rolling back $ENVIRONMENT deployment..."
    
    if [[ "$DRY_RUN" == true ]]; then
        info "[DRY RUN] Would rollback deployment"
        return 0
    fi
    
    case "$ENVIRONMENT" in
        "development")
            error "Rollback not supported for development environment"
            return 1
            ;;
        "staging"|"production")
            kubectl rollout undo deployment/sasi-app -n "$NAMESPACE" || {
                error "Rollback failed"
                return 1
            }
            
            kubectl rollout status deployment/sasi-app -n "$NAMESPACE" --timeout=300s || {
                error "Rollback verification failed"
                return 1
            }
            ;;
    esac
    
    success "Rollback completed"
}

# Send notifications
send_notification() {
    local status="$1"
    local message="$2"
    
    if [[ -z "$SLACK_WEBHOOK" ]]; then
        return 0
    fi
    
    local color=""
    local emoji=""
    
    case "$status" in
        "success")
            color="good"
            emoji=":white_check_mark:"
            ;;
        "warning")
            color="warning"
            emoji=":warning:"
            ;;
        "error")
            color="danger"
            emoji=":x:"
            ;;
        *)
            color="good"
            emoji=":information_source:"
            ;;
    esac
    
    local payload=$(cat << EOF
{
  "attachments": [
    {
      "color": "$color",
      "title": "$emoji SASI Deployment Notification",
      "fields": [
        {
          "title": "Environment",
          "value": "$ENVIRONMENT",
          "short": true
        },
        {
          "title": "Status",
          "value": "$status",
          "short": true
        },
        {
          "title": "Message",
          "value": "$message",
          "short": false
        },
        {
          "title": "Timestamp",
          "value": "$(date -u +'%Y-%m-%d %H:%M:%S UTC')",
          "short": true
        }
      ]
    }
  ]
}
EOF
)
    
    curl -X POST "$SLACK_WEBHOOK" \
        -H "Content-Type: application/json" \
        -d "$payload" \
        >/dev/null 2>&1 || warn "Failed to send Slack notification"
}

# Deployment confirmation
confirm_deployment() {
    if [[ "$FORCE" == true ]]; then
        return 0
    fi
    
    if [[ "$ENVIRONMENT" == "production" ]]; then
        warn "You are about to deploy to PRODUCTION environment!"
        warn "This will affect live users and services."
        echo
        read -p "Are you sure you want to continue? (yes/no): " confirmation
        
        if [[ "$confirmation" != "yes" ]]; then
            info "Deployment cancelled by user"
            exit 0
        fi
    elif [[ "$ENVIRONMENT" == "staging" ]]; then
        info "Deploying to staging environment"
        read -p "Continue? (y/N): " confirmation
        
        if [[ "$confirmation" != "y" && "$confirmation" != "Y" ]]; then
            info "Deployment cancelled by user"
            exit 0
        fi
    fi
}

# Smoke tests
run_smoke_tests() {
    info "Running smoke tests..."
    
    if [[ "$DRY_RUN" == true ]]; then
        info "[DRY RUN] Would run smoke tests"
        return 0
    fi
    
    # Wait a bit for the application to fully start
    sleep 10
    
    # Basic health check
    if ! perform_health_check "$APP_URL"; then
        error "Smoke test failed: Health check"
        return 1
    fi
    
    # Test API endpoints
    if ! curl -sf "$APP_URL/api/agents" >/dev/null 2>&1 && ! curl -sf "$APP_URL/api/agents" | grep -q "401"; then
        error "Smoke test failed: API endpoints"
        return 1
    fi
    
    # Test documentation
    if ! curl -sf "$APP_URL/api-docs" >/dev/null 2>&1; then
        warn "Documentation endpoint not accessible (non-critical)"
    fi
    
    success "Smoke tests passed"
}

# Main deployment function
main_deployment() {
    info "Starting SASI deployment to $ENVIRONMENT..."
    send_notification "info" "Deployment started for $ENVIRONMENT"
    
    # Pre-deployment steps
    check_prerequisites
    load_config
    get_environment_config
    confirm_deployment
    create_backup
    
    # Build steps
    build_documentation
    run_tests
    build_application
    
    # Deployment steps
    case "$ENVIRONMENT" in
        "development")
            deploy_local
            ;;
        "staging"|"production")
            build_docker_image
            deploy_kubernetes
            ;;
    esac
    
    # Post-deployment verification
    sleep 5  # Give the application time to start
    
    if perform_health_check "$APP_URL"; then
        run_smoke_tests
        success "Deployment completed successfully!"
        send_notification "success" "Deployment completed successfully for $ENVIRONMENT"
    else
        error "Deployment failed health check"
        send_notification "error" "Deployment failed health check for $ENVIRONMENT"
        
        if [[ "$ENVIRONMENT" != "development" ]]; then
            warn "Consider rolling back the deployment"
        fi
        
        exit 1
    fi
}

# Main execution logic
main() {
    case "${BUILD_DOCS_ONLY}" in
        true)
            info "Building documentation only..."
            build_documentation
            exit 0
            ;;
    esac
    
    case "${HEALTH_CHECK_ONLY}" in
        true)
            info "Performing health check only..."
            get_environment_config
            if perform_health_check "$APP_URL"; then
                success "Health check passed for $ENVIRONMENT"
                exit 0
            else
                error "Health check failed for $ENVIRONMENT"
                exit 1
            fi
            ;;
    esac
    
    case "${ROLLBACK}" in
        true)
            get_environment_config
            confirm_deployment
            rollback_deployment
            if perform_health_check "$APP_URL"; then
                success "Rollback completed successfully!"
                send_notification "success" "Rollback completed for $ENVIRONMENT"
            else
                error "Rollback verification failed"
                send_notification "error" "Rollback verification failed for $ENVIRONMENT"
                exit 1
            fi
            exit 0
            ;;
    esac
    
    # Standard deployment
    main_deployment
}

# Error handling
trap 'error "Deployment script interrupted"; send_notification "error" "Deployment interrupted for $ENVIRONMENT"; exit 1' INT TERM

# Start execution
info "SASI Deployment Automation v2.0.0-phase2b"
info "Log file: $LOG_FILE"

if [[ "$DRY_RUN" == true ]]; then
    warn "DRY RUN MODE - No actual changes will be made"
fi

main "$@"