#!/usr/bin/env bash

set -euo pipefail

readonly SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
readonly REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
readonly CLUSTER_NAME="nereus"
readonly CONTEXT="k3d-${CLUSTER_NAME}"
readonly ARGO_CHART_VERSION="2.41.1"
readonly PROMETHEUS_CHART_VERSION="88.5.2"

require_commands() {
  local command_name
  for command_name in docker k3d helm kubectl; do
    command -v "${command_name}" >/dev/null || {
      echo "missing required command: ${command_name}" >&2
      exit 1
    }
  done
  docker info >/dev/null
}

use_context() {
  kubectl config use-context "${CONTEXT}" >/dev/null
}

cluster_exists() {
  k3d cluster get "${CLUSTER_NAME}" >/dev/null 2>&1
}

up() {
  require_commands
  if ! cluster_exists; then
    k3d cluster create --config "${REPO_ROOT}/scripts/k3d-nereus.yaml"
  else
    k3d cluster start "${CLUSTER_NAME}"
  fi
  use_context

  helm repo add argo https://argoproj.github.io/argo-helm --force-update
  helm repo add prometheus-community https://prometheus-community.github.io/helm-charts --force-update
  helm repo update

  helm upgrade --install argo-rollouts argo/argo-rollouts \
    --namespace argo-rollouts \
    --create-namespace \
    --version "${ARGO_CHART_VERSION}" \
    --wait \
    --timeout 5m

  helm upgrade --install kube-prometheus-stack prometheus-community/kube-prometheus-stack \
    --namespace observability \
    --create-namespace \
    --version "${PROMETHEUS_CHART_VERSION}" \
    --values "${SCRIPT_DIR}/kube-prometheus-stack.values.yaml" \
    --wait \
    --timeout 10m

  kubectl create namespace nereus --dry-run=client -o yaml | kubectl apply -f -
  kubectl apply -f "${SCRIPT_DIR}/analysis-harness/harness.yaml"
  check
}

check() {
  require_commands
  cluster_exists || {
    echo "cluster ${CLUSTER_NAME} does not exist; run $0 up" >&2
    exit 1
  }
  use_context

  kubectl wait node --all --for=condition=Ready --timeout=3m
  kubectl wait deployment --all --namespace argo-rollouts --for=condition=Available --timeout=3m
  kubectl wait deployment --all --namespace observability --for=condition=Available --timeout=5m
  kubectl get rollout probe --namespace nereus >/dev/null
  kubectl get analysistemplate error-rate --namespace nereus >/dev/null
  echo "k3d rollback lab is ready"
}

analysis_name() {
  kubectl get rollout probe --namespace nereus \
    -o jsonpath='{.status.blueGreen.prePromotionAnalysisRunStatus.name}'
}

wait_for_analysis() {
  local previous_name="$1"
  local expected_phase="$2"
  local analysis_run=""
  local phase=""
  local attempt

  for attempt in {1..90}; do
    analysis_run="$(analysis_name)"
    if [[ -n "${analysis_run}" && "${analysis_run}" != "${previous_name}" ]]; then
      phase="$(kubectl get analysisrun "${analysis_run}" --namespace nereus -o jsonpath='{.status.phase}')"
      if [[ "${phase}" == "${expected_phase}" ]]; then
        echo "${analysis_run} reached ${expected_phase}"
        return 0
      fi
      if [[ "${phase}" == "Error" || "${phase}" == "Inconclusive" ]]; then
        echo "${analysis_run} ended unexpectedly with ${phase}" >&2
        return 1
      fi
    fi
    sleep 2
  done

  echo "analysis did not reach ${expected_phase} within 180 seconds" >&2
  return 1
}

set_proof_revision() {
  local query="$1"
  local revision="$2"
  kubectl patch rollout probe --namespace nereus --type=merge --patch \
    "{\"spec\":{\"strategy\":{\"blueGreen\":{\"prePromotionAnalysis\":{\"args\":[{\"name\":\"query\",\"value\":\"${query}\"}]}}},\"template\":{\"metadata\":{\"annotations\":{\"nereus.fiwlabs.dev/proof\":\"${revision}\"}}}}}"
}

prove() {
  check

  local proof_id
  local previous_analysis
  local healthy_analysis
  local healthy_revision=""
  local active_revision=""
  local stable_revision=""
  local attempt
  proof_id="$(date -u +%Y%m%d%H%M%S)"

  previous_analysis="$(analysis_name)"
  set_proof_revision "vector(0.0)" "healthy-${proof_id}"
  wait_for_analysis "${previous_analysis}" Successful
  healthy_analysis="$(analysis_name)"

  for attempt in {1..30}; do
    healthy_revision="$(kubectl get rollout probe --namespace nereus -o jsonpath='{.status.stableRS}')"
    active_revision="$(kubectl get service probe-active --namespace nereus -o jsonpath='{.spec.selector.rollouts-pod-template-hash}')"
    if [[ -n "${healthy_revision}" && "${active_revision}" == "${healthy_revision}" ]]; then
      break
    fi
    sleep 2
  done
  [[ -n "${healthy_revision}" && "${active_revision}" == "${healthy_revision}" ]] || {
    echo "healthy revision was not promoted" >&2
    return 1
  }

  set_proof_revision "vector(1.0)" "failing-${proof_id}"
  wait_for_analysis "${healthy_analysis}" Failed
  stable_revision="$(kubectl get rollout probe --namespace nereus -o jsonpath='{.status.stableRS}')"
  active_revision="$(kubectl get service probe-active --namespace nereus -o jsonpath='{.spec.selector.rollouts-pod-template-hash}')"
  [[ "${stable_revision}" == "${healthy_revision}" && "${active_revision}" == "${healthy_revision}" ]] || {
    echo "failed revision replaced the active healthy revision" >&2
    return 1
  }

  set_proof_revision "vector(0.0)" "healthy-${proof_id}"
  echo "rollback proof passed; active revision stayed ${healthy_revision}"
}

destroy() {
  require_commands
  if cluster_exists; then
    k3d cluster delete "${CLUSTER_NAME}"
  else
    echo "cluster ${CLUSTER_NAME} does not exist"
  fi
}

case "${1:-}" in
  up) up ;;
  check) check ;;
  prove) prove ;;
  destroy) destroy ;;
  *)
    echo "usage: $0 {up|check|prove|destroy}" >&2
    exit 2
    ;;
esac
