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

require_command() {
  local name="$1"
  if ! command -v "${name}" >/dev/null 2>&1; then
    echo "error: required command not found: ${name}" >&2
    exit 1
  fi
}

usage() {
  cat <<'EOF'
Install CuteOS onto Docker Desktop Kubernetes.

Usage:
  install-docker-desktop-k8s.sh [options]

Options:
  --tag <tag>                  CuteOS image tag to install
  --release <name>             Helm release name (default: cuteos)
  --namespace <name>           Kubernetes namespace (default: cuteos)
  --context <name>             Kubernetes context to target
  --admin-password <password>  Admin password to bootstrap
  --shared-token <token>       Optional shared service token
  --install-base-url <url>     Remote asset base URL (default: https://install.cuteos.io)
  --chart-url <url>            Override remote chart URL
  --values-url <url>           Override remote values profile URL
  --bootstrap-url <url>        Override remote bootstrap helper URL
  --skip-prepull               Skip Docker image pre-pull
  --skip-port-forward          Skip starting local API/UI port-forwards
  --help                       Show this help

Environment variables mirror the main flags:
  TAG, RELEASE, NAMESPACE, KUBE_CONTEXT, ADMIN_PASSWORD, SHARED_TOKEN,
  INSTALL_BASE_URL, CHART_URL, VALUES_URL, BOOTSTRAP_SCRIPT_URL,
  PREPULL_IMAGES, START_PORT_FORWARDS
EOF
}

wait_rollout() {
  local namespace="$1"
  local name="$2"
  local timeout="${3:-300s}"

  if kubectl "${KUBECTL_ARGS[@]}" -n "${namespace}" get deployment "${name}" >/dev/null 2>&1; then
    kubectl "${KUBECTL_ARGS[@]}" -n "${namespace}" rollout status deployment/"${name}" --timeout="${timeout}"
    return 0
  fi
  if kubectl "${KUBECTL_ARGS[@]}" -n "${namespace}" get statefulset "${name}" >/dev/null 2>&1; then
    kubectl "${KUBECTL_ARGS[@]}" -n "${namespace}" rollout status statefulset/"${name}" --timeout="${timeout}"
    return 0
  fi

  echo "warning: workload not found while waiting for rollout: ${name}" >&2
  return 1
}

download_file() {
  local url="$1"
  local destination="$2"
  echo "Downloading ${url}"
  curl -fsSL "${url}" -o "${destination}"
}

generate_password() {
  local generated=""
  set +o pipefail
  generated="$(LC_ALL=C tr -dc 'A-Za-z0-9' </dev/urandom | head -c 20)"
  set -o pipefail
  if [[ -z "${generated}" ]]; then
    echo "error: failed to generate an admin password" >&2
    exit 1
  fi
  printf '%s\n' "${generated}"
}

prepull_image() {
  local image="$1"
  if docker image inspect "${image}" >/dev/null 2>&1; then
    echo "Image already present: ${image}"
    return 0
  fi
  echo "Pulling ${image}"
  docker pull "${image}" >/dev/null
}

stop_background_process() {
  local pid_file="$1"
  if [[ ! -f "${pid_file}" ]]; then
    return 0
  fi
  local pid
  pid="$(cat "${pid_file}")"
  if [[ -n "${pid}" ]] && kill -0 "${pid}" >/dev/null 2>&1; then
    kill "${pid}" >/dev/null 2>&1 || true
    wait "${pid}" >/dev/null 2>&1 || true
  fi
  rm -f "${pid_file}"
}

start_port_forward() {
  local name="$1"
  local resource="$2"
  local mapping="$3"
  local pid_file="${STATE_DIR}/${name}.pid"
  local log_file="${STATE_DIR}/${name}.log"

  stop_background_process "${pid_file}"

  echo "Starting port-forward for ${resource} on ${mapping}"
  nohup kubectl "${KUBECTL_ARGS[@]}" -n "${NAMESPACE}" port-forward --address 127.0.0.1 "${resource}" "${mapping}" >"${log_file}" 2>&1 &
  local pid=$!
  echo "${pid}" >"${pid_file}"
}

wait_for_http() {
  local url="$1"
  local label="$2"
  local attempts="${3:-30}"

  local try=1
  while [[ "${try}" -le "${attempts}" ]]; do
    if curl -fsS "${url}" >/dev/null 2>&1; then
      return 0
    fi
    sleep 2
    try=$((try + 1))
  done

  echo "warning: ${label} did not become reachable at ${url}" >&2
  return 1
}

RELEASE="${RELEASE:-cuteos}"
NAMESPACE="${NAMESPACE:-cuteos}"
CHART_NAME="${CHART_NAME:-cuteos}"
FULLNAME="${FULLNAME:-}"
CHART_PATH="${CHART_PATH:-./charts/cuteos}"
VALUES_FILE="${VALUES_FILE:-${CHART_PATH}/values-docker-desktop.yaml}"
BOOTSTRAP_SCRIPT_PATH="${BOOTSTRAP_SCRIPT_PATH:-./deploy/scripts/bootstrap-k8s.sh}"
TAG="${TAG:-latest}"
IMAGE_REGISTRY="${IMAGE_REGISTRY:-ghcr.io/cuteos}"
RUNNER_IMAGE="${RUNNER_IMAGE:-}"
BOOTSTRAP_IMAGE="${BOOTSTRAP_IMAGE:-}"
ROLL_OUT_TIMEOUT="${ROLL_OUT_TIMEOUT:-300s}"
KUBE_CONTEXT="${KUBE_CONTEXT:-${KUBECTL_CONTEXT:-}}"
KUBECTL_DISPLAY_PREFIX="kubectl"
INSTALL_BASE_URL="${INSTALL_BASE_URL:-https://install.cuteos.io}"
CHART_URL="${CHART_URL:-}"
VALUES_URL="${VALUES_URL:-}"
BOOTSTRAP_SCRIPT_URL="${BOOTSTRAP_SCRIPT_URL:-}"
PREPULL_IMAGES="${PREPULL_IMAGES:-true}"
START_PORT_FORWARDS="${START_PORT_FORWARDS:-true}"
STATE_DIR="${STATE_DIR:-${HOME}/.cuteos/local-install}"

while [[ $# -gt 0 ]]; do
  case "$1" in
    --tag)
      TAG="$2"
      shift 2
      ;;
    --release)
      RELEASE="$2"
      shift 2
      ;;
    --namespace)
      NAMESPACE="$2"
      shift 2
      ;;
    --context)
      KUBE_CONTEXT="$2"
      shift 2
      ;;
    --admin-password)
      ADMIN_PASSWORD="$2"
      shift 2
      ;;
    --shared-token)
      SHARED_TOKEN="$2"
      shift 2
      ;;
    --install-base-url)
      INSTALL_BASE_URL="$2"
      shift 2
      ;;
    --chart-url)
      CHART_URL="$2"
      shift 2
      ;;
    --values-url)
      VALUES_URL="$2"
      shift 2
      ;;
    --bootstrap-url)
      BOOTSTRAP_SCRIPT_URL="$2"
      shift 2
      ;;
    --skip-prepull)
      PREPULL_IMAGES="false"
      shift
      ;;
    --skip-port-forward)
      START_PORT_FORWARDS="false"
      shift
      ;;
    --help|-h)
      usage
      exit 0
      ;;
    *)
      echo "error: unknown option: $1" >&2
      usage >&2
      exit 1
      ;;
  esac
