#!/bin/bash
#
# All-in-one DoHzel macOS setup: enroll, download signed DNS mobileconfig, install.
#
# Use this for a single MDM "Run Script" or manual testing on one Mac.
# For enterprise MDM at scale, prefer the split scripts (enroll + provision)
# and install the .mobileconfig via your MDM configuration profile channel.
#
# Requires: curl and plutil (both built into macOS). No Python/Ruby needed.
#
# Usage:
#   dohzel-mdm-standalone.sh --context-token TOKEN --join-token TOKEN [options]

set -euo pipefail

dohzel_mdm_log() { echo "[dohzel-mdm] $*"; }
dohzel_mdm_die() { dohzel_mdm_log "ERROR: $*"; exit 1; }

dohzel_mdm_need_curl() {
  command -v curl >/dev/null 2>&1 || dohzel_mdm_die "curl is required (/usr/bin/curl on macOS)"
}

dohzel_mdm_need_plutil() {
  command -v plutil >/dev/null 2>&1 || dohzel_mdm_die "plutil is required (built into macOS)"
}

# Escape a string for safe inclusion in a JSON document.
dohzel_mdm_json_escape() {
  local s="$1"
  s="${s//\\/\\\\}"
  s="${s//\"/\\\"}"
  printf '%s' "$s"
}

# Read a scalar value at a key path from a JSON file (empty if missing).
dohzel_mdm_json_get() {
  local file="$1" keypath="$2"
  plutil -extract "${keypath}" raw -o - "${file}" 2>/dev/null
}

# Build the enroll request body.
dohzel_mdm_build_enroll_json() {
  local context_token="$1" device_name="$2" join_token="$3"
  printf '{"joinToken":"%s","name":"%s","code":"%s"}' \
    "$(dohzel_mdm_json_escape "${context_token}")" \
    "$(dohzel_mdm_json_escape "${device_name}")" \
    "$(dohzel_mdm_json_escape "${join_token}")"
}

# POST a JSON body; response is written to stdout.
dohzel_mdm_api_post_json() {
  local url="$1" body="$2"
  curl -sS -f -X POST "${url}" \
    -H "Content-Type: application/json" \
    -d "${body}"
}

# Parse the enroll response file; write the state file; print the device token.
dohzel_mdm_save_enroll_state() {
  local resp_file="$1" state_file="$2"
  local err token doh id device_management profile_id

  err="$(dohzel_mdm_json_get "${resp_file}" error || true)"
  if [[ -n "${err}" ]]; then
    dohzel_mdm_log "API error: ${err}"
    return 1
  fi

  token="$(dohzel_mdm_json_get "${resp_file}" data.token || true)"
  if [[ -z "${token}" ]]; then
    dohzel_mdm_log "Enroll response missing data.token"
    return 1
  fi

  doh="$(dohzel_mdm_json_get "${resp_file}" data.doh || true)"
  id="$(dohzel_mdm_json_get "${resp_file}" data.id || true)"
  device_management="$(dohzel_mdm_json_get "${resp_file}" data.deviceManagement || true)"
  profile_id="$(dohzel_mdm_json_get "${resp_file}" data.profileId || true)"

  cat > "${state_file}" <<EOF
{
  "token": "$(dohzel_mdm_json_escape "${token}")",
  "doh": "$(dohzel_mdm_json_escape "${doh}")",
  "id": "$(dohzel_mdm_json_escape "${id}")",
  "deviceManagement": "$(dohzel_mdm_json_escape "${device_management}")",
  "profileId": "$(dohzel_mdm_json_escape "${profile_id}")"
}
EOF

  printf '%s' "${token}"
}

# Ensure the downloaded file is a mobileconfig, not an HTML error page.
dohzel_mdm_assert_mobileconfig() {
  local path="$1"
  [[ -s "${path}" ]] || dohzel_mdm_die "Profile file is missing or empty: ${path}"
  if head -c 20 "${path}" | grep -qi '<!DOCTYPE'; then
    dohzel_mdm_die "Downloaded file looks like HTML, not a mobileconfig"
  fi
}

dohzel_mdm_default_device_name() {
  scutil --get ComputerName 2>/dev/null || hostname -s
}

# Download the mobileconfig for a device token.
dohzel_mdm_download_mobileconfig() {
  local api_base="$1" device_token="$2" output="$3"
  dohzel_mdm_log "Downloading mobileconfig to ${output}"
  curl -sS -f -o "${output}" \
    -H "Authorization: ${device_token}" \
    "${api_base}/dohzel/device/mobileconfig" \
    || dohzel_mdm_die "mobileconfig download failed (re-enroll with --force?)"
  dohzel_mdm_assert_mobileconfig "${output}"
}

# Install a downloaded .mobileconfig on macOS.
dohzel_mdm_install_mobileconfig() {
  local path="$1"
  dohzel_mdm_assert_mobileconfig "${path}"

  if profiles install -path "${path}" 2>/dev/null; then
    dohzel_mdm_log "Profile installed via profiles(8)"
    return 0
  fi

  dohzel_mdm_log "profiles install unavailable (macOS 13+); opening System Settings"
  open "${path}"
  dohzel_mdm_log "Profile queued in System Settings > Privacy & Security > Profiles"
  dohzel_mdm_log "User action may be required unless MDM pushes the profile separately"
}

