#!/usr/bin/env bash

# Kubernetes Manifest Generator
# Interactive script to generate production-ready Kubernetes manifests

set -euo pipefail

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

# Script directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TEMPLATE_DIR="${SCRIPT_DIR}/../templates"
OUTPUT_DIR="${PWD}"

# Helper functions
print_header() {
    echo -e "\n${BLUE}========================================${NC}"
    echo -e "${BLUE}$1${NC}"
    echo -e "${BLUE}========================================${NC}\n"
}

print_success() {
    echo -e "${GREEN}✓ $1${NC}"
}

print_error() {
    echo -e "${RED}✗ $1${NC}"
}

print_warning() {
    echo -e "${YELLOW}⚠ $1${NC}"
}

prompt_input() {
    local prompt="$1"
    local default="$2"
    local result

    if [ -n "$default" ]; then
        read -p "$(echo -e ${BLUE}${prompt}${NC}) [${default}]: " result
        echo "${result:-$default}"
    else
        read -p "$(echo -e ${BLUE}${prompt}${NC}): " result
        echo "$result"
    fi
}

prompt_yes_no() {
    local prompt="$1"
    local default="${2:-n}"
    local result

    read -p "$(echo -e ${BLUE}${prompt}${NC}) [y/N]: " result
    result="${result:-$default}"

    [[ "$result" =~ ^[Yy]$ ]]
}

# Manifest generators
generate_deployment() {
    print_header "Generate Deployment Manifest"

    local name=$(prompt_input "Application name" "my-app")
    local namespace=$(prompt_input "Namespace" "default")
    local image=$(prompt_input "Container image" "nginx:1.25-alpine")
    local replicas=$(prompt_input "Number of replicas" "3")
    local port=$(prompt_input "Container port" "8080")
    local cpu_request=$(prompt_input "CPU request (e.g., 100m)" "100m")
    local mem_request=$(prompt_input "Memory request (e.g., 128Mi)" "128Mi")
    local cpu_limit=$(prompt_input "CPU limit (e.g., 200m)" "200m")
    local mem_limit=$(prompt_input "Memory limit (e.g., 256Mi)" "256Mi")

    local output_file="${OUTPUT_DIR}/${name}-deployment.yaml"

    cat > "$output_file" <<EOF
---
# Generated by Kubernetes Manifest Creator
# Application: ${name}
# Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ${name}
  namespace: ${namespace}
  labels:
    app.kubernetes.io/name: ${name}
    app.kubernetes.io/version: "1.0.0"
    app.kubernetes.io/component: application
    app.kubernetes.io/managed-by: kubectl
  annotations:
    description: "${name} deployment"
spec:
  replicas: ${replicas}
  revisionHistoryLimit: 10
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 1
      maxUnavailable: 0
  selector:
    matchLabels:
      app.kubernetes.io/name: ${name}
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ${name}
        app.kubernetes.io/version: "1.0.0"
        app.kubernetes.io/component: application
    spec:
      # Security context - run as non-root user
      securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        fsGroup: 2000
        seccompProfile:
          type: RuntimeDefault

      serviceAccountName: ${name}
      automountServiceAccountToken: false

      # High availability - spread pods across nodes
      affinity:
        podAntiAffinity:
          preferredDuringSchedulingIgnoredDuringExecution:
          - weight: 100
            podAffinityTerm:
              labelSelector:
                matchExpressions:
                - key: app.kubernetes.io/name
                  operator: In
                  values:
                  - ${name}
              topologyKey: kubernetes.io/hostname

      containers:
      - name: ${name}
        image: ${image}
        imagePullPolicy: IfNotPresent

        # Container security context
        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          runAsUser: 1000
          capabilities:
            drop:
            - ALL

        ports:
        - name: http
          containerPort: ${port}
          protocol: TCP

        # Environment variables (customize as needed)
        env:
        - name: APP_ENV
          value: "production"
        - name: LOG_LEVEL
          value: "info"

        # Resource limits and requests
        resources:
          requests:
            cpu: ${cpu_request}
            memory: ${mem_request}
          limits:
            cpu: ${cpu_limit}
            memory: ${mem_limit}

        # Health probes
        startupProbe:
          httpGet:
            path: /health
            port: http
          initialDelaySeconds: 0
          periodSeconds: 10
          timeoutSeconds: 3
          successThreshold: 1
          failureThreshold: 30

        livenessProbe:
          httpGet:
            path: /health
            port: http
          periodSeconds: 10
          timeoutSeconds: 3
          failureThreshold: 3

        readinessProbe:
          httpGet:
            path: /health
            port: http
          periodSeconds: 5
          timeoutSeconds: 3
          failureThreshold: 3

        # Volume mounts for writable directories
        volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: cache
          mountPath: /var/cache

      # Volumes
      volumes:
      - name: tmp
        emptyDir: {}
      - name: cache
        emptyDir: {}

      terminationGracePeriodSeconds: 30
      dnsPolicy: ClusterFirst
      restartPolicy: Always
EOF

    print_success "Deployment manifest created: $output_file"

    if prompt_yes_no "Create a Service for this deployment?"; then
        generate_service "$name" "$namespace" "$port"
    fi
}

