#!/usr/bin/env bash
set -euo pipefail

ENV_FILE="/etc/niceos/kubernetes/kubeadm.env"
PROFILE="flannel"
COMMAND="render"
OUTPUT=""
ADVERTISE_ADDRESS=""
RESOLV_CONF=""
KUBERNETES_VERSION=""
IMAGE_REPOSITORY=""
CLUSTER_NAME=""
SERVICE_SUBNET=""
POD_SUBNET=""
CRI_SOCKET=""
KUBE_PROXY_MODE=""
NODE_LABELS=""

usage() {
    cat <<'USAGE'
Usage:
  niceos-kubeadm-config render [options]
  niceos-kubeadm-config images-list [options]
  niceos-kubeadm-config images-pull [options]
  niceos-kubeadm-config init [options]

Commands:
  render       Render kubeadm init config.
  images-list List Kubernetes images required by kubeadm.
  images-pull Pull Kubernetes images required by kubeadm.
  init         Run kubeadm init with rendered NiceOS config.

Options:
  --profile NAME              flannel, calico or cilium
  --output PATH               output kubeadm config path
  --advertise-address IP      Kubernetes API advertise address
  --resolv-conf PATH          kubelet resolvConf path
  --kubernetes-version VER    Kubernetes version, for example v1.35.4
  --image-repository REPO     image repository, for example registry.k8s.io
  --cluster-name NAME         cluster name
  --service-subnet CIDR       service subnet
  --pod-subnet CIDR           pod subnet
  --cri-socket SOCKET         CRI socket
  --kube-proxy-mode MODE      iptables, ipvs or nftables
  --node-labels LABELS        kubelet node labels
  -h, --help                  show this help

Examples:
  niceos-kubeadm-config render --profile flannel
  niceos-kubeadm-config images-list --profile flannel
  niceos-kubeadm-config images-pull --profile flannel
  niceos-kubeadm-config init --profile flannel

Temporary upstream smoke-test:
  niceos-kubeadm-config init --profile flannel --image-repository registry.k8s.io
USAGE
}

log() {
    printf '[INFO] %s\n' "$*" >&2
}

die() {
    printf '[ERROR] %s\n' "$*" >&2
    exit 1
}

sed_escape() {
    printf '%s' "$1" | sed -e 's/[\/&]/\\&/g'
}

detect_advertise_address() {
    local ipaddr=""

    ipaddr="$(ip -4 route get 1.1.1.1 2>/dev/null | awk '
        {
          for (i = 1; i <= NF; i++) {
            if ($i == "src") {
              print $(i+1);
              exit
            }
          }
        }' || true)"

    if [ -z "$ipaddr" ]; then
        ipaddr="$(hostname -I 2>/dev/null | awk '{print $1}' || true)"
    fi

    [ -n "$ipaddr" ] || die "Cannot auto-detect advertise address. Use --advertise-address IP."
    printf '%s\n' "$ipaddr"
}

detect_resolv_conf() {
    if [ -n "${NICEOS_RESOLV_CONF:-}" ]; then
        printf '%s\n' "$NICEOS_RESOLV_CONF"
        return 0
    fi

    if [ -f /run/systemd/resolve/resolv.conf ] && command -v systemctl >/dev/null 2>&1; then
        if systemctl is-active --quiet systemd-resolved.service 2>/dev/null; then
            printf '%s\n' "/run/systemd/resolve/resolv.conf"
            return 0
        fi
    fi

    if [ -f /run/systemd/resolve/resolv.conf ]; then
        if grep -q '^nameserver ' /run/systemd/resolve/resolv.conf 2>/dev/null; then
            printf '%s\n' "/run/systemd/resolve/resolv.conf"
            return 0
        fi
    fi

    printf '%s\n' "/etc/resolv.conf"
}

