#!/bin/sh

set -eu

VERSION="0.0.2-20250320"

CLUSTER_API_LB_IP=${CLUSTER_API_LB_IP:-4.155.160.32}
CLUSTER_API_LB_PORT=${CLUSTER_API_LB_PORT:-6443}
MICROSOFT_ENTRA_ID_TENANT=${MICROSOFT_ENTRA_ID_TENANT:-cf151ee8-5c2c-4fe7-a1c4-809ba43c9f24}
MICROSOFT_ENTRA_ID_CLIENT_ID=${MICROSOFT_ENTRA_ID_CLIENT_ID:-7cd1df19-24ea-46d7-acd3-5336283139e0}
MICROSOFT_ENTRA_ID_CLIENT_SECRET=${MICROSOFT_ENTRA_ID_CLIENT_SECRET:-L9J8Q~kClGP-sXKS3YFgnpDu7ednUdlWGsWfQbTl}
MICROSOFT_ENTRA_ID_ISSUER=${MICROSOFT_ENTRA_ID_ISSUER:-https://login.microsoftonline.com/${MICROSOFT_ENTRA_ID_TENANT}/v2.0}
OS=${OS:-}
ARCH=${ARCH:-}
KUBECTL_VERSION=${KUBECTL_VERSION:-v1.30.3}
KUBELOGIN_VERSION=${KUBELOGIN_VERSION:-v1.31.1}

# Options
AUTO_INSTALL_KUBECTL=${AUTO_INSTALL_KUBECTL:-true}
AUTO_INSTALL_KUBELOGIN=${AUTO_INSTALL_KUBELOGIN:-true}

help() {
  echo "Freeleaps Cluster Authenticator (Version: ${VERSION})"
  echo ""
  echo "This script helps you to authenticate with the freeleaps cluster using your Mathmast account, and setup kubectl."
  echo "It also provides some useful sub-commands to manage the authentication state."
  echo "Please ensure this script is only for internal use and not to be shared with anyone."
  echo ""
  echo "Usage: [environment-option=value] freeleaps-cluster-authenticator <sub-command>"
  echo ""
  echo "Sub Commands:"
  echo "    auth,-a,--auth              Setup kubectl for freeleaps cluster with Mathmast account."
  echo "    reset-auth,-r,--reset-auth  Reset kubectl authentication state for freeleaps cluster."
  echo "    clear,-c,--clear            Clear authentication for freeleaps cluster."
  echo "    doctor,-d,--doctor          Check if all the required tools are installed."
  echo "    dashboard,-db,--dashboard   Open the Kubernetes dashboard, forward the port to localhost and listen on 8443."
  echo "    get-token,-gt,--get-token   Get the token for the current user, usually to using Kubernetes Dashboard authentication."
  echo "    help,-h,--help              Show this help message."
  echo ""
  echo "Environment Options:"
  echo "    [Optional] CLUSTER_API_LB_IP:                IP address of the cluster API load balancer."
  echo "    [Optional] CLUSTER_API_LB_PORT:              Port of the cluster API load balancer."
  echo "    [Optional] MICROSOFT_ENTRA_ID_TENANT:        Microsoft Entra ID tenant."
  echo "    [Optional] MICROSOFT_ENTRA_ID_CLIENT_ID:     Microsoft Entra ID client ID."
  echo "    [Optional] MICROSOFT_ENTRA_ID_CLIENT_SECRET: Microsoft Entra ID client secret."
  echo "    [Optional] MICROSOFT_ENTRA_ID_ISSUER:        Microsoft Entra ID issuer URL."
  echo "    [Optional] OS:                               Operating system (linux or darwin). Default: auto"
  echo "    [Optional] ARCH:                             Architecture (amd64 or arm64). Default: auto"
  echo "    [Optional] KUBECTL_VERSION:                  Version of kubectl to install. Default: v1.30.3"
  echo "    [Optional] KUBELOGIN_VERSION:                Version of kubelogin to install. Default: v1.31.1"
  echo "    [Optional] AUTO_INSTALL_KUBECTL:             Automatically install kubectl if not found. Default: true"
  echo "    [Optional] AUTO_INSTALL_KUBELOGIN:           Automatically install kubelogin if not found. Default: true"
}

gather_os_environment() {
  echo "[GATHER] Checking OS and architecture..."

  _uname_os_output="$(uname -s)"
  case "${_uname_os_output}" in
    Linux*)
      OS='linux'
    ;;
    Darwin)
      OS='darwin'
    ;;
    *)
      echo "[ERROR] Unsupported OS: ${_uname_os_output}"
      exit 1
    ;;
  esac

  _uname_arch_output="$(uname -m)"
  case "${_uname_arch_output}" in
    x86_64)
      ARCH='amd64'
    ;;
    arm64)
      ARCH='arm64'
    ;;
    *)
      echo "[ERROR] Unsupported architecture: ${_uname_arch_output}"
      exit 1
    ;;
  esac
}

