#!/bin/bash

# Planner MCP Production Deployment Script
# Comprehensive deployment automation with enterprise-grade safety checks

set -euo pipefail

# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
LOG_FILE="$PROJECT_ROOT/deploy.log"

# Default values
ENVIRONMENT="production"
DRY_RUN=false
SKIP_TESTS=false
SKIP_SECURITY_SCAN=false
FORCE_DEPLOY=false
IMAGE_TAG=""
TERRAFORM_APPLY=true
KUBERNETES_DEPLOY=true
ROLLBACK_ON_FAILURE=true
HEALTH_CHECK_TIMEOUT=300
CANARY_PERCENTAGE=10

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

# Logging functions
log() {
    echo -e "${GREEN}[$(date +'%Y-%m-%d %H:%M:%S')]${NC} $1" | tee -a "$LOG_FILE"
}

warn() {
    echo -e "${YELLOW}[$(date +'%Y-%m-%d %H:%M:%S')] WARNING:${NC} $1" | tee -a "$LOG_FILE"
}

error() {
    echo -e "${RED}[$(date +'%Y-%m-%d %H:%M:%S')] ERROR:${NC} $1" | tee -a "$LOG_FILE"
}

info() {
    echo -e "${BLUE}[$(date +'%Y-%m-%d %H:%M:%S')] INFO:${NC} $1" | tee -a "$LOG_FILE"
}

# Utility functions
command_exists() {
    command -v "$1" >/dev/null 2>&1
}

check_prerequisites() {
    log "Checking deployment prerequisites..."
    
    local missing_commands=()
    local required_commands=("terraform" "kubectl" "aws" "docker" "helm" "jq" "yq")
    
    for cmd in "${required_commands[@]}"; do
        if ! command_exists "$cmd"; then
            missing_commands+=("$cmd")
        fi
    done
    
    if [ ${#missing_commands[@]} -ne 0 ]; then
        error "Missing required commands: ${missing_commands[*]}"
        error "Please install the missing commands and run this script again."
        exit 1
    fi
    
    # Check AWS credentials
    if ! aws sts get-caller-identity >/dev/null 2>&1; then
        error "AWS credentials not configured. Please run 'aws configure' or set AWS environment variables."
        exit 1
    fi
    
    # Check Kubernetes context
    if ! kubectl config current-context >/dev/null 2>&1; then
        error "No Kubernetes context configured. Please configure kubectl."
        exit 1
    fi
    
    # Verify correct cluster context
    local current_context=$(kubectl config current-context)
    if [[ ! "$current_context" =~ planner-mcp.*$ENVIRONMENT ]]; then
        warn "Current kubectl context '$current_context' doesn't match environment '$ENVIRONMENT'"
        read -p "Continue anyway? (y/N): " -n 1 -r
        echo
        if [[ ! $REPLY =~ ^[Yy]$ ]]; then
            error "Deployment aborted by user"
            exit 1
        fi
    fi
    
    log "Prerequisites check passed ✓"
}

parse_arguments() {
    while [[ $# -gt 0 ]]; do
        case $1 in
            -e|--environment)
                ENVIRONMENT="$2"
                shift 2
                ;;
            --dry-run)
                DRY_RUN=true
                shift
                ;;
            --skip-tests)
                SKIP_TESTS=true
                shift
                ;;
            --skip-security-scan)
                SKIP_SECURITY_SCAN=true
                shift
                ;;
            --force)
                FORCE_DEPLOY=true
                shift
                ;;
            --image-tag)
                IMAGE_TAG="$2"
                shift 2
                ;;
            --skip-terraform)
                TERRAFORM_APPLY=false
                shift
                ;;
            --skip-kubernetes)
                KUBERNETES_DEPLOY=false
                shift
                ;;
            --no-rollback)
                ROLLBACK_ON_FAILURE=false
                shift
                ;;
            --health-timeout)
                HEALTH_CHECK_TIMEOUT="$2"
                shift 2
                ;;
            --canary)
                CANARY_PERCENTAGE="$2"
                shift 2
                ;;
            -h|--help)
                cat << EOF
Usage: $0 [OPTIONS]

Deploy Planner MCP to production environment