load_defaults() {
    # Defaults are product defaults for NiceOS Core Node.
    NICEOS_KUBERNETES_VERSION="${NICEOS_KUBERNETES_VERSION:-v1.35.4}"
    NICEOS_IMAGE_REPOSITORY="${NICEOS_IMAGE_REPOSITORY:-registry.niceos.ru/mirror/registry.k8s.io}"
    NICEOS_CLUSTER_NAME="${NICEOS_CLUSTER_NAME:-niceos}"
    NICEOS_SERVICE_SUBNET="${NICEOS_SERVICE_SUBNET:-10.96.0.0/12}"
    NICEOS_POD_SUBNET_FLANNEL="${NICEOS_POD_SUBNET_FLANNEL:-10.244.0.0/16}"
    NICEOS_POD_SUBNET_CALICO="${NICEOS_POD_SUBNET_CALICO:-192.168.0.0/16}"
    NICEOS_POD_SUBNET_CILIUM="${NICEOS_POD_SUBNET_CILIUM:-10.244.0.0/16}"
    NICEOS_CRI_SOCKET="${NICEOS_CRI_SOCKET:-unix:///run/containerd/containerd.sock}"
    NICEOS_KUBE_PROXY_MODE="${NICEOS_KUBE_PROXY_MODE:-iptables}"
    NICEOS_ADVERTISE_ADDRESS="${NICEOS_ADVERTISE_ADDRESS:-}"
    NICEOS_RESOLV_CONF="${NICEOS_RESOLV_CONF:-}"
    NICEOS_NODE_LABELS="${NICEOS_NODE_LABELS:-node.niceos.ru/profile=core-node,node.niceos.ru/kubernetes=1.35}"
    NICEOS_KUBEADM_OUTPUT="${NICEOS_KUBEADM_OUTPUT:-/etc/kubernetes/niceos-kubeadm-init.yaml}"
}

parse_args() {
    if [ $# -gt 0 ]; then
        case "$1" in
            render|images-list|images-pull|init)
                COMMAND="$1"
                shift
                ;;
        esac
    fi

    while [ $# -gt 0 ]; do
        case "$1" in
            --profile)
                PROFILE="${2:-}"
                shift 2
                ;;
            --output)
                OUTPUT="${2:-}"
                shift 2
                ;;
            --advertise-address)
                ADVERTISE_ADDRESS="${2:-}"
                shift 2
                ;;
            --resolv-conf)
                RESOLV_CONF="${2:-}"
                shift 2
                ;;
            --kubernetes-version)
                KUBERNETES_VERSION="${2:-}"
                shift 2
                ;;
            --image-repository)
                IMAGE_REPOSITORY="${2:-}"
                shift 2
                ;;
            --cluster-name)
                CLUSTER_NAME="${2:-}"
                shift 2
                ;;
            --service-subnet)
                SERVICE_SUBNET="${2:-}"
                shift 2
                ;;
            --pod-subnet)
                POD_SUBNET="${2:-}"
                shift 2
                ;;
            --cri-socket)
                CRI_SOCKET="${2:-}"
                shift 2
                ;;
            --kube-proxy-mode)
                KUBE_PROXY_MODE="${2:-}"
                shift 2
                ;;
            --node-labels)
                NODE_LABELS="${2:-}"
                shift 2
                ;;
            -h|--help)
                usage
                exit 0
                ;;
            *)
                die "Unknown option: $1"
                ;;
        esac
    done
}

normalize_profile() {
    case "$PROFILE" in
        flannel|calico|cilium)
            ;;
        *)
            die "Unsupported profile '$PROFILE'. Supported profiles: flannel, calico, cilium."
            ;;
    esac
}