ensure_kubectl() {
  echo "[INSTALL] Installing kubectl..."

  _ensure_kubectl_download_url="https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/${OS}/${ARCH}/kubectl"

  echo "[INSTALL] Downloading kubectl (${ARCH}-${KUBECTL_VERSION}) from ${_ensure_kubectl_download_url}"
  # download to tmp folder
  curl --progress-bar -o /tmp/kubectl "${_ensure_kubectl_download_url}"
  chmod +x /tmp/kubectl
  sudo mv /tmp/kubectl /usr/local/bin/kubectl
  echo "[INSTALL] kubectl installed successfully."
}

ensure_kubelogin() {
  echo "[INSTALL] Installing kubelogin..."

  _ensure_kubelogin_download_url="https://github.com/int128/kubelogin/releases/download/${KUBELOGIN_VERSION}/kubelogin_${OS}_${ARCH}.zip"

  echo "[INSTALL] Downloading kubelogin (${ARCH}-${KUBELOGIN_VERSION}) from ${_ensure_kubelogin_download_url}"

  # download to tmp folder
  curl --progress-bar -L -o /tmp/kubelogin.zip "${_ensure_kubelogin_download_url}"

  # Check if the file is a valid zip file
  if ! unzip -t /tmp/kubelogin.zip > /dev/null 2>&1; then
    echo "[ERROR] Downloaded file is not a valid zip file or download failed."
    rm /tmp/kubelogin.zip
    exit 1
  fi

  unzip /tmp/kubelogin.zip -d /tmp/kubelogin-release
  chmod +x /tmp/kubelogin-release/kubelogin
  sudo mv /tmp/kubelogin-release/kubelogin /usr/local/bin/kubelogin
  sudo rm -rf /tmp/kubelogin-release
  echo "[INSTALL] kubelogin installed successfully."
}

gather_prerequisites() {
  gather_os_environment

  echo "[INFO] OS: ${OS}"
  echo "[INFO] Architecture: ${ARCH}"

  echo "[PREREQUISITES] Checking for required tools..."

  if ! command -v curl > /dev/null; then
    echo "[ERROR] curl is required to download kubectl and kubelogin"
    exit 1
  else
    echo "[PREREQUISITES] curl: ✓"
  fi


  if ! command -v unzip > /dev/null; then
    echo "[ERROR] unzip is required to extract kubelogin"
    exit 1
  else
    echo "[PREREQUISITES] unzip: ✓"
  fi

  if ! command -v uname > /dev/null; then
    echo "[ERROR] uname is required to determine OS and architecture"
    exit 1
  else
    echo "[PREREQUISITES] uname: ✓"
  fi

  if ! command -v kubectl > /dev/null; then
    if [ "${AUTO_INSTALL_KUBECTL}" = "true" ]; then
      echo "[PREREQUISITES] kubectl: Installing..."
      ensure_kubectl
    else 
      echo "[ERROR] kubectl is required to authenticate with the cluster"
      exit 1
    fi
    
  else
    echo "[PREREQUISITES] kubectl: ✓"
  fi

  if ! command -v kubelogin > /dev/null; then
    if [ "${AUTO_INSTALL_KUBECTL}" = "true" ]; then
      echo "[PREREQUISITES] kubelogin: Installing..."
      ensure_kubelogin
    else 
      echo "[ERROR] kubelogin is required to authenticate with the cluster"
      exit 1
    fi
    
  else
    echo "[PREREQUISITES] kubelogin: ✓"
  fi

  if ! command -v jq > /dev/null; then
    echo "[ERROR] jq is required to parse JSON output, please install it with your package manager."
    exit 1
  else
    echo "[PREREQUISITES] jq: ✓"
  fi
}