Options:
    -e, --environment ENV       Target environment (production|staging) [default: production]
    --dry-run                   Show what would be deployed without making changes
    --skip-tests               Skip running tests before deployment
    --skip-security-scan       Skip security vulnerability scan
    --force                    Force deployment even if checks fail
    --image-tag TAG            Specific image tag to deploy
    --skip-terraform           Skip Terraform infrastructure deployment
    --skip-kubernetes          Skip Kubernetes application deployment
    --no-rollback             Don't rollback on deployment failure
    --health-timeout SECONDS   Health check timeout [default: 300]
    --canary PERCENTAGE        Canary deployment percentage [default: 10]
    -h, --help                 Show this help message

Examples:
    $0                                    # Deploy to production with all checks
    $0 -e staging --dry-run               # Dry run deployment to staging
    $0 --image-tag v1.2.3 --canary 25    # Deploy specific version with 25% canary
    $0 --skip-terraform                   # Deploy only application changes
EOF
                exit 0
                ;;
            *)
                error "Unknown option: $1"
                exit 1
                ;;
        esac
    done
    
    # Validate environment
    if [[ ! "$ENVIRONMENT" =~ ^(staging|production)$ ]]; then
        error "Invalid environment: $ENVIRONMENT"
        error "Valid environments: staging, production"
        exit 1
    fi
    
    # Set image tag if not provided
    if [ -z "$IMAGE_TAG" ]; then
        IMAGE_TAG="v$(date +%Y%m%d-%H%M%S)-$(git rev-parse --short HEAD 2>/dev/null || echo 'unknown')"
        info "Generated image tag: $IMAGE_TAG"
    fi
    
    info "Environment: $ENVIRONMENT"
    info "Image tag: $IMAGE_TAG"
    info "Dry run: $DRY_RUN"
}

run_pre_deployment_tests() {
    if [ "$SKIP_TESTS" = true ]; then
        warn "Skipping tests as requested"
        return
    fi
    
    log "Running pre-deployment tests..."
    
    cd "$PROJECT_ROOT"
    
    # Install dependencies
    info "Installing dependencies..."
    npm ci --production=false
    
    # Run linting
    info "Running code linting..."
    npm run lint
    
    # Run type checking
    info "Running type checking..."
    npm run typecheck
    
    # Run unit tests
    info "Running unit tests..."
    npm test
    
    # Run integration tests
    info "Running integration tests..."
    npm run test:integration || {
        warn "Integration tests failed, but continuing (may indicate external service issues)"
    }
    
    # Validate configuration
    info "Validating configuration..."
    npm run validate-config:prod
    
    log "Pre-deployment tests completed ✓"
}

run_security_scan() {
    if [ "$SKIP_SECURITY_SCAN" = true ]; then
        warn "Skipping security scan as requested"
        return
    fi
    
    log "Running security vulnerability scan..."
    
    cd "$PROJECT_ROOT"
    
    # Run npm audit
    info "Checking for npm vulnerabilities..."
    npm audit --audit-level moderate
    
    # Run security scan with audit-ci
    info "Running comprehensive security scan..."
    npm run security:scan
    
    # Scan Docker images (if they exist)
    if command_exists trivy; then
        info "Scanning Docker images with Trivy..."
        trivy image --severity HIGH,CRITICAL "planner-mcp/api:$IMAGE_TAG" 2>/dev/null || {
            warn "Image not found locally, skipping Trivy scan"
        }
    fi
    
    log "Security scan completed ✓"
}

build_and_push_images() {
    log "Building and pushing Docker images..."
    
    cd "$PROJECT_ROOT"
    
    # Build application image
    info "Building application image..."
    if [ "$DRY_RUN" = false ]; then
        docker build -t "planner-mcp/api:$IMAGE_TAG" \
                    -t "planner-mcp/api:latest" \
                    -f Dockerfile .
        
        # Build dashboard image if exists
        if [ -f "Dockerfile.dashboard" ]; then
            docker build -t "planner-mcp/dashboard:$IMAGE_TAG" \
                        -t "planner-mcp/dashboard:latest" \
                        -f Dockerfile.dashboard .
        fi
        
        # Tag for ECR (assuming ECR setup)
        local account_id=$(aws sts get-caller-identity --query Account --output text)
        local region=$(aws configure get region || echo "us-east-1")
        local ecr_repo="$account_id.dkr.ecr.$region.amazonaws.com/planner-mcp"
        
        # Login to ECR
        aws ecr get-login-password --region "$region" | \
            docker login --username AWS --password-stdin "$ecr_repo"
        
        # Tag and push images
        docker tag "planner-mcp/api:$IMAGE_TAG" "$ecr_repo/api:$IMAGE_TAG"
        docker tag "planner-mcp/api:$IMAGE_TAG" "$ecr_repo/api:latest"
        
        docker push "$ecr_repo/api:$IMAGE_TAG"
        docker push "$ecr_repo/api:latest"
        
        if [ -f "Dockerfile.dashboard" ]; then
            docker tag "planner-mcp/dashboard:$IMAGE_TAG" "$ecr_repo/dashboard:$IMAGE_TAG"
            docker tag "planner-mcp/dashboard:$IMAGE_TAG" "$ecr_repo/dashboard:latest"
            
            docker push "$ecr_repo/dashboard:$IMAGE_TAG"
            docker push "$ecr_repo/dashboard:latest"
        fi
    else
        info "DRY RUN: Would build and push images with tag $IMAGE_TAG"
    fi
    
    log "Image build and push completed ✓"
}