generate_service() {
    local name="${1:-$(prompt_input "Service name" "my-app")}"
    local namespace="${2:-$(prompt_input "Namespace" "default")}"
    local target_port="${3:-$(prompt_input "Target port" "8080")}"

    print_header "Generate Service Manifest"

    echo "Select service type:"
    echo "1. ClusterIP (internal only)"
    echo "2. NodePort (expose on node IP)"
    echo "3. LoadBalancer (cloud load balancer)"

    local service_type
    read -p "Enter choice [1-3]: " choice

    case $choice in
        1) service_type="ClusterIP" ;;
        2) service_type="NodePort" ;;
        3) service_type="LoadBalancer" ;;
        *) service_type="ClusterIP" ;;
    esac

    local service_port=$(prompt_input "Service port" "80")
    local output_file="${OUTPUT_DIR}/${name}-service.yaml"

    cat > "$output_file" <<EOF
---
# Generated by Kubernetes Manifest Creator
# Service: ${name}
# Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
apiVersion: v1
kind: Service
metadata:
  name: ${name}
  namespace: ${namespace}
  labels:
    app.kubernetes.io/name: ${name}
    app.kubernetes.io/component: service
  annotations:
    description: "Service for ${name}"
spec:
  type: ${service_type}

  selector:
    app.kubernetes.io/name: ${name}

  ports:
  - name: http
    protocol: TCP
    port: ${service_port}
    targetPort: ${target_port}
EOF

    if [ "$service_type" = "NodePort" ]; then
        local node_port=$(prompt_input "Node port (30000-32767, empty for auto)" "")
        if [ -n "$node_port" ]; then
            echo "    nodePort: ${node_port}" >> "$output_file"
        fi
    fi

    if [ "$service_type" = "LoadBalancer" ]; then
        cat >> "$output_file" <<EOF

  # Preserve client IP
  externalTrafficPolicy: Local

  # Restrict access (0.0.0.0/0 = allow all)
  loadBalancerSourceRanges:
  - "0.0.0.0/0"
EOF
    fi

    print_success "Service manifest created: $output_file"
}

generate_configmap() {
    print_header "Generate ConfigMap Manifest"

    local name=$(prompt_input "ConfigMap name" "app-config")
    local namespace=$(prompt_input "Namespace" "default")
    local output_file="${OUTPUT_DIR}/${name}-configmap.yaml"

    cat > "$output_file" <<EOF
---
# Generated by Kubernetes Manifest Creator
# ConfigMap: ${name}
# Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
apiVersion: v1
kind: ConfigMap
metadata:
  name: ${name}
  namespace: ${namespace}
  labels:
    app.kubernetes.io/name: ${name}
    app.kubernetes.io/component: configuration
data:
  # Add your configuration key-value pairs here
  app.name: "my-app"
  app.env: "production"
  log.level: "info"

  # Example configuration file
  app.properties: |
    server.port=8080
    server.host=0.0.0.0
    log.format=json
EOF

    print_success "ConfigMap manifest created: $output_file"
    print_warning "Remember to customize the configuration values!"
}

generate_ingress() {
    print_header "Generate Ingress Manifest"

    local name=$(prompt_input "Ingress name" "app-ingress")
    local namespace=$(prompt_input "Namespace" "default")
    local host=$(prompt_input "Hostname" "example.com")
    local service_name=$(prompt_input "Backend service name" "my-app")
    local service_port=$(prompt_input "Backend service port" "80")
    local output_file="${OUTPUT_DIR}/${name}-ingress.yaml"

    local enable_tls=false
    if prompt_yes_no "Enable TLS/HTTPS?"; then
        enable_tls=true
    fi

    cat > "$output_file" <<EOF
---
# Generated by Kubernetes Manifest Creator
# Ingress: ${name}
# Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ${name}
  namespace: ${namespace}
  labels:
    app.kubernetes.io/name: ${name}
  annotations:
    kubernetes.io/ingress.class: "nginx"
EOF

    if [ "$enable_tls" = true ]; then
        cat >> "$output_file" <<EOF
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/force-ssl-redirect: "true"
EOF
    fi

    cat >> "$output_file" <<EOF
    nginx.ingress.kubernetes.io/rate-limit: "100"
spec:
  ingressClassName: nginx
EOF

    if [ "$enable_tls" = true ]; then
        cat >> "$output_file" <<EOF

  tls:
  - hosts:
    - ${host}
    secretName: ${name}-tls
EOF
    fi

    cat >> "$output_file" <<EOF

  rules:
  - host: ${host}
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: ${service_name}
            port:
              number: ${service_port}
EOF

    print_success "Ingress manifest created: $output_file"

    if [ "$enable_tls" = true ]; then
        print_warning "Remember to install cert-manager for automatic TLS certificates!"
        print_warning "kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.13.0/cert-manager.yaml"
    fi
}