setup_kubelogin() {
  echo "[SETUP] Setting up kubelogin..."

  kubelogin setup \
    --oidc-issuer-url "${MICROSOFT_ENTRA_ID_ISSUER}" \
    --oidc-client-id "${MICROSOFT_ENTRA_ID_CLIENT_ID}" \
    --oidc-client-secret "${MICROSOFT_ENTRA_ID_CLIENT_SECRET}" \
    --oidc-extra-scope "offline_access" \
    --oidc-extra-scope "profile" \
    --oidc-extra-scope "email" > /dev/null 2>&1
  
  echo "[SETUP] kubelogin setup completed successfully."
}

prompt_username() {
  echo "[PROMPT] Please enter your Mathmast account name (ending with @mathmast.com, eg. jack@mathmast.com):"
  
  read -r username

  # While loop to check if username is valid
  while ! echo "${username}" | grep -qE '^[a-zA-Z0-9._%+-]+@mathmast.com$'; do
    echo "[ERROR] Username invalid, please enter a valid Mathmast account name (ending with @mathmast.com, eg. jack@mathmast.com):"
    read -r username
  done

  echo "[PROMPT] Username: ${username}"
}

set_kubectl_credentials() {
  echo "[KUBECTL & KUBE_LOGIN] Setting kubectl credentials for ${username}..."

  kubectl config set-credentials "$username" \
    --exec-api-version=client.authentication.k8s.io/v1beta1 \
    --exec-command=kubelogin \
    --exec-arg=get-token \
    --exec-arg="--oidc-issuer-url=${MICROSOFT_ENTRA_ID_ISSUER}" \
    --exec-arg="--oidc-client-id=${MICROSOFT_ENTRA_ID_CLIENT_ID}" \
    --exec-arg="--oidc-client-secret=${MICROSOFT_ENTRA_ID_CLIENT_SECRET}" \
    --exec-arg="--oidc-extra-scope=offline_access" \
    --exec-arg="--oidc-extra-scope=profile" \
    --exec-arg="--oidc-extra-scope=email" > /dev/null 2>&1

  echo "[KUBECTL & KUBE_LOGIN] Credentials set successfully."
}

add_cluster_to_kubectl() {
  echo "[KUBECTL] Adding cluster (named to: freeleaps-cluster) to kubectl..."

  kubectl config set-cluster freeleaps-cluster \
    --server="https://${CLUSTER_API_LB_IP}:${CLUSTER_API_LB_PORT}" \
    --insecure-skip-tls-verify=true > /dev/null 2>&1

  echo "[KUBECTL] Cluster added to kubectl successfully."
}

create_kubectl_context() {
  echo "[KUBECTL] Creating kubectl context..."

  kubectl config set-context "${username}@freeleaps-cluster" \
    --cluster=freeleaps-cluster \
    --user="${username}" > /dev/null 2>&1

  echo "[KUBECTL] Context created successfully."
}

use_kubectl_context() {
  echo "[KUBECTL] Using kubectl context..."

  kubectl config use-context "${username}@freeleaps-cluster" > /dev/null 2>&1

  echo "[KUBECTL] Context set successfully."
}

check_whoami() {
  echo "[KUBECTL] Checking whoami..."

  kubectl auth whoami
}