deploy_infrastructure() {
    if [ "$TERRAFORM_APPLY" = false ]; then
        warn "Skipping Terraform infrastructure deployment"
        return
    fi
    
    log "Deploying infrastructure with Terraform..."
    
    cd "$PROJECT_ROOT/infrastructure/terraform"
    
    # Initialize Terraform
    info "Initializing Terraform..."
    if [ "$DRY_RUN" = false ]; then
        terraform init -upgrade
    else
        info "DRY RUN: Would run terraform init"
    fi
    
    # Create workspace if it doesn't exist
    if [ "$DRY_RUN" = false ]; then
        terraform workspace select "$ENVIRONMENT" 2>/dev/null || \
            terraform workspace new "$ENVIRONMENT"
    else
        info "DRY RUN: Would select/create workspace $ENVIRONMENT"
    fi
    
    # Plan infrastructure changes
    info "Planning infrastructure changes..."
    local plan_file="/tmp/terraform-$ENVIRONMENT-plan"
    
    if [ "$DRY_RUN" = false ]; then
        terraform plan \
            -var="environment=$ENVIRONMENT" \
            -var="image_tag=$IMAGE_TAG" \
            -out="$plan_file"
    else
        info "DRY RUN: Would run terraform plan"
        return
    fi
    
    # Show plan summary
    terraform show -no-color "$plan_file" | head -50
    
    # Confirm deployment
    if [ "$FORCE_DEPLOY" = false ]; then
        echo
        read -p "Apply these infrastructure changes? (y/N): " -n 1 -r
        echo
        if [[ ! $REPLY =~ ^[Yy]$ ]]; then
            info "Infrastructure deployment cancelled by user"
            return
        fi
    fi
    
    # Apply changes
    info "Applying infrastructure changes..."
    terraform apply "$plan_file"
    
    # Update kubectl config
    info "Updating kubectl configuration..."
    local cluster_name=$(terraform output -raw cluster_id)
    local region=$(terraform output -raw region)
    aws eks update-kubeconfig --region "$region" --name "$cluster_name"
    
    log "Infrastructure deployment completed ✓"
}

deploy_kubernetes() {
    if [ "$KUBERNETES_DEPLOY" = false ]; then
        warn "Skipping Kubernetes application deployment"
        return
    fi
    
    log "Deploying application to Kubernetes..."
    
    cd "$PROJECT_ROOT"
    
    # Update image tags in manifests
    info "Updating image tags in Kubernetes manifests..."
    find infrastructure/kubernetes -name "*.yaml" -type f -exec \
        sed -i.bak "s/image: \"planner-mcp\/\([^:]*\):.*\"/image: \"planner-mcp\/\1:$IMAGE_TAG\"/" {} \;
    
    # Apply namespace first
    info "Creating/updating namespaces..."
    if [ "$DRY_RUN" = false ]; then
        kubectl apply -f infrastructure/kubernetes/namespace.yaml
    else
        info "DRY RUN: Would apply namespace.yaml"
    fi
    
    # Create secrets (from Terraform outputs)
    info "Creating/updating secrets..."
    if [ "$DRY_RUN" = false ]; then
        create_kubernetes_secrets
    else
        info "DRY RUN: Would create Kubernetes secrets"
    fi
    
    # Apply ConfigMaps
    info "Creating/updating ConfigMaps..."
    if [ "$DRY_RUN" = false ]; then
        kubectl apply -f infrastructure/kubernetes/configmap.yaml
    else
        info "DRY RUN: Would apply configmap.yaml"
    fi
    
    # Deploy using rolling update or canary
    if [ "$CANARY_PERCENTAGE" -gt 0 ] && [ "$CANARY_PERCENTAGE" -lt 100 ]; then
        deploy_canary
    else
        deploy_rolling_update
    fi
    
    log "Kubernetes deployment completed ✓"
}