done

if [[ -z "${FULLNAME}" ]]; then
  if [[ "${RELEASE}" == *"${CHART_NAME}"* ]]; then
    FULLNAME="${RELEASE}"
  else
    FULLNAME="${RELEASE}-${CHART_NAME}"
  fi
fi

if [[ -z "${RUNNER_IMAGE}" ]]; then
  RUNNER_IMAGE="${IMAGE_REGISTRY}/cuteos-ansible-runner:${TAG}"
fi
if [[ -z "${BOOTSTRAP_IMAGE}" ]]; then
  BOOTSTRAP_IMAGE="${IMAGE_REGISTRY}/cuteos-bootstrap:${TAG}"
fi
if [[ -z "${CHART_URL}" ]]; then
  CHART_URL="${INSTALL_BASE_URL%/}/charts/cuteos.tgz"
fi
if [[ -z "${VALUES_URL}" ]]; then
  VALUES_URL="${INSTALL_BASE_URL%/}/profiles/values-docker-desktop.yaml"
fi
if [[ -z "${BOOTSTRAP_SCRIPT_URL}" ]]; then
  BOOTSTRAP_SCRIPT_URL="${INSTALL_BASE_URL%/}/scripts/bootstrap-k8s.sh"
fi

mkdir -p "${STATE_DIR}"
TMP_DIR="$(mktemp -d)"
trap 'rm -rf "${TMP_DIR}"' EXIT