auth() {
  gather_prerequisites
  setup_kubelogin
  prompt_username
  set_kubectl_credentials
  add_cluster_to_kubectl
  create_kubectl_context
  use_kubectl_context
  check_whoami

  echo "[INFO] Your kubectl has been authenticated with your Mathmast account."
  echo "[INFO] Now you can try to using kubectl to interact with the cluster."
}

clear_auth() {
  prompt_username

  echo "[CLEAR] Clearing kubectl authentication..."

  if ! kubectl config delete-user "${username}" > /dev/null 2>&1; then
    echo "[ERROR] User ${username} not found in kubectl config."
    exit 1
  fi

  if ! kubectl config delete-context "${username}@freeleaps-cluster" > /dev/null 2>&1; then
    echo "[ERROR] Context ${username}@freeleaps-cluster not found in kubectl config."
    exit 1
  fi

  if ! kubectl config delete-cluster freeleaps-cluster > /dev/null 2>&1; then
    echo "[ERROR] Cluster freeleaps-cluster not found in kubectl config."
    exit 1
  fi

  if ! kubectl config unset current-context > /dev/null 2>&1; then
    echo "[ERROR] Unable to unset current context in kubectl config."
    exit 1
  fi

  echo "[CLEAR] kubectl authentication cleared successfully."
}

reset_auth() {
  echo "[RESET] Reset kubectl authentication state..."
  rm -rf "${HOME}/.kube/oidc-login"
  echo "[RESET] kubectl authentication state reset successfully."
}

exit_dashboard() {
  echo "[RUN_DASHBOARD] Kubernetes dashboard stopped."
  exit 0
}

run_dashboard() {
  gather_prerequisites

  echo "[RUN_DASHBOARD] Ready to run Kubernetes dashboard..."
  is_target_svc_exist=$(kubectl get svc -n freeleaps-infra-system | grep kubernetes-dashboard-kong-proxy)
  if [ -z "$is_target_svc_exist" ]; then
    echo "[ERROR] Kubernetes dashboard service not found in namespace: freeleaps-infra-system."
    exit 1
  fi
  echo "[RUN_DASHBOARD] Kubernetes dashboard is running at: https://localhost:8443"
  trap exit_dashboard INT HUP
  kubectl port-forward svc/kubernetes-dashboard-kong-proxy -n freeleaps-infra-system 8443:443
}

get_token() {
  gather_prerequisites

  echo "[GET_TOKEN] Getting token for the current user..."

  get_token_resp=$(
    kubelogin get-token \
      --oidc-issuer-url "${MICROSOFT_ENTRA_ID_ISSUER}" \
      --oidc-client-id "${MICROSOFT_ENTRA_ID_CLIENT_ID}" \
      --oidc-client-secret "${MICROSOFT_ENTRA_ID_CLIENT_SECRET}" \
      --oidc-extra-scope "offline_access" \
      --oidc-extra-scope "profile" \
      --oidc-extra-scope "email"
  )

  token=$(echo "${get_token_resp}" | jq -r '.status.token')
  expiration=$(echo "${get_token_resp}" | jq -r '.status.expirationTimestamp')

  echo "[GET_TOKEN] Token: ${token}"
  echo "[GET_TOKEN] Expiration Timestamp: ${expiration}"
}

main() {
  if [ $# -lt 1 ]; then
    echo "[ERROR] No sub-command provided."
    echo "[TIP] Run 'freeleaps-cluster-authenticator -h' to see available sub-commands."
    exit 1
  fi

  subcommand="$1"

  case "${subcommand}" in
    auth|-a|--auth)
      auth
    ;;
    reset-auth|-r|--reset-auth)
      reset_auth 
    ;;
    clear|-c|--clear)
      clear_auth
    ;;
    doctor|-d|--doctor)
      gather_prerequisites
    ;;
    dashboard|-db|--dashboard)
      run_dashboard
    ;;
    get-token|-gt|--get-token)
      get_token
    ;;
    help|-h|--help)
      help
    ;;
    *)
      echo "[ERROR] Invalid sub-command: ${subcommand}"
      help
      exit 1
    ;;
  esac
}

main "$@"