create_kubernetes_secrets() {
    local namespace="planner-mcp"
    
    # Get database credentials from Terraform
    local db_secret_arn=$(cd infrastructure/terraform && terraform output -raw database_secret_arn)
    local redis_secret_arn=$(cd infrastructure/terraform && terraform output -raw redis_secret_arn)
    
    # Fetch secrets from AWS Secrets Manager
    local db_secret=$(aws secretsmanager get-secret-value --secret-id "$db_secret_arn" --query SecretString --output text)
    local redis_secret=$(aws secretsmanager get-secret-value --secret-id "$redis_secret_arn" --query SecretString --output text)
    
    # Create database credentials secret
    kubectl create secret generic database-credentials \
        --namespace="$namespace" \
        --from-literal=host=$(echo "$db_secret" | jq -r .host) \
        --from-literal=port=$(echo "$db_secret" | jq -r .port) \
        --from-literal=dbname=$(echo "$db_secret" | jq -r .dbname) \
        --from-literal=username=$(echo "$db_secret" | jq -r .username) \
        --from-literal=password=$(echo "$db_secret" | jq -r .password) \
        --dry-run=client -o yaml | kubectl apply -f -
    
    # Create Redis credentials secret
    kubectl create secret generic redis-credentials \
        --namespace="$namespace" \
        --from-literal=host=$(echo "$redis_secret" | jq -r .host) \
        --from-literal=port=$(echo "$redis_secret" | jq -r .port) \
        --from-literal=password=$(echo "$redis_secret" | jq -r .password) \
        --dry-run=client -o yaml | kubectl apply -f -
    
    # Create application secrets (generate if not exist)
    kubectl create secret generic app-secrets \
        --namespace="$namespace" \
        --from-literal=jwt-secret="$(openssl rand -hex 32)" \
        --from-literal=jwt-refresh-secret="$(openssl rand -hex 32)" \
        --from-literal=encryption-key="$(openssl rand -hex 32)" \
        --from-literal=field-encryption-key="$(openssl rand -hex 32)" \
        --dry-run=client -o yaml | kubectl apply -f -
}

deploy_rolling_update() {
    info "Performing rolling update deployment..."
    
    # Apply service first
    kubectl apply -f infrastructure/kubernetes/service.yaml
    
    # Apply deployment with rolling update
    kubectl apply -f infrastructure/kubernetes/deployment.yaml
    
    # Wait for rollout to complete
    info "Waiting for deployment rollout..."
    kubectl rollout status deployment/planner-mcp-api --namespace=planner-mcp --timeout=300s
    kubectl rollout status deployment/planner-mcp-dashboard --namespace=planner-mcp --timeout=300s
}

deploy_canary() {
    info "Performing canary deployment with $CANARY_PERCENTAGE% traffic..."
    
    # This would require more sophisticated tooling like Istio, Flagger, or Argo Rollouts
    # For now, we'll simulate with a simple approach
    
    warn "Canary deployment not fully implemented - falling back to rolling update"
    deploy_rolling_update
}

run_health_checks() {
    log "Running post-deployment health checks..."
    
    local namespace="planner-mcp"
    local service_name="planner-mcp-api-service"
    local health_endpoint="/health"
    
    # Wait for pods to be ready
    info "Waiting for pods to be ready..."
    kubectl wait --for=condition=ready pod \
        --selector=app.kubernetes.io/name=planner-mcp-api \
        --namespace="$namespace" \
        --timeout=300s
    
    # Port forward to test health endpoint
    info "Testing health endpoints..."
    kubectl port-forward service/"$service_name" 8080:3001 --namespace="$namespace" &
    local port_forward_pid=$!
    
    # Wait for port forward to establish
    sleep 5
    
    local health_check_start=$(date +%s)
    local health_check_passed=false
    
    while [ $(($(date +%s) - health_check_start)) -lt $HEALTH_CHECK_TIMEOUT ]; do
        if curl -sf "http://localhost:8080$health_endpoint" >/dev/null 2>&1; then
            health_check_passed=true
            break
        fi
        info "Health check failed, retrying in 10 seconds..."
        sleep 10
    done
    
    # Clean up port forward
    kill $port_forward_pid 2>/dev/null || true
    
    if [ "$health_check_passed" = false ]; then
        error "Health checks failed after $HEALTH_CHECK_TIMEOUT seconds"
        if [ "$ROLLBACK_ON_FAILURE" = true ]; then
            rollback_deployment
        fi
        exit 1
    fi
    
    log "Health checks passed ✓"
}

