#!/bin/bash

set -eo pipefail

# Set default version
VERSION="9.2605.0"
IMAGE_VERSION=$VERSION
# Set default helm settings as empty
HELM_SETTINGS=""
HELM_CHART_PREFIX=""
HELM_CMD_ARGS=()
MARIADB_CHART_VERSION="4.4.3"
ENVOY_GATEWAY_CHART_VERSION="1.6.6"
CERT_MANAGER_CHART_VERSION="1.19.1"

# Function to generate a random password
generate_password() {
    openssl rand -base64 48 | tr -dc '[:alnum:]' | head -c 16
}

# Function to display script usage
function display_usage() {
  echo "### Usage: $0"
  echo -e "    --upgrade                                                \t upgrade an existing installation"
  echo -e "    --add-nginx-node-port                                    \t "
  echo -e "    --add-nginx-load-balancer                                \t "
  echo -e "    --namespace <namespace>                                  \t required"
  echo -e "    --domain <domain>                                        \t required"
  echo -e "    --container-registry <container_registry>                \t required"
  echo -e "    --registry-image-prefix <registry-image-prefix>          \t optional (default: nevis)"
  echo -e "    --registry-username <registry_username>                  \t required"
  echo -e "    --registry-password <registry_password>                  \t required"
  echo -e "    --cloudsmith-password <cloudsmith_password>              \t required if not using a separate helm repository"
  echo -e "    --nevisadmin-password <nevisadmin_password>              \t"
  echo -e "    --git-password <git_password>                            \t"
  echo -e "    --database-password <database_password>                  \t"
  echo -e "    --version <chart_version>                                \t"
  echo -e "    --helm-repository <helm_repository>                      \t"
  echo -e "    --helm-repository-username <helm_repository_password>    \t"
  echo -e "    --helm-repository-password <helm_repository_username>    \t"
  echo -e "    --cluster-context                                        \t switch to the given context"
  echo -e "    --skip-images                                            \t skip image pull from Cloudsmith"
  echo -e "    --no-prompt                                              \t answer y to every question"
  echo -e "    --helm-setting <setting>                                 \t add a custom helm value to the installation with --set"
  echo -e "    --image-version <image_version>                          \t"
  echo -e "    --routing-api <ingress|gateway>                          \t routing API type (default: gateway)"
  echo -e "    --help                                                   \t show this help"
}

check_helm() {
    # Check if Helm is installed
    if ! command -v helm &> /dev/null
    then
        # Ask the user if they want to install Helm
        read -r -p "### Helm is required for the installation process. Do you want to install Helm now? (y/n) " install_helm
        if [[ $install_helm == "y" ]]; then
            echo "### Installing Helm..."
            curl https://raw.githubusercontent.com/helm/helm/master/scripts/get-helm-3 | bash
        else
            echo "### Helm installation skipped. Please install Helm and re-run this script."
            exit 1
        fi
    else
        echo "### Helm is available, continuing the installation."
    fi
}