generate_statefulset() {
    print_header "Generate StatefulSet Manifest"

    local name=$(prompt_input "StatefulSet name" "database")
    local namespace=$(prompt_input "Namespace" "default")
    local image=$(prompt_input "Container image" "postgres:16-alpine")
    local replicas=$(prompt_input "Number of replicas" "1")
    local port=$(prompt_input "Container port" "5432")
    local storage_size=$(prompt_input "Storage size" "10Gi")
    local output_file="${OUTPUT_DIR}/${name}-statefulset.yaml"

    cat > "$output_file" <<EOF
---
# Generated by Kubernetes Manifest Creator
# StatefulSet: ${name}
# Generated: $(date -u +"%Y-%m-%d %H:%M:%S UTC")
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: ${name}
  namespace: ${namespace}
  labels:
    app.kubernetes.io/name: ${name}
    app.kubernetes.io/component: database
spec:
  serviceName: ${name}-headless
  replicas: ${replicas}
  selector:
    matchLabels:
      app.kubernetes.io/name: ${name}
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ${name}
        app.kubernetes.io/component: database
    spec:
      securityContext:
        runAsNonRoot: true
        runAsUser: 999
        fsGroup: 999
        seccompProfile:
          type: RuntimeDefault

      serviceAccountName: ${name}
      automountServiceAccountToken: false

      containers:
      - name: ${name}
        image: ${image}
        imagePullPolicy: IfNotPresent

        securityContext:
          allowPrivilegeEscalation: false
          readOnlyRootFilesystem: true
          runAsNonRoot: true
          runAsUser: 999
          capabilities:
            drop:
            - ALL

        ports:
        - name: db
          containerPort: ${port}

        resources:
          requests:
            cpu: 250m
            memory: 512Mi
          limits:
            cpu: 500m
            memory: 1Gi

        volumeMounts:
        - name: data
          mountPath: /var/lib/postgresql/data
        - name: tmp
          mountPath: /tmp

      volumes:
      - name: tmp
        emptyDir: {}

  volumeClaimTemplates:
  - metadata:
      name: data
    spec:
      accessModes:
      - ReadWriteOnce
      resources:
        requests:
          storage: ${storage_size}

---
# Headless Service for StatefulSet
apiVersion: v1
kind: Service
metadata:
  name: ${name}-headless
  namespace: ${namespace}
spec:
  type: ClusterIP
  clusterIP: None
  publishNotReadyAddresses: true
  selector:
    app.kubernetes.io/name: ${name}
  ports:
  - name: db
    port: ${port}
    targetPort: ${port}

---
# Regular Service for client connections
apiVersion: v1
kind: Service
metadata:
  name: ${name}
  namespace: ${namespace}
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/name: ${name}
  ports:
  - name: db
    port: ${port}
    targetPort: ${port}
EOF

    print_success "StatefulSet manifest created: $output_file"
}

# Main menu
show_menu() {
    clear
    print_header "Kubernetes Manifest Generator"

    echo "Select the type of manifest to generate:"
    echo ""
    echo "  1. Deployment          - Stateless application"
    echo "  2. Service             - Expose application"
    echo "  3. ConfigMap           - Configuration data"
    echo "  4. Ingress             - HTTP/HTTPS routing"
    echo "  5. StatefulSet         - Stateful application"
    echo "  6. Complete App Stack  - Full 3-tier application"
    echo ""
    echo "  0. Exit"
    echo ""
}

copy_complete_stack() {
    print_header "Copy Complete Application Stack"

    local source="${TEMPLATE_DIR}/full-app-example/complete-3tier-app.yaml"
    local dest="${OUTPUT_DIR}/complete-app-stack.yaml"

    if [ ! -f "$source" ]; then
        print_error "Template not found: $source"
        return 1
    fi

    cp "$source" "$dest"
    print_success "Complete stack template copied to: $dest"
    print_warning "Remember to customize the manifest for your application!"
    print_warning "Update namespaces, images, domains, and secrets before deploying."
}

# Main loop
main() {
    while true; do
        show_menu
        read -p "Enter your choice [0-6]: " choice

        case $choice in
            1) generate_deployment ;;
            2) generate_service ;;
            3) generate_configmap ;;
            4) generate_ingress ;;
            5) generate_statefulset ;;
            6) copy_complete_stack ;;
            0)
                echo ""
                print_success "Goodbye!"
                exit 0
                ;;
            *)
                print_error "Invalid choice. Please try again."
                sleep 2
                ;;
        esac

        echo ""
        read -p "Press Enter to continue..."
    done
}

# Run main function
main
