#!/usr/bin/env bash
set -e

help-table() {
    local -a rows
    local max_cmd=7 max_opt=6 max_desc=11

    rows+=("Command|Option|Description")
    rows+=("===ENV===| | ")
    rows+=("version||Show version of required tools.")
    rows+=("info||Show info about env.")
    rows+=("template-dotenv||Generate .env from .env.template.")
    rows+=("export-kubeconfig|[user][path]|Export kubeconfig file for service account.")
    rows+=("generate-kubconfig|[name]|Generate kubeconfig for deployment-updater.")
    rows+=("load-kubeconfig-from-keepass|[ctx]|Load kubeconfig file from KeePass.")
    rows+=("save-kubeconfig-to-keepass|[ctx]|Save kubeconfig file in KeePass.")
    rows+=("===DOCS===| | ")
    rows+=("init-zensical-docs||Initialize Zensical documentation build dependencies.")
    rows+=("docs||Generate Helm chart readmes.")
    rows+=("lint||Lint Helm charts.")
    rows+=("dev-zensical-docs||Run Zensical development server.")
    rows+=("build-zensical-docs||Build the Zensical documentation website.")
    rows+=("serve-zensical-docs||Serve the Zensical documentation build folder.")
    rows+=("===HOST===| | ")
    rows+=("start||Start kind Kubernetes cluster.")
    rows+=("start-and-prepare||Start and prepare Kubernetes cluster.")
    rows+=("load-image|[tag]|Load Docker image into kind cluster.")
    rows+=("delete-kind||Delete kind Kubernetes cluster.")
    rows+=("setup-hosts||Update hosts file.")
    rows+=("forward|[chart]|Forward port.")
    rows+=("===CHART===| | ")
    rows+=("list-charts||List local Helm charts.")
    rows+=("list-external-charts||List external Helm charts.")
    rows+=("list-cluster||List clusters.")
    rows+=("list-deployment||List deployments.")
    rows+=("list-context||List kubeconfig files.")
    rows+=("switch-context||Switch kubeconfig file.")
    rows+=("get-context||Show active kubeconfig.")
    rows+=("get-cluster||Show active cluster.")
    rows+=("update-dependencies|[chart]|Update dependencies for a chart.")
    rows+=("install-chart|[chart][values]|Install Helm chart.")
    rows+=("upgrade-release|[chart][values]|Upgrade Helm release.")
    rows+=("uninstall-release|[release]|Uninstall Helm release.")
    rows+=("===KUBECTL===| | ")
    rows+=("show-secrets|[chart]|Show secrets for chart.")
    rows+=("exec-bash|[chart]|Open bash shell in container.")
    rows+=("logs|[grep]|Select pods with grep and show logs.")
    rows+=("list||List Helm deployments.")
    rows+=("get-pods||List all pods.")
    rows+=("get-namespace||Show active namespace.")
    rows+=("login-oc||Refresh the auth token for OpenShift.")
    rows+=("run-job|[name]|Make a copy of job.")
    rows+=("watch-cluster-memory||Watch memory consumption of pods.")
    rows+=("===HELM===| | ")
    rows+=("add-repos||Add the required Helm repos.")
    rows+=("template-manifest|[chart][values]|Template all charts with Helm.")
    rows+=("package-repo|[chart]|Package Helm charts.")
    rows+=("index-repo||Index Helm chart.")
    rows+=("remove-repo||Remove the kubernetes-build repo.")
    rows+=("commit||Commit .tgz files.")
    rows+=("===LLM===| | ")
    rows+=("commit-with-llm||Commit with llm generated commit message.")
    rows+=("update-with-llm|[glob][prompt]|Feed files matching glob to LLM and apply changes.")
    rows+=("===TEST===| | ")
    rows+=("test-chart-cluster-issuer||Test cluster issuer.")
    rows+=("test-chart-odoo|[cleanup]|Setup Odoo chart and dependencies.")
    rows+=("test-chart-nextcloud|[cleanup]|Setup Nextcloud chart and dependencies.")

    for row in "${rows[@]}"; do
        IFS='|' read -r cmd opt desc <<< "$row"
        (( ${#cmd} > max_cmd )) && max_cmd=${#cmd}
        (( ${#opt} > max_opt )) && max_opt=${#opt}
        (( ${#desc} > max_desc )) && max_desc=${#desc}
    done

    printf '| %-*s | %-*s | %-*s |\n' \
        "$max_cmd" "Command" \
        "$max_opt" "Option" \
        "$max_desc" "Description"

    printf '|-%*s-|-%*s-|-%*s-|\n' \
        "$max_cmd" "$(printf '%*s' "$max_cmd" '' | tr ' ' '-')" \
        "$max_opt" "$(printf '%*s' "$max_opt" '' | tr ' ' '-')" \
        "$max_desc" "$(printf '%*s' "$max_desc" '' | tr ' ' '-')"

    local first_row=true
    for row in "${rows[@]:1}"; do
        IFS='|' read -r cmd opt desc <<< "$row"
        if [[ "$cmd" =~ ^===.*===$ ]]; then
            group_name="${cmd/===/}"
            group_name="${group_name/===/}"
            
            if [ "$first_row" = true ]; then
                first_row=false
            else
                printf '|-%*s-|-%*s-|-%*s-|\n' \
                    "$max_cmd" "$(printf '%*s' "$max_cmd" '' | tr ' ' '-')" \
                    "$max_opt" "$(printf '%*s' "$max_opt" '' | tr ' ' '-')" \
                    "$max_desc" "$(printf '%*s' "$max_desc" '' | tr ' ' '-')"
            fi
            
            local left_pad=$(( (max_cmd - ${#group_name}) / 2 ))
            local right_pad=$(( max_cmd - left_pad - ${#group_name} ))
            printf '| %*s%s%*s | %*s | %*s |\n' \
                "$left_pad" "" \
                "$group_name" \
                "$right_pad" "" \
                "$max_opt" "" \
                "$max_desc" ""
            
            printf '|-%*s-|-%*s-|-%*s-|\n' \
                "$max_cmd" "$(printf '%*s' "$max_cmd" '' | tr ' ' '-')" \
                "$max_opt" "$(printf '%*s' "$max_opt" '' | tr ' ' '-')" \
                "$max_desc" "$(printf '%*s' "$max_desc" '' | tr ' ' '-')"
        else
            first_row=false
            printf '| %-*s | %-*s | %-*s |\n' \
                "$max_cmd" "$cmd" \
                "$max_opt" "$opt" \
                "$max_desc" "$desc"
        fi
    done
}

help() {
    echo
    if [[ -n "$1" ]]; then
        help-table | grep -i "$1" | column -t -s'|'
    else
        echo 'task <command> [options]'
        echo
        echo 'commands:'
        echo
        help-table
    fi
    echo
}

# Default vars

OC_PROJECT=${OC_PROJECT:="odoo"}
CONTAINER_REGISTRY=${CONTAINER_REGISTRY:="mintsystem/"}
CONTAINER_CONFIG="$HOME/.docker/$CONTAINER_REGISTRY"

# Dynamic vars

pass_entry=$(echo "env/${PWD##*/}" | tr '[:upper:]' '[:lower:]')
keepass_entry=$(echo "env/${PWD##*/}" | tr '[:upper:]' '[:lower:]')

get-namespace() {
    if kubectl-ctx &> /dev/null; then
        echo $(kubectl-ns -c 2> /dev/null)
    fi
}
namespace="$(get-namespace)"

get-context() {
    local alias="$(basename "$(readlink "$HOME/.kube/config" 2>/dev/null)" 2>/dev/null | cut -d '.' -f 2)"
    echo "${alias:-loc}"
}
context_alias="$(get-context)"

# Import commands

clone-taskfile(){
    if [[ ! -d "$HOME/taskfile.build" ]]; then
        echo -e "\033[38;5;214mGit\033[0m: Clone taskfile repo"
        git clone https://git.taskfile.build "$HOME/taskfile.build"
    else
        echo -e "\033[38;5;214mGit\033[0m: Pull taskfile repo"
        git -C "$HOME/taskfile.build" pull
    fi
}

if [[ -d "$HOME/taskfile.build/bin" ]]; then
    for file in "$HOME/taskfile.build/bin/"*; do
        if [[ -f "$file" ]]; then
            source "$file"
        fi
    done
fi

# Env Commands

version() {
    kind version
    helm version
}

info() {
    echo "Cluster: $(get-cluster)"
    echo "Context: $(get-context)"
    echo "Namespace: $(get-namespace)"
}

setup-hosts() {
    echo 'Adding entries to /etc/hosts ...'
    # Cluster
    grep -qxF '127.0.0.1 knd.local' /etc/hosts || echo '127.0.0.1 knd.local' | sudo tee -a /etc/hosts > /dev/null
    # Odoo
    grep -qxF '127.0.0.1 odoo.local' /etc/hosts || echo '127.0.0.1 odoo.local' | sudo tee -a /etc/hosts > /dev/null
    grep -qxF '127.0.0.1 odoo.knd.local' /etc/hosts || echo '127.0.0.1 odoo.knd.local' | sudo tee -a /etc/hosts > /dev/null
    grep -qxF '127.0.0.1 staging.odoo.knd.local' /etc/hosts || echo '127.0.0.1 staging.odoo.knd.local' | sudo tee -a /etc/hosts > /dev/null
    # Odoo with different namespace
    grep -qxF '127.0.0.1 restic.local' /etc/hosts || echo '127.0.0.1 restic.local' | sudo tee -a /etc/hosts > /dev/null
    grep -qxF '127.0.0.1 restic.knd.local' /etc/hosts || echo '127.0.0.1 restic.knd.local' | sudo tee -a /etc/hosts > /dev/null
    grep -qxF '127.0.0.1 staging.restic.knd.local' /etc/hosts || echo '127.0.0.1 staging.restic.knd.local' | sudo tee -a /etc/hosts > /dev/null
    # Nextcloud
    grep -qxF '127.0.0.1 nextcloud.local' /etc/hosts || echo '127.0.0.1 nextcloud.local' | sudo tee -a /etc/hosts > /dev/null
    grep -qxF '127.0.0.1 nextcloud.knd.local' /etc/hosts || echo '127.0.0.1 nextcloud.knd.local' | sudo tee -a /etc/hosts > /dev/null
    # Collabora
    grep -qxF '127.0.0.1 doc.nextcloud.knd.local' /etc/hosts || echo '127.0.0.1 doc.nextcloud.knd.local' | sudo tee -a /etc/hosts > /dev/null
    # Removed hugo and vuepress hosts (replaced by taskfile-build)
}

test-chart-odoo() {
    if [[ "$1" = 'cleanup' ]]; then
        echo 'Cleanup environment'
        delete-kind
    else
        start-and-prepare

        update-dependencies odoo
        install-chart odoo test_values/knd/odoo.yaml
        kubectl wait --for=condition=Available --timeout=300s deployments/odoo-odoo
    fi
}

test-chart-nextcloud() {
    if [[ "$1" = 'cleanup' ]]; then
        echo 'Cleanup environment'
        delete-kind
    else
        start
        add-repos
        info
        install-chart traefik
        kubectl wait --namespace traefik --for=condition=Available --timeout=60s deployments/traefik
        install-chart cnpg
        kubectl wait --namespace cnpg-system --for=condition=Available --timeout=60s deployments/cnpg-cloudnative-pg
        kubectl create secret generic nextcloud-creds --from-literal=NEXTCLOUD_ADMIN_USER="test" --from-literal=NEXTCLOUD_ADMIN_PASSWORD="test"
        install-chart nextcloud test_values/knd/nextcloud.yaml
    fi
}

export-kubeconfig() {
  if test -z "$1"; then echo '\$1 is empty.'; exit; fi
  if test -z "$2"; then echo '\$2 is empty.'; exit; fi

  local kube_user=$1
  local kube_output_file=$2
  kubectl config set-cluster default --server=https://k3s.raspberrypi.build --kubeconfig=$kube_output_file
  kubectl config set-context default --cluster=default --user=$kube_user --kubeconfig=$kube_output_file
  kubectl config set-credentials "$kube_user" --token=$(kubectl create token $kube_user --duration=87600h) --kubeconfig=$kube_output_file
  kubectl config use-context default --kubeconfig=$kube_output_file

  echo "Kubeconfig exported for user $kube_user to file $kube_output_file"
}

generate-kubconfig() {
  if test -z "$1"; then echo '\$1 is empty.'; exit; fi

  local name="$1"
  local cluster=$(get-cluster)
  local kubeconfig_file="values/.kube/config.${cluster}-${name}"
  local secret_name="${name}-sa-token"

  local server=$(kubectl config view --minify --output 'jsonpath={.clusters[0].cluster.server}')
  local ca=$(kubectl get secret/$secret_name -o jsonpath='{.data.ca\.crt}')
  local token=$(kubectl get secret/$secret_name -o jsonpath='{.data.token}' | base64 --decode)
  local namespace=$(kubectl get secret/$secret_name -o jsonpath='{.data.namespace}' | base64 --decode)

  echo "apiVersion: v1
kind: Config
clusters:
- name: default-cluster
  cluster:
    certificate-authority-data: ${ca}
    server: ${server}
contexts:
- name: default-context
  context:
    cluster: default-cluster
    namespace: ${namespace}
    user: default-user
current-context: default-context
users:
- name: default-user
  user:
    token: ${token}" > "$kubeconfig_file"

  echo "Kubeconfig generated for $name at $kubeconfig_file"
}

# Host Commands

start() {
    if [[ "$context_alias" != "loc" ]]; then
        echo "Current context alias is '$context_alias' and not 'loc'."
        exit 1
    fi

    echo 'Start kind.'
    kind create cluster
}

start-and-prepare() {
    start
    add-repos
    info

    echo "Install traefik."
    install-chart traefik
    kubectl wait --namespace traefik --for=condition=Available --timeout=60s deployments/traefik
    
    echo "Install the CloudNativePG."
    install-chart cnpg
    kubectl wait --namespace cnpg-system --for=condition=Available --timeout=60s deployments/cnpg-cloudnative-pg
    
    echo "Install k8up."
    install-chart k8up

    #FIXME: This does not work in GitHub runner env.
    echo "Load Odoo image."
    docker pull mintsystem/odoo:19.0.20260926 
    load-image mintsystem/odoo:19.0.20260926
}

load-image() {
    if test -z "$1"; then echo '\$1 is empty.'; exit; fi

    if kind &> /dev/null && kind get clusters | grep -q 'kind'; then
        echo 'Kind cluster is available. Checking for local Docker images...'

        if docker images -q "$1" > /dev/null; then
            echo 'Loading local Odoo image into Kind cluster...'
            kind load docker-image "$1"
        fi
    fi
}

delete-kind() {
    echo 'Delete kind.'
    kind delete cluster
}

forward() {
    if test -z "$1"; then echo '\$1 is empty.'; exit; fi
    "forward-$1"
}

forward-postgres() {
    local port=5432
    echo "Connect to http://localhost:$port url in your browser."
    kubectl port-forward service/postgres $port:5432
}

forward-odoo() {
    local port=8080
    echo "Open http://localhost:$port url in your browser."
    kubectl port-forward service/odoo $port:8069
}

forward-odoo-staging() {
    local port=8080
    echo "Open http://localhost:$port url in your browser."
    kubectl port-forward service/odoo-staging $port:8069
}

forward-nextcloud() {
    local port=8080
    echo "Open http://localhost:$port url in your browser."
    kubectl port-forward service/nextcloud $port:8080
}

forward-prometheus-agent() {
    local port=9090
    echo "Connect to http://localhost:$port to access the Prometheus agent."
    kubectl port-forward -n prometheus deployment/prometheus-agent $port:9090
}

forward-traefik() {
    local port=8080
    kubectl get ingresses
    echo "Open http://\$namespace.knd.local:$port url in your browser."
    kubectl port-forward -n traefik svc/traefik $port:80
}

# Chart Commands

list-charts() {
    for dir in */; do
        dir_name="${dir%/}"
        if [ -f "$dir/Chart.yaml" ]; then
            echo "$dir_name"
        fi
    done
}

list-external-charts() {
    echo 'cert-manager'
    echo 'cnpg'
    # echo 'haproxy-ingress'
    echo 'k8up'
    echo 'metrics-server'
    echo 'traefik'
}

list-cluster() {
    local -a rows
    local max_name=4 max_alias=5 max_base=4

    for file in $(grep -l "^kind: cluster$" values/*.yaml 2>/dev/null); do
        local name=$(yq eval '.name' "$file" 2>/dev/null)
        local alias=$(yq eval '.alias' "$file" 2>/dev/null)
        local base=$(basename "$file" .yaml)
        [[ -z "$name" ]] && name="(not set)"
        [[ -z "$alias" ]] && alias="(not set)"

        rows+=("$name|$alias|$base")

        (( ${#name} > max_name )) && max_name=${#name}
        (( ${#alias} > max_alias )) && max_alias=${#alias}
        (( ${#base} > max_base )) && max_base=${#base}
    done

    [[ ${#rows[@]} -eq 0 ]] && {
        echo "No cluster files found."
        return
    }

    printf '| %-*s | %-*s | %-*s |\n' \
        "$max_name" "Name" \
        "$max_alias" "Alias" \
        "$max_base" "Cname"

    printf '|-%*s-|-%*s-|-%*s-|\n' \
        "$max_name" "$(printf '%*s' "$max_name" '' | tr ' ' '-')" \
        "$max_alias" "$(printf '%*s' "$max_alias" '' | tr ' ' '-')" \
        "$max_base" "$(printf '%*s' "$max_base" '' | tr ' ' '-')"

    printf '%s\n' "${rows[@]}" | sort | while IFS='|' read -r name alias base; do
        printf '| %-*s | %-*s | %-*s |\n' \
            "$max_name" "$name" \
            "$max_alias" "$alias" \
            "$max_base" "$base"
    done
}

list-deployment() {
    local -a rows
    local max_namespace=9 max_cluster=7 max_chart=5

    for file in $(grep -l "^kind: deployment$" values/*.yaml 2>/dev/null); do
        local base=$(basename "$file" .yaml)
        local namespace="${base%%.*}"
        local cluster="${base#*.}"
        local chart=$(yq eval '.chart' "$file" 2>/dev/null)
        [[ "${chart}" == "null" ]] && chart=""

        rows+=("$namespace|$cluster|$chart")

        (( ${#namespace} > max_namespace )) && max_namespace=${#namespace}
        (( ${#cluster} > max_cluster )) && max_cluster=${#cluster}
        (( ${#chart} > max_chart )) && max_chart=${#chart}
    done

    [[ ${#rows[@]} -eq 0 ]] && {
        echo "No deployment files found."
        return
    }

    printf '| %-*s | %-*s | %-*s |\n' \
        "$max_namespace" "Namespace" \
        "$max_cluster" "Cluster" \
        "$max_chart" "Chart"

    printf '|-%*s-|-%*s-|-%*s-|\n' \
        "$max_namespace" "$(printf '%*s' "$max_namespace" '' | tr ' ' '-')" \
        "$max_cluster" "$(printf '%*s' "$max_cluster" '' | tr ' ' '-')" \
        "$max_chart" "$(printf '%*s' "$max_chart" '' | tr ' ' '-')"

    printf '%s\n' "${rows[@]}" | sort | while IFS='|' read -r namespace cluster chart; do
        printf '| %-*s | %-*s | %-*s |\n' \
            "$max_namespace" "$namespace" \
            "$max_cluster" "$cluster" \
            "$max_chart" "$chart"
    done
}

list-context() {
    local -a rows
    local max_name=4 max_path=4

    for file in values/.kube/config.*; do
        [[ ! -f "$file" ]] && continue
        local name=$(basename "$file" | sed 's/^config\.//')
        
        rows+=("$name|$file")

        (( ${#name} > max_name )) && max_name=${#name}
        (( ${#file} > max_path )) && max_path=${#file}
    done

    [[ ${#rows[@]} -eq 0 ]] && {
        echo "No context files found in values/.kube/config.*"
        return
    }

    printf '| %-*s | %-*s |\n' \
        "$max_name" "Name" \
        "$max_path" "Path"

    printf '|-%*s-|-%*s-|\n' \
        "$max_name" "$(printf '%*s' "$max_name" '' | tr ' ' '-')" \
        "$max_path" "$(printf '%*s' "$max_path" '' | tr ' ' '-')"

    printf '%s\n' "${rows[@]}" | sort | while IFS='|' read -r name path; do
        printf '| %-*s | %-*s |\n' \
            "$max_name" "$name" \
            "$max_path" "$path"
    done
}

switch-context() {
    if test -z "$1"; then echo '\$1 is empty.'; exit; fi
    mkdir -p "$HOME/.kube"
    local link_name="$HOME/.kube/config"
    local target="$PWD/values/.kube/config.$1"

    if [[ ! -f "$target" ]]; then
        echo "Error: Target config file '$target' does not exist." >&2
        return 1
    fi

    echo "Remove $link_name"
    rm "$link_name" || true
    
    echo "Setup symlink from $link_name to $target"
    touch "$target"
    ln -s "$target" "$link_name"
    chmod 0644 "$target"

    local context_alias="${1%%-*}"
    local cluster_file=""
    for file in values/*.yaml; do
        if [[ "$(yq eval '.kind' "$file" 2>/dev/null)" == "cluster" && "$(yq eval '.alias' "$file" 2>/dev/null)" == "$context_alias" ]]; then
            cluster_file="$file"
            break
        fi
    done

    if [[ -n "$cluster_file" && "$(yq eval '.openshift' "$cluster_file" 2>/dev/null)" == "true" ]]; then
        local server
        server="$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')"
        echo "Login to OpenShift cluster"
        oc login --web --server="$server"
    fi

    echo 'Select namespace'
    kubectl-ns
}

get-cluster() {
    basename "$(readlink "$HOME/.kube/config")" | cut -d. -f2 | cut -d- -f1 
}

update-dependencies() {
    if test -z "$1"; then echo '\$1 is empty.'; exit; fi
    helm dependency update "$1"
}

install-chart() {
    if test -z "$1"; then echo '\$1 is empty.'; exit; fi
    if test -z "$2"; then echo '\$2 is empty.'; exit; fi

    local values_file="$2"
    local expected_namespace=""
    if [[ "$values_file" =~ ^values/[^/]+/[^/]+\.yaml$ ]]; then
        expected_namespace="$(basename "$values_file" .yaml)"
    elif [[ "$values_file" =~ ^values/[^/]+/[^/]+/.+\.yaml$ ]]; then
        expected_namespace="$(echo "$values_file" | cut -d '/' -f3)"
    fi
    if [[ -n "$expected_namespace" && "$namespace" != "$expected_namespace" ]]; then
        echo "Error: Active namespace '$namespace' does not match values file namespace '$expected_namespace'."
        exit 1
    fi

    if [[ "$(get-context)" = 'axo' ]]; then
        echo "Set oc project to $OC_PROJECT."
        oc project "$OC_PROJECT"
    fi
    local release_name="$1"

    if [[ "$1" = 'cluster-issuer' ]]; then
        "install-$1" "$2"
    elif list-charts | grep -q "$1"; then
        echo "Install chart $1 as release $release_name:"
        helm install "$release_name" "$1" \
            --values "$2"
    else
        "install-$1" "$2"
    fi
}

install-cnpg() {
    echo 'Install cnpg/cloudnative-pg.'
    helm upgrade --install cnpg \
      --namespace cnpg-system \
      --create-namespace \
      cnpg/cloudnative-pg
}

install-k8up() {
    echo 'Install k8up-io/k8up.'
    helm install k8up k8up-io/k8up \
        --create-namespace --namespace k8up
}

install-cert-manager() {
    echo 'Install jetstack/cert-manager.'
    helm install \
      cert-manager jetstack/cert-manager \
      --namespace cert-manager \
      --create-namespace \
      --version v1.18.2 \
      --set crds.enabled=true \
      --set config.featureGates.ACMEHTTP01IngressPathTypeExact=false

    # https://github.com/Infomaniak/cert-manager-webhook-infomaniak
    echo 'Apply Infomaniak webhook'
    kubectl apply -f https://github.com/Infomaniak/cert-manager-webhook-infomaniak/releases/download/v0.3.1/rendered-manifest.yaml
}

install-cluster-issuer() {
    echo 'Install clusterissuer.'
    helm install clusterissuer cluster-issuer \
        --namespace cert-manager \
        --values "$1"
}

install-traefik() {
    echo 'Install traefik/traefik.'
    helm install traefik traefik/traefik \
        --create-namespace --namespace traefik
}

install-metrics-server() {
    echo 'Install metrics-server/metrics-server.'
    local values_args=()
    if [[ -n "$1" ]]; then
        values_args+=(--values "$1")
    fi
    helm install metrics-server metrics-server/metrics-server \
        --create-namespace --namespace metrics-server \
        --set-json 'args=["--kubelet-insecure-tls"]' \
        "${values_args[@]}"
}

upgrade-release() {
    if test -z "$1"; then echo '\$1 is empty.'; exit; fi
    if test -z "$2"; then echo '\$2 is empty.'; exit; fi

    local values_file="$2"
    local expected_namespace=""
    if [[ "$values_file" =~ ^values/[^/]+/[^/]+\.yaml$ ]]; then
        expected_namespace="$(basename "$values_file" .yaml)"
    elif [[ "$values_file" =~ ^values/[^/]+/[^/]+/.+\.yaml$ ]]; then
        expected_namespace="$(echo "$values_file" | cut -d '/' -f3)"
    fi
    if [[ -n "$expected_namespace" && "$namespace" != "$expected_namespace" ]]; then
        echo "Error: Active namespace '$namespace' does not match values file namespace '$expected_namespace'."
        exit 1
    fi

    local release_name="$1"

    if list-charts | grep -q "$1"; then
        echo "Upgrade $1 chart for release $release_name:"
        helm upgrade "$release_name" "$1" \
            --values "$2"
    else
        "upgrade-$1" "$2"
    fi
}

upgrade-cert-manager() {
    echo 'Upgrade jetstack/cert-manager.'
    helm upgrade \
        cert-manager jetstack/cert-manager \
        --namespace cert-manager \
        --version v1.18.2 \
        --set crds.enabled=true \
        --set config.featureGates.ACMEHTTP01IngressPathTypeExact=false

    # https://github.com/Infomaniak/cert-manager-webhook-infomaniak
    echo 'Apply Infomaniak webhook'
    kubectl apply -f https://github.com/Infomaniak/cert-manager-webhook-infomaniak/releases/download/v0.3.1/rendered-manifest.yaml
}

upgrade-cluster-issuer() {
    echo 'Upgrade cluster-issuer.'
    helm upgrade \
        clusterissuer cluster-issuer \
        --namespace cert-manager \
        --values "$2"
}

uninstall-release() {
    if test -z "$1"; then echo '\$1 is empty.'; exit; fi
    local release_name="$1"

    echo "Uninstall release $release_name:"

    local namespace=""
    case "$release_name" in
        cert-manager) namespace="cert-manager" ;;
        cnpg) namespace="cnpg-system" ;;
        k8up) namespace="k8up" ;;
        metrics-server) namespace="metrics-server" ;;
        traefik) namespace="traefik" ;;
    esac

    if [[ -n "$namespace" ]]; then
        helm uninstall "$release_name" --namespace "$namespace"
    else
        helm uninstall "$release_name"
    fi
}

uninstall-haproxy-ingress() {
    echo 'Uninstall ingress-nginx release.'
    helm uninstall haproxy-ingress/haproxy-ingress --namespace haproxy-ingress
    kubectl delete namespace haproxy-ingress
}

uninstall-ingress-nginx() {
    echo 'Uninstall ingress-nginx release.'
    helm uninstall ingress-nginx --namespace ingress-nginx
    kubectl delete namespace ingress-nginx
}

run-job() {
    if test -z "$1"; then echo '\$1 is empty.'; exit 1; fi
    local job_name="$1"

    echo "Run job: $job_name"
    kubectl delete job ${job_name}-run --ignore-not-found
    kubectl create job --from=cronjob/${job_name} ${job_name}-run
}

# kubectl Commands

show-secrets() {
    if test -z "$1"; then echo '\$1/chart is empty.'; exit; fi

    if [[ "$1" = 'postgres' ]]; then
        echo 'postgres/user:'
        kubectl get secret postgres -o jsonpath="{.data.user}" | base64 --decode && echo
        echo 'postgres/password:'
        kubectl get secret postgres -o jsonpath="{.data.password}" | base64 --decode && echo
    
    elif [[ "$1" = 'cluster-issuer' ]]; then
        echo 'cert-manager/infomaniak-api-credentials:'
        kubectl get secret -n cert-manager infomaniak-api-credentials -o jsonpath="{.data.access-token}" | base64 --decode && echo

    elif [[ "$1" = 'forgejo-runner' ]]; then
        echo 'forgejo-runner/forgejoInstanceToken:'
        kubectl get secret forgejo-runner -o jsonpath="{.data.forgejoInstanceToken}" | base64 --decode && echo

    elif [[ "$1" = 'nextcloud' ]]; then
        echo 'nextcloud/creds:'
        kubectl get secret nextcloud-creds -o jsonpath="{.data.nextcloud-username}" | base64 --decode && echo
        echo 'nextcloud/password:'
        kubectl get secret nextcloud-creds -o jsonpath="{.data.nextcloud-password}" | base64 --decode && echo
        echo 'nextcloud-postgresql-app:username:'
        kubectl get secret nextcloud-postgresql-app -o jsonpath="{.data.username}" | base64 --decode && echo
        echo 'nextcloud-postgresql-app:password:'
        kubectl get secret nextcloud-postgresql-app -o jsonpath="{.data.password}" | base64 --decode && echo
    fi
}

exec-bash() {
    if test -z "$1"; then echo '\$1 is empty.'; exit; fi
    kubectl exec -it "$1" -- bash
}

logs() {
    local pod_id=$(kubectl get pods -o=name --all-namespaces | grep "$1")
    kubectl logs "$pod_id"
}

get-pods() {
    kubectl get pods
}

watch-cluster-memory() {
    if ! kubectl get --raw /apis/metrics.k8s.io/ &> /dev/null; then
        echo 'Error: Metrics API is not available.'
        echo 'Install metrics-server to enable pod memory stats.'
        return 1
    fi

    watch -n 2 'echo "=== Nodes ==="; kubectl top nodes; echo; echo "=== Pods ==="; kubectl top pods --all-namespaces | { read -r header; echo "$header"; sort -k4 -n -r; }'
}

# Helm Commands

template-manifest() {
    if test -z "$1"; then echo '\$1 is empty.'; exit; fi
    if test -z "$2"; then echo '\$2 is empty.'; exit; fi
    local release_name="$1"

    if [[ "$1" = 'cluster-issuer' ]]; then
        helm --debug template "$release_name" cluster-issuer \
            --namespace cert-manager \
            --values "$2" > manifest.yaml
    elif list-charts | grep -q "$1"; then
        helm --debug template "$release_name" "$1" \
            --values "$2"
    fi
}

list() {
    helm list
}

package-repo() {
    if [[ -n "$1" ]]; then
        echo "Package $1 Helm chart."
        helm package "$1" --destination docs/
    else
        echo 'Package all Helm charts.'
        update-dependencies odoo
        update-dependencies prometheus-agent
        rm -f docs/*.tgz
        for chart in $(list-charts); do
            helm package "$chart" --destination docs/
        done
        index-repo
    fi
}

commit() {
    git config user.name 'Mint Bot'
    git config user.email 'bot@mint-system.ch'
    git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/Mint-System/Kubernetes-Build"
    git add docs/*.tgz docs/index.yaml
    git commit -m 'feat(helm): package revision update'
    git push || echo 'No changes to commit'
}

index-repo() {
    echo 'Update docs/index.yaml'
    helm repo index docs/
}

add-repos(){
    echo 'Add kubernetes-build.'
    helm repo add kubernetes-build https://kubernetes.build

    echo 'Add jetstack.'
    helm repo add jetstack https://charts.jetstack.io

    echo 'Add cnpg.'
    helm repo add cnpg https://cloudnative-pg.github.io/charts

    echo 'Add k8up.'
    helm repo add k8up-io https://k8up-io.github.io/k8up

    echo 'Add metrics-server.'
    helm repo add metrics-server https://kubernetes-sigs.github.io/metrics-server

    echo 'Add traefik.'
    helm repo add traefik https://helm.traefik.io/traefik
}

remove-repo(){
    echo 'Remove the kubernetes-build repo.'
    helm repo remove kubernetes-build
}

# Docs Commands

docs() {
    for repo in $(list-charts | grep -v '^deployment-updater$'); do
        pnpm run generate-readme --values "$repo/values.yaml" -r "$repo/README.md"
    done
}

lint() {
    for chart in $(list-charts); do
        helm lint "$chart"
    done
}

if [[ "$1" == *","* ]]; then
    # Split comma-separated commands
    IFS=',' read -ra COMMANDS <<< "$1"
    for cmd in "${COMMANDS[@]}"; do
        # Trim whitespace
        cmd=$(echo "$cmd" | xargs)
        if declare -f "$cmd" > /dev/null; then
            "$cmd"
        else
            echo "Unknown command: $1"
            help "$@"
            exit 1
        fi
    done
elif declare -f "$1" > /dev/null; then
    "$1" "${@:2}"
else
    case "$1" in
        *)
            echo "Unknown command: $1"
            help
            exit 1
            ;;
    esac
fi