# Reuse an existing enrollment or enroll via the API. Sets DEVICE_TOKEN.
dohzel_mdm_ensure_enrolled() {
  local context_token="$1" join_token="$2" device_name="$3" state_file="$4" api_base="$5" force="$6"

  if [[ -f "${state_file}" && "${force}" != "1" ]]; then
    if DEVICE_TOKEN="$(dohzel_mdm_json_get "${state_file}" token)" && [[ -n "${DEVICE_TOKEN}" ]]; then
      dohzel_mdm_log "Using existing enrollment from ${state_file}"
      return 0
    fi
  fi

  dohzel_mdm_log "Enrolling device name=${device_name}"
  local body resp_file
  body="$(dohzel_mdm_build_enroll_json "${context_token}" "${device_name}" "${join_token}")"
  resp_file="$(mktemp -t dohzel-mdm)"

  if ! dohzel_mdm_api_post_json "${api_base}/dohzel/device/enroll" "${body}" > "${resp_file}"; then
    rm -f "${resp_file}"
    dohzel_mdm_die "Enroll HTTP request failed"
  fi

  if ! DEVICE_TOKEN="$(dohzel_mdm_save_enroll_state "${resp_file}" "${state_file}")"; then
    rm -f "${resp_file}"
    dohzel_mdm_die "Enroll API error (check context token and join token)"
  fi
  rm -f "${resp_file}"

  dohzel_mdm_log "Enrolled successfully; device id=$(dohzel_mdm_json_get "${state_file}" id)"
}

API_BASE="https://api.hafnova.com/api"
CONTEXT_TOKEN=""
JOIN_TOKEN=""
DEVICE_NAME=""
STATE_DIR="/var/db/dohzel"
STATE_FILE=""
OUTPUT=""
FORCE_REENROLL=0
SKIP_INSTALL=0
DEVICE_TOKEN=""

usage() {
  cat <<EOF
Usage: $(basename "$0") --context-token TOKEN --join-token TOKEN [options]

Enrolls this Mac, downloads the DoHzel DNS mobileconfig, and installs it.

Required:
  --context-token TOKEN  Context token from Hafnova (tenant enrollment key)
  --join-token TOKEN     Join token from DoHzel console (Add Device → Managed or Freedom)

Options:
  --api-base URL         API root (default: https://api.hafnova.com/api)
  --device-name NAME     Device label in DoHzel (default: Mac computer name)
  --state-dir DIR        State directory (default: /var/db/dohzel)
  --output PATH          Mobileconfig path (default: STATE_DIR/dohzel-dns.mobileconfig)
  --force                Enroll a new device even if state file exists
  --no-install           Download only; do not run profiles install / open
  -h, --help             Show this help

If already enrolled, re-downloads and re-installs the profile (unless --force
creates a new device enrollment first).
EOF
}

while [[ $# -gt 0 ]]; do
  case "$1" in
    --context-token) CONTEXT_TOKEN="${2:-}"; shift 2 ;;
    --join-token) JOIN_TOKEN="${2:-}"; shift 2 ;;
    --api-base) API_BASE="${2:-}"; shift 2 ;;
    --device-name) DEVICE_NAME="${2:-}"; shift 2 ;;
    --state-dir) STATE_DIR="${2:-}"; shift 2 ;;
    --output) OUTPUT="${2:-}"; shift 2 ;;
    --force) FORCE_REENROLL=1; shift ;;
    --no-install) SKIP_INSTALL=1; shift ;;
    -h|--help) usage; exit 0 ;;
    *) dohzel_mdm_die "Unknown argument: $1 (use --help)" ;;
  esac
done

[[ -n "${CONTEXT_TOKEN}" ]] || dohzel_mdm_die "--context-token is required"
[[ -n "${JOIN_TOKEN}" ]] || dohzel_mdm_die "--join-token is required"

STATE_DIR="${STATE_DIR:-/var/db/dohzel}"
STATE_FILE="${STATE_DIR}/enrollment.json"
OUTPUT="${OUTPUT:-${STATE_DIR}/dohzel-dns.mobileconfig}"
DEVICE_NAME="${DEVICE_NAME:-$(dohzel_mdm_default_device_name)}"

dohzel_mdm_need_curl
dohzel_mdm_need_plutil
mkdir -p "${STATE_DIR}"

dohzel_mdm_ensure_enrolled \
  "${CONTEXT_TOKEN}" \
  "${JOIN_TOKEN}" \
  "${DEVICE_NAME}" \
  "${STATE_FILE}" \
  "${API_BASE}" \
  "${FORCE_REENROLL}"

DEVICE_TOKEN="$(dohzel_mdm_json_get "${STATE_FILE}" token)"
dohzel_mdm_download_mobileconfig "${API_BASE}" "${DEVICE_TOKEN}" "${OUTPUT}"

if [[ "${SKIP_INSTALL}" == "1" ]]; then
  dohzel_mdm_log "Profile saved at ${OUTPUT} (--no-install)"
else
  dohzel_mdm_install_mobileconfig "${OUTPUT}"
fi

dohzel_mdm_log "Done."
echo "${OUTPUT}"