if [[ -z "${ADMIN_PASSWORD:-}" ]]; then
  ADMIN_PASSWORD="$(generate_password)"
  GENERATED_ADMIN_PASSWORD="true"
else
  GENERATED_ADMIN_PASSWORD="false"
fi

require_command kubectl
require_command helm
require_command curl
require_command docker

LOCAL_CHART_SOURCE="false"
LOCAL_BOOTSTRAP_SOURCE="false"
if [[ -d "${CHART_PATH}" || -f "${CHART_PATH}" ]]; then
  LOCAL_CHART_SOURCE="true"
fi
if [[ -f "${VALUES_FILE}" ]]; then
  LOCAL_VALUES_SOURCE="true"
else
  LOCAL_VALUES_SOURCE="false"
fi
if [[ -f "${BOOTSTRAP_SCRIPT_PATH}" ]]; then
  LOCAL_BOOTSTRAP_SOURCE="true"
fi

if [[ "${LOCAL_CHART_SOURCE}" != "true" ]]; then
  CHART_PATH="${TMP_DIR}/cuteos.tgz"
  download_file "${CHART_URL}" "${CHART_PATH}"
fi

if [[ "${LOCAL_VALUES_SOURCE}" != "true" ]]; then
  VALUES_FILE="${TMP_DIR}/values-docker-desktop.yaml"
  download_file "${VALUES_URL}" "${VALUES_FILE}"
fi

if [[ "${LOCAL_BOOTSTRAP_SOURCE}" != "true" ]]; then
  BOOTSTRAP_SCRIPT_PATH="${TMP_DIR}/bootstrap-k8s.sh"
  download_file "${BOOTSTRAP_SCRIPT_URL}" "${BOOTSTRAP_SCRIPT_PATH}"
  chmod +x "${BOOTSTRAP_SCRIPT_PATH}"
fi

KUBECTL_ARGS=()
HELM_ARGS=()
if [[ -n "${KUBE_CONTEXT}" ]]; then
  KUBECTL_ARGS+=(--context "${KUBE_CONTEXT}")
  HELM_ARGS+=(--kube-context "${KUBE_CONTEXT}")
  KUBECTL_DISPLAY_PREFIX="kubectl --context ${KUBE_CONTEXT}"
fi

CURRENT_CONTEXT="$(kubectl "${KUBECTL_ARGS[@]}" config current-context 2>/dev/null || true)"
if [[ -n "${CURRENT_CONTEXT}" ]]; then
  echo "Using Kubernetes context: ${CURRENT_CONTEXT}"
  if [[ "${CURRENT_CONTEXT}" != "docker-desktop" ]]; then
    echo "warning: current context is not docker-desktop; proceeding with ${CURRENT_CONTEXT}" >&2
  fi
fi

kubectl "${KUBECTL_ARGS[@]}" cluster-info >/dev/null

if [[ "${PREPULL_IMAGES}" == "true" ]]; then
  for image in \
    "${IMAGE_REGISTRY}/cuteos-control-plane:${TAG}" \
    "${IMAGE_REGISTRY}/cuteos-execution-runner:${TAG}" \
    "${RUNNER_IMAGE}" \
    "${IMAGE_REGISTRY}/cuteos-nats:${TAG}" \
    "${IMAGE_REGISTRY}/cuteos-postgres:${TAG}" \
    "${IMAGE_REGISTRY}/cuteos-openbao:${TAG}" \
    "${IMAGE_REGISTRY}/cuteos-opa:${TAG}" \
    "${IMAGE_REGISTRY}/cuteos-minio:${TAG}" \
    "${IMAGE_REGISTRY}/cuteos-web:${TAG}" \
    "${BOOTSTRAP_IMAGE}"
  do
    prepull_image "${image}"
  done
fi