helm_login() {
    # Input validation
    if [[ -z "${HELM_REPOSITORY:-}" || -z "${HELM_REPOSITORY_USERNAME:-}" ]]; then
        echo "Error: HELM_REPOSITORY and HELM_REPOSITORY_USERNAME must be set" >&2
        return 1
    fi

    if [[ -z "${HELM_REPOSITORY_PASSWORD:-}" ]]; then
        echo "Warning: HELM_REPOSITORY_PASSWORD is empty" >&2
    fi

    echo "### Preparing Helm authentication for $HELM_REPOSITORY as $HELM_REPOSITORY_USERNAME"

    if [[ "$HELM_REPOSITORY" == oci://* ]]; then
        # === OCI Repository Path ===
        echo "OCI-based repository detected"

        # Extract registry host (everything before first /)
        local login_helm_repo="${HELM_REPOSITORY#oci://}"
        local registry_host="${login_helm_repo%%/*}"

        echo "OCI registry host for login: $registry_host"

        # Login to the registry (password via stdin = secure)
        if ! echo "$HELM_REPOSITORY_PASSWORD" | helm registry login "$registry_host" \
            --username "$HELM_REPOSITORY_USERNAME" \
            --password-stdin; then
            echo "Error: Helm registry login failed for $registry_host" >&2
            return 1
        fi

        # Ensure trailing slash (makes chart referencing consistent)
        [[ "${HELM_REPOSITORY: -1}" != "/" ]] && HELM_REPOSITORY="${HELM_REPOSITORY}/"

        # For OCI the chart reference is the full oci:// URL prefix + chart name.
        # Example usage later: helm install my-release "${HELM_CHART_PREFIX}my-chart" "${HELM_CMD_ARGS[@]}" ...
        HELM_CHART_PREFIX="${HELM_REPOSITORY}"
        HELM_CMD_ARGS=()
        if [[ -n "${VERSION:-}" ]]; then
            HELM_CMD_ARGS+=(--version "${VERSION}")
        fi

        echo "OCI login successful. Use full OCI URL for install."

    else
        # === Classic Helm Repository (HTTP/S) ===
        echo "Classic Helm repository detected — credentials will be passed at install/upgrade time"

        HELM_CHART_PREFIX=""
        HELM_CMD_ARGS=(--repo "${HELM_REPOSITORY}" --username "${HELM_REPOSITORY_USERNAME}" --password "${HELM_REPOSITORY_PASSWORD}")
        if [[ -n "${VERSION:-}" ]]; then
            HELM_CMD_ARGS+=(--version "${VERSION}")
        fi
    fi

    echo "Helm repo args ready:"
    echo "  ${HELM_CMD_ARGS[*]}"
}

read_inputs() {
    read_domain_input
    read_release_inputs
}

read_domain_input() {
    if [ -z "$DOMAIN" ]; then
        # Ask for the domain where nevisAdmin 4 will be accessible
        read -r -p "### Enter the domain where nevisAdmin 4 will be accessible, for example 'nevis.os3434.com'. This domain will be used for a DNS A record with the IP of the nginx loadbalancer.: " DOMAIN
    fi
}

read_release_inputs() {
    if [ -z "$RELEASE_NAMESPACE" ]; then
        # Enter the namespace where nevisAdmin4 will be installed:
        read -r -p "### Enter the namespace where nevisAdmin 4 will be installed: " RELEASE_NAMESPACE
    fi

    if [ -z "$CONTAINER_REGISTRY" ]; then
        # Ask for the container registry to push the images
        read -r -p "### Enter the container registry to push the images to, for example 'os.icr.io': " CONTAINER_REGISTRY
    fi

    if [ -z "$REGISTRY_USERNAME" ]; then
        read -r -p "### Enter the username for the registry: " REGISTRY_USERNAME
    fi

    if [ -z "$REGISTRY_PASSWORD" ]; then
        read -r -p "### Enter the password for the registry: " REGISTRY_PASSWORD
    fi

    if { [ "$get_images" == "y" ] || [ -z "$HELM_REPOSITORY_PASSWORD" ]; }  && [ -z "$CLOUDSMITH_PASSWORD" ]; then
        read -r -p "### Enter the password for the nevissecurity/rolling Cloudsmith repository, you can get the password by selecting one of the docker images in 'https://portal.nevis.net/portal/secure/releases/rolling': " CLOUDSMITH_PASSWORD
    fi

    HELM_REPOSITORY="${HELM_REPOSITORY:-https://dl.cloudsmith.io/basic/nevissecurity/rolling/helm/charts}"
    HELM_REPOSITORY_USERNAME="${HELM_REPOSITORY_USERNAME:-nevissecurity/rolling}"
    HELM_REPOSITORY_PASSWORD="${HELM_REPOSITORY_PASSWORD:-$CLOUDSMITH_PASSWORD}"
}

verify_cluster(){
    if [ -n "$CLUSTER_CONTEXT" ]; then
        echo "### Switching to Kubernetes context: $CLUSTER_CONTEXT"
        kubectl config use-context "$CLUSTER_CONTEXT"
    fi

    # Get the name of the current Kubernetes cluster
    current_cluster=$(kubectl config current-context)
    echo "### Connected to Kubernetes cluster: $current_cluster"

    # Ask the user if the current cluster is correct
    if [[ $prompt == "y" ]]; then
      read -r -p "### Is this the correct Kubernetes cluster? (y/n) " correct_cluster
    else
      correct_cluster=y
    fi
    if [[ $correct_cluster != "y" ]]; then
        echo "### Please connect to the correct Kubernetes cluster and re-run this script."
        exit 1
    fi
}

install_cert_manager(){
   if kubectl get crd | grep -q cert-manager.io &> /dev/null
   then
       echo "### Cert-manager CRDs found, cert-manager is already installed on the cluster. Continuing the installation."
       return
   fi
   # Ask the user whether to install cert-manager or not
   if [[ $prompt == "y" ]]; then
      read -r -p "### Cert-manager is required for the installation, do you want to install cert-manager now to the 'cert-manager' namespace? This requires cluster scoped access. (y/n) " INSTALL_CERT_MANAGER
   else
      INSTALL_CERT_MANAGER=y
   fi
   if [[ $INSTALL_CERT_MANAGER != "y" ]]; then
      echo "### Please install cert-manager based on https://cert-manager.io/docs/installation/ and re-run this script."
      exit 1
   fi
   if kubectl auth can-i create crd >/dev/null 2>&1 && kubectl auth can-i create namespace >/dev/null 2>&1; then
       echo "### User has the required permissions to install cert-manager"
   else
       echo "### No permission to apply CustomResourceDefinitions to the cluster and to create namespaces, please acquire the necessary permissions then rerun this script."
       echo "### You can also install cert-manager separately by following: https://cert-manager.io/docs/installation/ "
       exit 1
   fi
   # Install cert-manager using helm
   helm install cert-manager cert-manager --create-namespace --namespace cert-manager --repo https://charts.jetstack.io \
       --version $CERT_MANAGER_CHART_VERSION --wait --set installCRDs=true \
       --set config.apiVersion="controller.config.cert-manager.io/v1alpha1" \
       --set config.enableGatewayAPI=true \
       --set config.kind=ControllerConfiguration

}

install_crd() {
    if kubectl get crd | grep -q operator.nevis-security.ch &> /dev/null
    then
        echo "### nevisAdmin 4 CRDs are already installed on the cluster. Continuing the installation."
        return
    fi
    # Ask the user whether to install crds or not
    if [[ $prompt == "y" ]]; then
        read -r -p "### Installing nevisAdmin4-crd helm chart, proceed? This requires cluster scoped permissions (y/n) " INSTALL_CRD
    else
        INSTALL_CRD=y
    fi
    if [[ $INSTALL_CRD != "y" ]]; then
        echo "### Please install based on https://docs.nevis.net/nevisadmin4/Installation/Software-Installation/Kubernetes-Based-Installation/Installation-in-Kubernetes-Cluster#install-crd-chart and re-run this script."
        exit 1
    fi

    if kubectl auth can-i create crd >/dev/null 2>&1; then
        echo "### User has the required permissions to install CRDs"
    else
        echo "### No permission to apply CustomResourceDefinitions to the cluster, please acquire the necessary permissions then rerun this script."
        echo "### Or install manually based on https://docs.nevis.net/nevisadmin4/Installation/Software-Installation/Kubernetes-Based-Installation/Installation-in-Kubernetes-Cluster#install-crd-chart"
        exit 1
    fi
    helm install nevisadmin4-crd -n default "${HELM_CHART_PREFIX}nevisadmin4-crd" "${HELM_CMD_ARGS[@]}"
}

install_envoy() {
    if helm list -A -o json 2>/dev/null | grep -q '"chart":"gateway-helm-'; then
        echo "### Envoy Gateway is already installed on the cluster. Continuing the installation."
        return
    fi
    if [[ $prompt == "y" ]]; then
        read -r -p "### Installing Envoy Gateway helm chart, proceed? This requires cluster scoped permissions (y/n) " INSTALL_ENVOY
    else
        echo "### Installing Envoy Gateway $ENVOY_GATEWAY_CHART_VERSION"
        INSTALL_ENVOY=y
    fi
    if [[ $INSTALL_ENVOY != "y" ]]; then
        echo "### Please install based on https://gateway.envoyproxy.io/v1.0/install/install-helm/ and re-run this script."
        exit 1
    fi

    if kubectl auth can-i create crd >/dev/null 2>&1 && kubectl auth can-i create namespace >/dev/null 2>&1; then
        echo "### User has the required permissions to install Envoy Gateway"
    else
        echo "### No permission to apply CustomResourceDefinitions to the cluster and to create namespaces, please acquire the necessary permissions then rerun this script."
        exit 1
    fi

    helm upgrade --install envoy-gateway oci://docker.io/envoyproxy/gateway-helm \
        --version "$ENVOY_GATEWAY_CHART_VERSION" \
        --namespace envoy \
        --create-namespace \
        --wait 2>&1 | tr -cd '\11\12\15\40-\176'
    kubectl apply -f - <<'EOF'
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: envoy-proxy-conf
  namespace: envoy
spec:
  logging:
    level:
      default: warn
  mergeGateways: true
  backendTLS:
    ecdhCurves:
      - X25519MLKEM768
      - X25519
    maxVersion: "1.3"
    minVersion: "1.3"
---
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: envoy-gateway
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
  parametersRef:
    group: gateway.envoyproxy.io
    kind: EnvoyProxy
    name: envoy-proxy-conf
    namespace: envoy
EOF
}

upgrade_crd() {
    # Ask the user whether to install crds or not
    if [[ $prompt == "y" ]]; then
        read -r -p "### Upgrading nevisAdmin4-crd helm chart, proceed? This requires cluster scoped permissions (y/n) " UPGRADE_CRD
    else
        UPGRADE_CRD=y
    fi
    if [[ $UPGRADE_CRD != "y" ]]; then
        echo "### Please upgrade based on https://docs.nevis.net/nevisadmin4/Installation/Software-Upgrade/Kubernetes-Upgrade#upgrade-for-helm-installations."
        exit 1
    fi

    if kubectl auth can-i create crd >/dev/null 2>&1; then
        echo "### User has the required permissions to update CRDs"
    else
        echo "### No permission to update CustomResourceDefinitions to the cluster, please acquire the necessary permissions then rerun this script."
        echo "### Or upgrade manually based on https://docs.nevis.net/nevisadmin4/Installation/Software-Upgrade/Kubernetes-Upgrade#upgrade-for-helm-installations."
        exit 1
    fi
    helm upgrade nevisadmin4-crd -n default "${HELM_CHART_PREFIX}nevisadmin4-crd" "${HELM_CMD_ARGS[@]}"
}

push_images() {
    if [[ $get_images != "y" ]]; then
        return
    fi
    REGISTRY=docker.cloudsmith.io/nevissecurity/rolling
    # Ask the user whether to install crds or not
    read -r -p "### Nevis docker images will be pulled from $REGISTRY, and pushed to $CONTAINER_REGISTRY/$REGISTRY_IMAGE_PREFIX, proceed? (y/n) " PUSH_IMAGES
    if [[ $PUSH_IMAGES != "y" ]]; then
       echo "### Rerun this script and specify the correct registry."
       exit 1
    fi
    echo "### Login to registry $REGISTRY"
    docker login $REGISTRY -p "$CLOUDSMITH_PASSWORD" -u nevissecurity/rolling
    echo "### Login to registry $CONTAINER_REGISTRY"
    docker login "$CONTAINER_REGISTRY" -p "$REGISTRY_PASSWORD" -u "$REGISTRY_USERNAME"
    echo "### Pushing docker images to registry $CONTAINER_REGISTRY"
    declare -a images=("nevisproxy:$IMAGE_VERSION"
                       "nevisproxy-dbschema:$IMAGE_VERSION"
                       "nevislogrend:$IMAGE_VERSION"
                       "nevisfido:$IMAGE_VERSION"
                       "nevisfido-dbschema:$IMAGE_VERSION"
                       "nevisauth:$IMAGE_VERSION"
                       "nevisauth-dbschema:$IMAGE_VERSION"
                       "nevisidm:$IMAGE_VERSION"
                       "nevisidm-dbschema:$IMAGE_VERSION"
                       "nevismeta:$IMAGE_VERSION"
                       "nevismeta-dbschema:$IMAGE_VERSION"
                       "nevisadmin4:$IMAGE_VERSION"
                       "nevisadmin4-dbschema:$IMAGE_VERSION"
                       "nevisoperator:$IMAGE_VERSION"
                       "nevisadapt:$IMAGE_VERSION"
                       "nevisdetect-admin:$IMAGE_VERSION"
                       "nevisdetect-core:$IMAGE_VERSION"
                       "nevisdetect-entrypoint:$IMAGE_VERSION"
                       "nevisdetect-persistency:$IMAGE_VERSION"
                       "nevisadapt-dbschema:$IMAGE_VERSION"
                       "nevisdetect-persistency-dbschema:$IMAGE_VERSION"
                       "nevis-git-init:1.4.0"
                       "nevisdp:$IMAGE_VERSION"
                       "nevis-ubi-tools:1.5.0"
                       "nevis-base-flyway:$IMAGE_VERSION")
    for i in "${images[@]}"; do
        docker pull $REGISTRY/"$i"
        docker tag $REGISTRY/"$i" "$CONTAINER_REGISTRY"/"$REGISTRY_IMAGE_PREFIX"/"$i"
        docker push "$CONTAINER_REGISTRY"/"$REGISTRY_IMAGE_PREFIX"/"$i"
    done
}

install_mariadb() {
    namespace=$1
    tz_init_script=$'#!/bin/bash\nset -e\npassword_aux="${MARIADB_ROOT_PASSWORD:-}"\nif [[ -f "${MARIADB_ROOT_PASSWORD_FILE:-}" ]]; then\n    password_aux=$(cat "$MARIADB_ROOT_PASSWORD_FILE")\nfi\nif [[ "${MARIADB_REPLICATION_MODE}" != "slave" ]]; then\n    echo "Loading time zones"\n    if command -v mariadb >/dev/null 2>&1; then\n        mariadb-tzinfo-to-sql /usr/share/zoneinfo | mariadb -u root -p"${password_aux}" mysql\n    else\n        mariadb-tzinfo-to-sql /usr/share/zoneinfo | mysql -u root -p"${password_aux}" mysql\n    fi\nfi'
    echo "$tz_init_script" > /tmp/load_time_zones.sh
    mysql_conf=$'[mysqld]\nskip-name-resolve\nexplicit_defaults_for_timestamp\nbasedir=/usr\nplugin_dir=/usr/lib/mysql/plugin\nport=3306\nsocket=/run/mysqld/mysql.sock\ntmpdir=/tmp\nmax_allowed_packet=16M\nbind-address=*\npid-file=/run/mysqld/mysqld.pid\nlog-error=/var/lib/mysql/mariadb.err\ncharacter-set-server=utf8mb4\nslow_query_log=0\nslow_query_log_file=/var/lib/mysql/mariadb-slow.log\nlong_query_time=10.0\nmax_connections=1200\nconnect_timeout=5\nwait_timeout=600\ntransaction-isolation=READ-COMMITTED\nlower_case_table_names=1\nlog_bin_trust_function_creators=1\n\n[client]\nport=3306\nsocket=/run/mysqld/mysql.sock\ndefault-character-set=UTF8\nplugin_dir=/usr/lib/mysql/plugin\n\n[manager]\nport=3306\nsocket=/run/mysqld/mysql.sock\npid-file=/run/mysqld/mysql.pid'
    echo "$mysql_conf" > /tmp/mariadb_custom.cnf
    echo "### Installing MariaDB helm chart"
    helm upgrade --install mariadb mariadb \
        --namespace "$namespace" \
        --repo https://groundhog2k.github.io/helm-charts/ \
        --version "$MARIADB_CHART_VERSION" \
        --wait \
        --timeout 10m \
        --set settings.rootPassword.value="$DB_ROOT_PASSWORD" \
        --set userDatabase.name.value=gitea \
        --set userDatabase.user.value=gitea \
        --set userDatabase.password.value="$DB_ROOT_PASSWORD" \
        --set storage.requestedSize=5Gi \
        --set image.tag=11.4 \
        --set-file customScripts."load_time_zones\\.sh"=/tmp/load_time_zones.sh \
        --set-file customConfig=/tmp/mariadb_custom.cnf
    rm /tmp/load_time_zones.sh
    rm /tmp/mariadb_custom.cnf
}

install_nevisadmin4() {
    NEVISADMIN_PASSWORD="${NEVISADMIN_PASSWORD:-$(generate_password)}"
    GIT_USER=root
    GIT_PASSWORD="${GIT_PASSWORD:-$(generate_password)}"
    DB_ROOT_USER=root
    DB_ROOT_PASSWORD="${DATABASE_PASSWORD:-$(generate_password)}"
    DB_APP_PASSWORD="${DATABASE_PASSWORD:-$(generate_password)}"
    DB_SCHEMA_PASSWORD="${DATABASE_PASSWORD:-$(generate_password)}"
    DATABASE_HOST=mariadb
    RELEASE_NAME=nevisadmin4
    REGISTRY_IMAGE_PREFIX="${REGISTRY_IMAGE_PREFIX:-nevis}"

    if ! kubectl get namespace "$RELEASE_NAMESPACE" >/dev/null 2>&1; then
        echo "### Creating namespace for nevisAdmin 4."
        kubectl create namespace "$RELEASE_NAMESPACE"
    fi

    echo "### Creating registry secret"
    kubectl delete secret registry-secret --ignore-not-found  -n "$RELEASE_NAMESPACE"
    kubectl create secret docker-registry registry-secret --docker-server="$CONTAINER_REGISTRY" --docker-username="$REGISTRY_USERNAME" --docker-password="$REGISTRY_PASSWORD" -n "$RELEASE_NAMESPACE"

    if [ ! -f "neviskey" ]; then
        echo "### Generating private keys for git access"
        ssh-keygen -t ecdsa -C "kubernetes" -m PEM -P "" -f neviskey
    fi

    install_mariadb "$RELEASE_NAMESPACE"

    GIT_REPOSITORY_URL=ssh://git@gitea-ssh:2222/$GIT_USER/deploy.git

    if [[ $ROUTING_API == "gateway" ]]; then
        INGRESS_ENABLED=false
        GATEWAY_ENABLED=true
        GITEA_INGRESS_ENABLED=false
    else
        INGRESS_ENABLED=true
        GATEWAY_ENABLED=false
        GITEA_INGRESS_ENABLED=true
    fi

    if [[ "$DOMAIN" == *"local"* ]]; then
        CERT_MANAGER_ISSUER=selfsigned-issuer
    elif [[ $ROUTING_API == "gateway" ]]; then
        CERT_MANAGER_ISSUER=letsencrypt-prod-gw
    else
        CERT_MANAGER_ISSUER=letsencrypt-prod
    fi

    echo "### Installing nevisadmin4 helm chart"
    helm upgrade --install "$RELEASE_NAME" "${HELM_CHART_PREFIX}nevisadmin4" "${HELM_CMD_ARGS[@]}" --create-namespace --wait --timeout 10m -n "$RELEASE_NAMESPACE" \
        --set image.repository="$CONTAINER_REGISTRY" \
        --set image.imagePrefix="$REGISTRY_IMAGE_PREFIX" \
        --set git.repositoryUrl=$GIT_REPOSITORY_URL \
        --set git.username=$GIT_USER \
        --set image.imagePullSecretName=registry-secret \
        --set git.password="$GIT_PASSWORD" \
        --set database.host=$DATABASE_HOST \
        --set database.root.username=$DB_ROOT_USER \
        --set database.root.password="$DB_ROOT_PASSWORD" \
        --set nevisAdmin4.database.schemaUserPassword="$DB_SCHEMA_PASSWORD" \
        --set nevisAdmin4.database.applicationUserPassword="$DB_APP_PASSWORD" \
        --set nevisAdmin4.database.enableSSL=false \
        --set nevisAdmin4.domain="$DOMAIN" \
        --set nevisAdmin4.password="$NEVISADMIN_PASSWORD" \
        --set nevisAdmin4.certManagerIssuer="$CERT_MANAGER_ISSUER" \
        --set nevisAdmin4.ingress.enabled="$INGRESS_ENABLED" \
        --set nevisAdmin4.gateway.enabled="$GATEWAY_ENABLED" \
        --set bootstrap.nevisAdmin4.enabled=true \
        --set bootstrap.gitea.enabled=true \
        --set gitea.enabled=true \
        --set gitea.ingress.enabled="$GITEA_INGRESS_ENABLED" \
        --set 'gitea.ingress.hosts[0].host'="$DOMAIN" \
        --set 'gitea.ingress.hosts[0].paths[0].path'="/gitea(/|$)(.*)" \
        --set 'gitea.ingress.hosts[0].paths[0].pathType'=ImplementationSpecific \
        --set 'gitea.ingress.tls[0].hosts[0]'="$DOMAIN" \
        --set 'gitea.ingress.tls[0].secretName'=gitea-tls \
        --set gitea.ingress.className="nginx-nevisadmin4-$RELEASE_NAMESPACE" \
        --set gitea.gitea.config.server.ROOT_URL=http://"$DOMAIN"/ \
        --set gitea.gitea.config.database.USER=$DB_ROOT_USER \
        --set gitea.gitea.config.database.PASSWD="$DB_ROOT_PASSWORD" \
        --set gitea.gitea.admin.username=$GIT_USER \
        --set gitea.gitea.admin.password="$GIT_PASSWORD" \
        --set git.privateKey64="$(cat neviskey | base64 | tr -d '\n')" \
        --set git.publicKey64="$(cat neviskey.pub | base64 | tr -d '\n')" \
        --set git.knownHosts64=UkVQTEFDRV9NRQo= \
        --set nginx.enabled=false \
        --set nginx.controller.scope.enabled=true \
        --set nginx.controller.scope.namespace="$RELEASE_NAMESPACE" \
        --set nginx.controller.ingressClassResource.name=nginx-nevisadmin4-"$RELEASE_NAMESPACE" \
        --set nevisAdmin4.ingress.ingressClass=nginx-nevisadmin4-"$RELEASE_NAMESPACE"  \
        --set nevisOperator.ingressClass=nginx-nevisadmin4-"$RELEASE_NAMESPACE"  \
        --set nevisOperator.apiType=ingress \
        $HELM_SETTINGS

    echo "### Extending nevisAdmin 4 configuration with gitea public key for git access"
    helm upgrade nevisadmin4 "${HELM_CHART_PREFIX}nevisadmin4" "${HELM_CMD_ARGS[@]}" --reuse-values --wait -n "$RELEASE_NAMESPACE" \
        --set git.knownHosts64="$(echo gitea-ssh "$(kubectl exec gitea-0 -c gitea -n "$RELEASE_NAMESPACE" -- cat /data/ssh/gitea.rsa.pub)" | base64 | tr -d '\n')"

    if [[ $ROUTING_API == "ingress" ]]; then
        echo "### Adding ingress-nginx loadbalancer to the installation"
        helm upgrade nevisadmin4 "${HELM_CHART_PREFIX}nevisadmin4" "${HELM_CMD_ARGS[@]}" --reuse-values -n "$RELEASE_NAMESPACE" \
            --set nginx.enabled=true
    else
        echo "### Adding gitea HTTPRoute"
        kubectl apply -f - <<EOF
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: gitea-route
  namespace: $RELEASE_NAMESPACE
spec:
  hostnames:
  - $DOMAIN
  parentRefs:
  - group: gateway.networking.k8s.io
    kind: Gateway
    name: nevisadmin4-gateway
  rules:
  - backendRefs:
    - group: ""
      kind: Service
      name: gitea-http
      port: 3000
    matches:
    - path:
        type: PathPrefix
        value: /gitea
    filters:
    - type: URLRewrite
      urlRewrite:
        path:
          type: ReplacePrefixMatch
          replacePrefixMatch: /
EOF
    fi

    echo "### nevisAdmin 4 successfully installed"
    echo "### The following passwords were generated during the installation process:"
    echo "### nevisAdmin 4 'admin' user password: $NEVISADMIN_PASSWORD"
    echo "### Gitea 'root' user password: $GIT_PASSWORD"
    echo "### Database 'root' user password: $DB_ROOT_PASSWORD"
}

upgrade_nevisadmin4() {
    echo "### Upgrading nevisadmin4 helm chart"
    RELEASE_NAME=nevisadmin4
    helm get values $RELEASE_NAME -n $RELEASE_NAMESPACE > old-values.yaml
    helm upgrade "$RELEASE_NAME" "${HELM_CHART_PREFIX}nevisadmin4" "${HELM_CMD_ARGS[@]}" --create-namespace --wait --timeout 10m -n "$RELEASE_NAMESPACE" \
        -f old-values.yaml $HELM_SETTINGS
}

check_nginx_ip() {
    nginx_service_name="nevisadmin4-nginx-controller"
    iteration_count=60
    sleep_interval=10
    external_ip=""
    port_forward_message="### Until then you can access nevisAdmin 4 using port forwarding by running \"kubectl port-forward -n $RELEASE_NAMESPACE nevisadmin4-0 28111:9080\", then visiting 127.0.0.1:28111/nevisadmin"

    echo "### Verifying the external IP was assigned to the created nginx loadbalancer."
    for ((i=0; i<iteration_count; i++)); do
        # Get the external IP using kubectl and JSONPath
        external_ip=$(kubectl get service "$nginx_service_name" -n "$RELEASE_NAMESPACE" -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
        if [[ -n $external_ip ]]; then
            echo "### External IP assigned: $external_ip"
            echo "### Use this IP to create a DNS A record for the domain $DOMAIN, then visit https://$DOMAIN/nevisadmin"
            echo "$port_forward_message"
            return
        fi
        echo "### External IP not assigned yet. Retrying in $sleep_interval seconds..."
        sleep $sleep_interval
    done
    echo "### Timeout: external IP was not assigned within $((iteration_count * sleep_interval)) seconds. On certain Kubernetes clusters the IP assigment of the loadbalancer is not automatic, contact the platform team of the cluster for support."
    echo "### To use a NodePort service and expose nginx directly instead of using a loadbalancer rerun this script with the option '--add-nginx-nodeport'"
    echo "$port_forward_message"
}

check_envoy_ip() {
    gateway_name="nevisadmin4-gateway"
    iteration_count=60
    sleep_interval=10
    external_ip=""
    echo "### Verifying the external IP was assigned to the created Envoy Gateway."
    for ((i=0; i<iteration_count; i++)); do
        # Get the external IP using kubectl and JSONPath
        external_ip=$(kubectl get gateway "$gateway_name" -n "$RELEASE_NAMESPACE" -o jsonpath='{.status.addresses[0].value}')
        if [[ -n $external_ip ]]; then
            echo "### External IP assigned to Envoy Gateway: $external_ip"
            echo "### Use this IP to create a DNS A record for the domain $DOMAIN, then visit https://$DOMAIN/nevisadmin"
            return
        fi
        echo "### External IP not assigned to Envoy Gateway yet. Retrying in $sleep_interval seconds..."
        sleep $sleep_interval
    done
    echo "### Timeout: external IP was not assigned to Envoy Gateway within $((iteration_count * sleep_interval)) seconds. On certain Kubernetes clusters the IP assignment of the loadbalancer is not automatic, contact the platform team of the cluster for support."

}

add_nginx_node_port() {
    if   [[ $ROUTING_API == "gateway" ]]; then
        echo "### Routing API type is set to gateway, skipping nginx NodePort configuration"
        return
    fi
    read_release_inputs
    check_helm
    helm_login
    echo "### Adjusting nginx service to use NodePort"
    helm upgrade nevisadmin4 "${HELM_CHART_PREFIX}nevisadmin4" "${HELM_CMD_ARGS[@]}" --reuse-values --wait -n "$RELEASE_NAMESPACE" \
        --set nginx.controller.service.type=NodePort
}

add_nginx_load_balancer() {
    if   [[ $ROUTING_API == "gateway" ]]; then
        echo "### Routing API type is set to gateway, skipping nginx LoadBalancer configuration"
        return
    fi
    read_release_inputs
    check_helm
    helm_login
    echo "### Adjusting nginx service to use LoadBalancer"
    helm upgrade nevisadmin4 "${HELM_CHART_PREFIX}nevisadmin4" "${HELM_CMD_ARGS[@]}" --reuse-values --wait -n "$RELEASE_NAMESPACE" \
        --set nginx.controller.service.type=LoadBalancer
}

upgrade() {
    read_release_inputs
    push_images
    check_helm
    helm_login
    upgrade_crd
    upgrade_nevisadmin4
}

install() {
    read_inputs
    push_images
    check_helm
    helm_login
    if [[ $ROUTING_API == "gateway" ]]; then
        install_envoy
    fi
    install_cert_manager
    install_crd
    install_nevisadmin4
    if [[ $ROUTING_API == "ingress" ]]; then
        check_nginx_ip
    else
        check_envoy_ip
    fi
}

# by default we pull docker images from Cloudsmith and push them to a container registry
get_images=y

# by default we ask for confirmation
prompt=y

# by default we use gateway as the routing API
ROUTING_API=gateway

# Parse script arguments
while [[ $# -gt 0 ]]; do
  case "$1" in
    --add-nginx-node-port)
      add_node_port=y
      shift
      ;;
    --add-nginx-load-balancer)
      add_load_balancer=y
      shift
      ;;
    --upgrade)
      upgrade=y
      shift
      ;;
    --namespace)
      if [[ -n $2 ]]; then
        RELEASE_NAMESPACE=$2
        shift
      else
        echo "### Namespace option requires an argument"
        display_usage
        exit 1
      fi
      shift
      ;;
    --domain)
      if [[ -n $2 ]]; then
        DOMAIN=$2
        shift
      else
        echo "### Domain option requires an argument"
        display_usage
        exit 1
      fi
      shift
      ;;
    --container-registry)
      if [[ -n $2 ]]; then
        CONTAINER_REGISTRY=$2
        shift
      else
        echo "### Container registry option requires an argument"
        display_usage
        exit 1
      fi
      shift
      ;;
    --registry-image-prefix)
      if [[ -n $2 ]]; then
        REGISTRY_IMAGE_PREFIX=$2
        shift
      else
        echo "### Registry image prefix option requires an argument"
        display_usage
        exit 1
      fi
      shift
      ;;
    --registry-username)
      if [[ -n $2 ]]; then
        REGISTRY_USERNAME=$2
        shift
      else
        echo "### Registry username option requires an argument"
        display_usage
        exit 1
      fi
      shift
      ;;
    --registry-password)
      if [[ -n $2 ]]; then
        REGISTRY_PASSWORD=$2
        shift
      else
        echo "### Registry password option requires an argument"
        display_usage
        exit 1
      fi
      shift
      ;;
    --cloudsmith-password)
      if [[ -n $2 ]]; then
        CLOUDSMITH_PASSWORD=$2
        shift
      else
        echo "### Cloudsmith password option requires an argument"
        display_usage
        exit 1
      fi
      shift
      ;;
    --helm-repository)
      if [[ -n $2 ]]; then
        HELM_REPOSITORY=$2
        shift
      else
        echo "### Helm repository option requires an argument"
        display_usage
        exit 1
      fi
      shift
      ;;
    --helm-repository-password)
      if [[ -n $2 ]]; then
        HELM_REPOSITORY_PASSWORD=$2
        shift
      else
        echo "### Helm repository password option requires an argument"
        display_usage
        exit 1
      fi
      shift
      ;;
    --helm-repository-username)
      if [[ -n $2 ]]; then
        HELM_REPOSITORY_USERNAME=$2
        shift
      else
        echo "### Helm repository username option requires an argument"
        display_usage
        exit 1
      fi
      shift
      ;;
    --cluster-context)
      if [[ -n $2 ]]; then
        CLUSTER_CONTEXT=$2
        shift
      else
        echo "### Cluster context option requires an argument"
        display_usage
        exit 1
      fi
      shift
      ;;
    --database-password)
      if [[ -n $2 ]]; then
        DATABASE_PASSWORD=$2
        shift
      else
        echo "### Database password option requires an argument"
        display_usage
        exit 1
      fi
      shift
      ;;
    --nevisadmin-password)
      if [[ -n $2 ]]; then
        NEVISADMIN_PASSWORD=$2
        shift
      else
        echo "### Nevisadmin password option requires an argument"
        display_usage
        exit 1
      fi
      shift
      ;;
    --git-password)
      if [[ -n $2 ]]; then
        GIT_PASSWORD=$2
        shift
      else
        echo "### Git password option requires an argument"
        display_usage
        exit 1
      fi
      shift
      ;;
    --version)
      if [[ -n $2 ]]; then
        VERSION=$2
        shift
      else
        echo "### Version option requires an argument"
        display_usage
        exit 1
      fi
      shift
      ;;
    --helm-setting)
      if [[ -n $2 ]]; then
        HELM_SETTINGS+="--set $2 "
        shift
      else
        echo "### Helm setting option requires an argument"
        display_usage
        exit 1
      fi
      shift
      ;;
    --image-version)
      if [[ -n $2 ]]; then
        IMAGE_VERSION=$2
        shift
      else
        echo "### Image version option requires an argument"
        display_usage
        exit 1
      fi
      shift
      ;;
    --routing-api)
      if [[ -n $2 ]]; then
        if [[ $2 != "ingress" && $2 != "gateway" ]]; then
          echo "### Routing API option requires a value of 'ingress' or 'gateway'"
          display_usage
          exit 1
        fi
        ROUTING_API=$2
        shift
      else
        echo "### Routing API option requires an argument"
        display_usage
        exit 1
      fi
      shift
      ;;
    --skip-images)
      get_images=n
      shift
      ;;
    --no-prompt)
      prompt=n
      shift
      ;;
    --help)
      display_usage
      exit 1
      ;;
    *)
      echo "### Invalid option: $1"
      display_usage
      exit 1
      ;;
  esac
done

echo "### Welcome to the nevisAdmin 4 Kubernetes installer. The installer will deploy nevisAdmin 4 with all it's required dependencies to the cluster."
verify_cluster
if [[ $upgrade == "y" ]]; then
    upgrade
    exit 0
fi
if [[ $add_node_port == "y" ]]; then
    add_nginx_node_port
    exit 0
fi
if [[ $add_load_balancer == "y" ]]; then
    add_nginx_load_balancer
    exit 0
fi
install