resolve_values() {
    normalize_profile

    KUBERNETES_VERSION="${KUBERNETES_VERSION:-$NICEOS_KUBERNETES_VERSION}"
    IMAGE_REPOSITORY="${IMAGE_REPOSITORY:-$NICEOS_IMAGE_REPOSITORY}"
    CLUSTER_NAME="${CLUSTER_NAME:-$NICEOS_CLUSTER_NAME}"
    SERVICE_SUBNET="${SERVICE_SUBNET:-$NICEOS_SERVICE_SUBNET}"
    CRI_SOCKET="${CRI_SOCKET:-$NICEOS_CRI_SOCKET}"
    KUBE_PROXY_MODE="${KUBE_PROXY_MODE:-$NICEOS_KUBE_PROXY_MODE}"
    NODE_LABELS="${NODE_LABELS:-$NICEOS_NODE_LABELS}"
    OUTPUT="${OUTPUT:-$NICEOS_KUBEADM_OUTPUT}"

    if [ -z "$ADVERTISE_ADDRESS" ]; then
        ADVERTISE_ADDRESS="${NICEOS_ADVERTISE_ADDRESS:-}"
    fi
    if [ -z "$ADVERTISE_ADDRESS" ]; then
        ADVERTISE_ADDRESS="$(detect_advertise_address)"
    fi

    if [ -z "$RESOLV_CONF" ]; then
        RESOLV_CONF="${NICEOS_RESOLV_CONF:-}"
    fi
    if [ -z "$RESOLV_CONF" ]; then
        RESOLV_CONF="$(detect_resolv_conf)"
    fi

    if [ -z "$POD_SUBNET" ]; then
        case "$PROFILE" in
            flannel) POD_SUBNET="$NICEOS_POD_SUBNET_FLANNEL" ;;
            calico)  POD_SUBNET="$NICEOS_POD_SUBNET_CALICO" ;;
            cilium)  POD_SUBNET="$NICEOS_POD_SUBNET_CILIUM" ;;
        esac
    fi

    [ -n "$KUBERNETES_VERSION" ] || die "Kubernetes version is empty."
    [ -n "$IMAGE_REPOSITORY" ] || die "Image repository is empty."
    [ -n "$CLUSTER_NAME" ] || die "Cluster name is empty."
    [ -n "$SERVICE_SUBNET" ] || die "Service subnet is empty."
    [ -n "$POD_SUBNET" ] || die "Pod subnet is empty."
    [ -n "$CRI_SOCKET" ] || die "CRI socket is empty."
    [ -n "$KUBE_PROXY_MODE" ] || die "kube-proxy mode is empty."
    [ -n "$RESOLV_CONF" ] || die "resolvConf is empty."
    [ -n "$ADVERTISE_ADDRESS" ] || die "advertiseAddress is empty."
}

render_config() {
    local template="/usr/share/niceos/kubernetes/kubeadm/profiles/init-${PROFILE}.yaml.in"

    [ -f "$template" ] || die "Template not found: $template"

    mkdir -p "$(dirname "$OUTPUT")"

    sed \
        -e "s|__ADVERTISE_ADDRESS__|$(sed_escape "$ADVERTISE_ADDRESS")|g" \
        -e "s|__CRI_SOCKET__|$(sed_escape "$CRI_SOCKET")|g" \
        -e "s|__NODE_LABELS__|$(sed_escape "$NODE_LABELS")|g" \
        -e "s|__CLUSTER_NAME__|$(sed_escape "$CLUSTER_NAME")|g" \
        -e "s|__KUBERNETES_VERSION__|$(sed_escape "$KUBERNETES_VERSION")|g" \
        -e "s|__IMAGE_REPOSITORY__|$(sed_escape "$IMAGE_REPOSITORY")|g" \
        -e "s|__POD_SUBNET__|$(sed_escape "$POD_SUBNET")|g" \
        -e "s|__SERVICE_SUBNET__|$(sed_escape "$SERVICE_SUBNET")|g" \
        -e "s|__RESOLV_CONF__|$(sed_escape "$RESOLV_CONF")|g" \
        -e "s|__KUBE_PROXY_MODE__|$(sed_escape "$KUBE_PROXY_MODE")|g" \
        "$template" > "$OUTPUT"

    chmod 0600 "$OUTPUT"

    log "Rendered kubeadm config: $OUTPUT"
    log "Profile: $PROFILE"
    log "Kubernetes: $KUBERNETES_VERSION"
    log "Image repository: $IMAGE_REPOSITORY"
    log "Advertise address: $ADVERTISE_ADDRESS"
    log "Pod subnet: $POD_SUBNET"
    log "Service subnet: $SERVICE_SUBNET"
    log "resolvConf: $RESOLV_CONF"
    log "CRI socket: $CRI_SOCKET"
    log "kube-proxy mode: $KUBE_PROXY_MODE"
}

main() {
    if [ -f "$ENV_FILE" ]; then
        # shellcheck disable=SC1090
        . "$ENV_FILE"
    fi

    load_defaults
    parse_args "$@"
    resolve_values

    case "$COMMAND" in
        render)
            render_config
            ;;
        images-list)
            render_config
            kubeadm config images list --config "$OUTPUT"
            ;;
        images-pull)
            render_config
            kubeadm config images pull --config "$OUTPUT" --cri-socket="$CRI_SOCKET"
            ;;
        init)
            render_config
            kubeadm init --config "$OUTPUT"
            ;;
        *)
            die "Unsupported command: $COMMAND"
            ;;
    esac
}

main "$@"
