#!/usr/bin/env bash
# Sync Keycloak users into Jenkins local accounts (for CLI/API use).
# Jenkins is OIDC-enabled, but local users can still be provisioned for tokens.
# Requires: curl, jq, kubectl. Needs Jenkins admin user+API token.

set -euo pipefail

require() { command -v "$1" >/dev/null 2>&1 || { echo "missing required binary: $1" >&2; exit 1; }; }
require curl; require jq; require kubectl

: "${KEYCLOAK_URL:=https://sso.bstein.dev}"
: "${KEYCLOAK_REALM:=atlas}"
: "${KEYCLOAK_CLIENT_ID:?set KEYCLOAK_CLIENT_ID or export via secret}"
: "${KEYCLOAK_CLIENT_SECRET:?set KEYCLOAK_CLIENT_SECRET or export via secret}"
: "${JENKINS_URL:=https://ci.bstein.dev}"
: "${JENKINS_NAMESPACE:=jenkins}"
: "${JENKINS_ADMIN_SECRET_NAME:=jenkins-admin-token}"
: "${JENKINS_ADMIN_USER_KEY:=username}"
: "${JENKINS_ADMIN_TOKEN_KEY:=token}"
: "${DEFAULT_PASSWORD:=TempSsoPass!2025}"

fetch_token() {
  curl -fsS -X POST \
    -d "grant_type=client_credentials" \
    -d "client_id=${KEYCLOAK_CLIENT_ID}" \
    -d "client_secret=${KEYCLOAK_CLIENT_SECRET}" \
    "${KEYCLOAK_URL}/realms/${KEYCLOAK_REALM}/protocol/openid-connect/token" \
    | jq -r '.access_token'
}

pull_users() {
  local token="$1"
  curl -fsS -H "Authorization: Bearer ${token}" \
    "${KEYCLOAK_URL}/admin/realms/${KEYCLOAK_REALM}/users?max=500" \
    | jq -r '.[] | select(.enabled == true) | select(.username | startswith("service-account-") | not) | [.id, .username, (.email // "")] | @tsv'
}

get_admin_auth() {
  local user token
  if [[ -n "${JENKINS_ADMIN_USER:-}" && -n "${JENKINS_ADMIN_TOKEN:-}" ]]; then
    echo "${JENKINS_ADMIN_USER}:${JENKINS_ADMIN_TOKEN}"
    return
  fi
  user=$(kubectl -n "${JENKINS_NAMESPACE}" get secret "${JENKINS_ADMIN_SECRET_NAME}" -o "jsonpath={.data.${JENKINS_ADMIN_USER_KEY}}" | base64 -d)
  token=$(kubectl -n "${JENKINS_NAMESPACE}" get secret "${JENKINS_ADMIN_SECRET_NAME}" -o "jsonpath={.data.${JENKINS_ADMIN_TOKEN_KEY}}" | base64 -d)
  echo "${user}:${token}"
}

get_crumb() {
  local auth="$1"
  curl -fsS -u "${auth}" "${JENKINS_URL}/crumbIssuer/api/json" | jq -r .crumb
}

user_exists() {
  local auth="$1" user="$2"
  local code
  code=$(curl -s -o /dev/null -w '%{http_code}' -u "${auth}" "${JENKINS_URL}/user/${user}/api/json")
  [[ "${code}" == "200" ]]
}

create_user() {
  local auth="$1" crumb="$2" username="$3" email="$4"
  local status
  status=$(curl -s -o /dev/null -w '%{http_code}' \
    -u "${auth}" \
    -H "Jenkins-Crumb: ${crumb}" \
    -X POST \
    --data "username=${username}&password1=${DEFAULT_PASSWORD}&password2=${DEFAULT_PASSWORD}&fullname=${username}&email=${email}" \
    "${JENKINS_URL}/securityRealm/createAccountByAdmin")

  if [[ "${status}" == "200" || "${status}" == "302" ]]; then
    echo "created jenkins user ${username}"
  elif [[ "${status}" == "400" ]]; then
    echo "jenkins user ${username} already exists (400)" >&2
  else
    echo "failed to create jenkins user ${username} (status ${status})" >&2
  fi
}

main() {
  local kc_token auth crumb
  kc_token="$(fetch_token)"
  auth="$(get_admin_auth)"
  crumb="$(get_crumb "${auth}")"

  while IFS=$'\t' read -r _ uid email; do
    if user_exists "${auth}" "${uid}"; then
      continue
    fi
    create_user "${auth}" "${crumb}" "${uid}" "${email}"
  done < <(pull_users "${kc_token}")
}

main "$@"