run_smoke_tests() {
    log "Running smoke tests..."
    
    # Basic API functionality tests
    local namespace="planner-mcp"
    local service_name="planner-mcp-api-service"
    
    # Port forward for testing
    kubectl port-forward service/"$service_name" 8080:3000 --namespace="$namespace" &
    local port_forward_pid=$!
    sleep 5
    
    # Test API endpoints
    local base_url="http://localhost:8080"
    
    # Test health endpoint
    if ! curl -sf "$base_url/health" | jq -e '.status == "healthy"' >/dev/null; then
        error "Health endpoint test failed"
        kill $port_forward_pid 2>/dev/null || true
        return 1
    fi
    
    # Test metrics endpoint
    if ! curl -sf "$base_url/metrics" | grep -q "nodejs_version_info"; then
        error "Metrics endpoint test failed"
        kill $port_forward_pid 2>/dev/null || true
        return 1
    fi
    
    # Clean up
    kill $port_forward_pid 2>/dev/null || true
    
    log "Smoke tests passed ✓"
}

rollback_deployment() {
    error "Rolling back deployment due to failure..."
    
    local namespace="planner-mcp"
    
    # Rollback deployments
    kubectl rollout undo deployment/planner-mcp-api --namespace="$namespace"
    kubectl rollout undo deployment/planner-mcp-dashboard --namespace="$namespace"
    
    # Wait for rollback to complete
    kubectl rollout status deployment/planner-mcp-api --namespace="$namespace" --timeout=300s
    kubectl rollout status deployment/planner-mcp-dashboard --namespace="$namespace" --timeout=300s
    
    error "Deployment rolled back"
}

cleanup() {
    info "Cleaning up deployment artifacts..."
    
    # Clean up temporary files
    rm -f /tmp/terraform-*-plan
    
    # Restore original Kubernetes manifests
    find infrastructure/kubernetes -name "*.yaml.bak" -type f | while read -r backup_file; do
        mv "$backup_file" "${backup_file%.bak}"
    done
    
    info "Cleanup completed"
}

print_deployment_summary() {
    log "Deployment completed successfully!"
    echo
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo -e "${GREEN}Planner MCP Deployment Summary${NC}"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    echo
    info "Environment: $ENVIRONMENT"
    info "Image Tag: $IMAGE_TAG"
    info "Deployment Time: $(date)"
    echo
    
    if command_exists kubectl && kubectl cluster-info >/dev/null 2>&1; then
        echo "🔗 Access Information:"
        local external_ip=$(kubectl get service planner-mcp-external-service -n planner-mcp -o jsonpath='{.status.loadBalancer.ingress[0].hostname}' 2>/dev/null || echo "Pending...")
        echo "  • External URL: https://$external_ip"
        echo "  • Health Check: https://$external_ip/health"
        echo "  • Metrics: https://$external_ip/metrics"
        echo "  • Dashboard: https://$external_ip/dashboard"
        echo
        
        echo "📊 Resource Status:"
        kubectl get pods -n planner-mcp
        echo
    fi
    
    echo "📚 Next Steps:"
    echo "  1. Monitor application health and metrics"
    echo "  2. Run full integration tests"
    echo "  3. Update monitoring alerts"
    echo "  4. Verify backup and disaster recovery procedures"
    echo "  5. Update documentation with new deployment info"
    echo
    echo "📖 Documentation: https://docs.planner-mcp.example.com"
    echo "🎯 Monitoring: https://monitoring.planner-mcp.example.com"
    echo "📧 Support: support@planner-mcp.example.com"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
}

# Main execution
main() {
    echo -e "${BLUE}Planner MCP Production Deployment${NC}"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
    
    # Initialize log file
    echo "Deployment started at $(date)" > "$LOG_FILE"
    
    # Trap for cleanup on exit
    trap cleanup EXIT
    
    parse_arguments "$@"
    check_prerequisites
    run_pre_deployment_tests
    run_security_scan
    build_and_push_images
    deploy_infrastructure
    deploy_kubernetes
    run_health_checks
    run_smoke_tests
    print_deployment_summary
    
    log "Deployment completed successfully at $(date)"
}

# Error handling
trap 'error "Deployment failed on line $LINENO. Check $LOG_FILE for details."; exit 1' ERR

# Run main function
main "$@"