#!/bin/bash
#
# Citadel Ephemeral Node Lifecycle Manager
# Simulates the 48-hour node recycling pattern used in Citadel
#
# This script demonstrates the core Citadel principle:
# "Nodes are immutable and short-lived (max 48 hours)"
#

set -e

# Configuration
NODE_MAX_AGE_HOURS=${NODE_MAX_AGE_HOURS:-48}  # Default 48 hours
DRAIN_TIMEOUT=${DRAIN_TIMEOUT:-300}           # 5 minutes drain timeout
DRY_RUN=${DRY_RUN:-false}

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

# Set kubeconfig
export KUBECONFIG=${KUBECONFIG:-/etc/rancher/k3s/k3s.yaml}

usage() {
    echo "Usage: $0 [command] [options]"
    echo ""
    echo "Commands:"
    echo "  check [node]     Check age of node(s)"
    echo "  drain <node>     Drain a specific node"
    echo "  cycle            Check and drain all aged nodes"
    echo "  status           Show node age summary"
    echo ""
    echo "Options:"
    echo "  --max-age HOURS  Maximum node age (default: 48)"
    echo "  --dry-run        Show what would be done"
    echo ""
    echo "Environment Variables:"
    echo "  NODE_MAX_AGE_HOURS  Maximum node age in hours (default: 48)"
    echo "  DRAIN_TIMEOUT       Drain timeout in seconds (default: 300)"
    echo "  DRY_RUN             Set to 'true' for dry run mode"
    echo ""
    echo "Examples:"
    echo "  $0 status                    # Show all node ages"
    echo "  $0 check my-node             # Check specific node age"
    echo "  $0 cycle --dry-run           # Dry run of node cycling"
    echo "  $0 drain my-node             # Drain aged node"
}

log_info() {
    echo -e "${BLUE}[INFO]${NC} $1"
}

log_warn() {
    echo -e "${YELLOW}[WARN]${NC} $1"
}

log_error() {
    echo -e "${RED}[ERROR]${NC} $1"
}

log_success() {
    echo -e "${GREEN}[OK]${NC} $1"
}

# Get node age in hours
get_node_age_hours() {
    local node=$1
    local creation_time=$(kubectl get node "$node" -o jsonpath='{.metadata.creationTimestamp}')

    # Convert to epoch
    local creation_epoch=$(date -d "$creation_time" +%s 2>/dev/null || date -j -f "%Y-%m-%dT%H:%M:%SZ" "$creation_time" +%s 2>/dev/null)
    local current_epoch=$(date +%s)

    local age_seconds=$((current_epoch - creation_epoch))
    local age_hours=$((age_seconds / 3600))

    echo $age_hours
}

# Get node age formatted
get_node_age_formatted() {
    local hours=$1
    local days=$((hours / 24))
    local remaining_hours=$((hours % 24))

    if [ $days -gt 0 ]; then
        echo "${days}d ${remaining_hours}h"
    else
        echo "${hours}h"
    fi
}

# Check single node
check_node() {
    local node=$1
    local age_hours=$(get_node_age_hours "$node")
    local age_formatted=$(get_node_age_formatted $age_hours)
    local status=$(kubectl get node "$node" -o jsonpath='{.status.conditions[?(@.type=="Ready")].status}')

    echo ""
    echo "Node: $node"
    echo "  Age: $age_formatted ($age_hours hours)"
    echo "  Max Age: ${NODE_MAX_AGE_HOURS} hours"
    echo "  Status: $status"

    if [ $age_hours -ge $NODE_MAX_AGE_HOURS ]; then
        log_warn "Node is AGED and should be recycled!"
        return 1
    else
        local remaining=$((NODE_MAX_AGE_HOURS - age_hours))
        log_success "Node is healthy. ${remaining} hours until recycle."
        return 0
    fi
}