HELM_SET_ARGS=(
  --set api.image.repository="${IMAGE_REGISTRY}/cuteos-control-plane"
  --set api.image.tag="${TAG}"
  --set executionRunner.image.repository="${IMAGE_REGISTRY}/cuteos-execution-runner"
  --set executionRunner.image.tag="${TAG}"
  --set executionRunner.env.CUTEOS_EXECUTION_RUNNER_MODE=kubernetes_job
  --set executionRunner.env.CUTEOS_EXECUTION_JOB_IMAGE="${RUNNER_IMAGE}"
  --set nats.image.repository="${IMAGE_REGISTRY}/cuteos-nats"
  --set nats.image.tag="${TAG}"
  --set postgres.image.repository="${IMAGE_REGISTRY}/cuteos-postgres"
  --set postgres.image.tag="${TAG}"
  --set openbao.image.repository="${IMAGE_REGISTRY}/cuteos-openbao"
  --set openbao.image.tag="${TAG}"
  --set opa.image.repository="${IMAGE_REGISTRY}/cuteos-opa"
  --set opa.image.tag="${TAG}"
  --set minio.image.repository="${IMAGE_REGISTRY}/cuteos-minio"
  --set minio.image.tag="${TAG}"
  --set web.image.repository="${IMAGE_REGISTRY}/cuteos-web"
  --set web.image.tag="${TAG}"
)

if [[ -n "${SHARED_TOKEN:-}" ]]; then
  HELM_SET_ARGS+=(--set-string "serviceAuth.sharedToken=${SHARED_TOKEN}")
fi

echo "Installing CuteOS into namespace ${NAMESPACE}"
helm "${HELM_ARGS[@]}" upgrade --install "${RELEASE}" "${CHART_PATH}" \
  --namespace "${NAMESPACE}" \
  --create-namespace \
  --reset-values \
  -f "${VALUES_FILE}" \
  "${HELM_SET_ARGS[@]}"

echo "Waiting for core services"
for workload in \
  "${FULLNAME}"-postgres \
  "${FULLNAME}"-nats \
  "${FULLNAME}"-openbao \
  "${FULLNAME}"-minio \
  "${FULLNAME}"-opa \
  "${FULLNAME}"-execution-runner
do
  wait_rollout "${NAMESPACE}" "${workload}" "${ROLL_OUT_TIMEOUT}"
done

echo "Bootstrapping platform baseline"
RELEASE="${RELEASE}" \
NAMESPACE="${NAMESPACE}" \
FULLNAME="${FULLNAME}" \
BOOTSTRAP_IMAGE="${BOOTSTRAP_IMAGE}" \
ADMIN_PASSWORD="${ADMIN_PASSWORD}" \
"${BOOTSTRAP_SCRIPT_PATH}"

echo "Waiting for control plane and web"
wait_rollout "${NAMESPACE}" "${FULLNAME}"-control-plane "${ROLL_OUT_TIMEOUT}"
wait_rollout "${NAMESPACE}" "${FULLNAME}"-web "${ROLL_OUT_TIMEOUT}"

API_URL="http://127.0.0.1:18080"
WEB_URL="http://127.0.0.1:18081"

if [[ "${START_PORT_FORWARDS}" == "true" ]]; then
  start_port_forward control-plane "svc/${FULLNAME}-control-plane" "18080:8080"
  start_port_forward web "svc/${FULLNAME}-web" "18081:8080"
  wait_for_http "${API_URL}/api/health" "API health endpoint" || true
  wait_for_http "${WEB_URL}/healthz" "web health endpoint" || true
fi

cat <<EOF

CuteOS local cluster is ready.

Dashboard:
  ${WEB_URL}

API:
  ${API_URL}/api/health

Sign in as:
  Username: admin
  Password: ${ADMIN_PASSWORD}

Admin password source:
  $(if [[ "${GENERATED_ADMIN_PASSWORD}" == "true" ]]; then echo "generated by installer"; else echo "provided by caller"; fi)
EOF

if [[ "${START_PORT_FORWARDS}" != "true" ]]; then
  cat <<EOF

Port-forwards were skipped. Start them manually:
  ${KUBECTL_DISPLAY_PREFIX} -n ${NAMESPACE} port-forward svc/${FULLNAME}-control-plane 18080:8080
  ${KUBECTL_DISPLAY_PREFIX} -n ${NAMESPACE} port-forward svc/${FULLNAME}-web 18081:8080
EOF
else
  cat <<EOF

Local state:
  Port-forward PID files and logs live in ${STATE_DIR}
EOF
fi