# Show status of all nodes
show_status() {
    echo "=========================================="
    echo "  Citadel Node Lifecycle Status"
    echo "  Max Age: ${NODE_MAX_AGE_HOURS} hours"
    echo "=========================================="
    echo ""

    printf "%-30s %-15s %-15s %-10s\n" "NODE" "AGE" "MAX AGE" "STATUS"
    printf "%-30s %-15s %-15s %-10s\n" "----" "---" "-------" "------"

    local aged_count=0

    for node in $(kubectl get nodes -o jsonpath='{.items[*].metadata.name}'); do
        local age_hours=$(get_node_age_hours "$node")
        local age_formatted=$(get_node_age_formatted $age_hours)

        if [ $age_hours -ge $NODE_MAX_AGE_HOURS ]; then
            local status="${RED}AGED${NC}"
            ((aged_count++))
        else
            local remaining=$((NODE_MAX_AGE_HOURS - age_hours))
            local status="${GREEN}OK (${remaining}h left)${NC}"
        fi

        printf "%-30s %-15s %-15s " "$node" "$age_formatted" "${NODE_MAX_AGE_HOURS}h"
        echo -e "$status"
    done

    echo ""
    if [ $aged_count -gt 0 ]; then
        log_warn "$aged_count node(s) need recycling"
    else
        log_success "All nodes are within lifecycle limits"
    fi
}

# Drain a node
drain_node() {
    local node=$1

    log_info "Draining node: $node"

    if [ "$DRY_RUN" = "true" ]; then
        log_info "[DRY RUN] Would cordon node: $node"
        log_info "[DRY RUN] Would drain node: $node"
        log_info "[DRY RUN] Node ready for replacement"
        return 0
    fi

    # Cordon the node (prevent new pods)
    log_info "Cordoning node..."
    kubectl cordon "$node"

    # Drain the node (evict pods)
    log_info "Draining pods (timeout: ${DRAIN_TIMEOUT}s)..."
    kubectl drain "$node" \
        --ignore-daemonsets \
        --delete-emptydir-data \
        --force \
        --timeout="${DRAIN_TIMEOUT}s" || {
            log_error "Drain failed or timed out"
            log_warn "Some pods may still be running"
        }

    log_success "Node $node drained and ready for replacement"
    echo ""
    echo "Next steps:"
    echo "  1. Terminate the node (cloud provider or physical removal)"
    echo "  2. Launch new node from fresh image"
    echo "  3. New node will auto-join cluster"
    echo ""
    echo "For K3s single-node development:"
    echo "  - This simulates the lifecycle; actual termination not performed"
    echo "  - In production, automation would replace the node"
}

# Cycle all aged nodes
cycle_nodes() {
    log_info "Checking all nodes for lifecycle recycling..."
    echo ""

    local nodes_to_drain=()

    for node in $(kubectl get nodes -o jsonpath='{.items[*].metadata.name}'); do
        local age_hours=$(get_node_age_hours "$node")

        if [ $age_hours -ge $NODE_MAX_AGE_HOURS ]; then
            nodes_to_drain+=("$node")
            log_warn "Node $node is $age_hours hours old (max: $NODE_MAX_AGE_HOURS)"
        else
            log_success "Node $node is $age_hours hours old (OK)"
        fi
    done

    echo ""

    if [ ${#nodes_to_drain[@]} -eq 0 ]; then
        log_success "No nodes need recycling"
        return 0
    fi

    log_warn "${#nodes_to_drain[@]} node(s) need recycling"
    echo ""

    for node in "${nodes_to_drain[@]}"; do
        drain_node "$node"
    done
}

# Parse arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        --max-age)
            NODE_MAX_AGE_HOURS="$2"
            shift 2
            ;;
        --dry-run)
            DRY_RUN=true
            shift
            ;;
        check|drain|cycle|status|help)
            COMMAND="$1"
            shift
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        *)
            NODE_ARG="$1"
            shift
            ;;
    esac
done

# Execute command
case ${COMMAND:-help} in
    check)
        if [ -z "$NODE_ARG" ]; then
            show_status
        else
            check_node "$NODE_ARG"
        fi
        ;;
    drain)
        if [ -z "$NODE_ARG" ]; then
            log_error "Node name required for drain command"
            exit 1
        fi
        drain_node "$NODE_ARG"
        ;;
    cycle)
        cycle_nodes
        ;;
    status)
        show_status
        ;;
    help|*)
        usage
        ;;
esac
