#!/usr/bin/bash
# /usr/bin/agent-enforcer
# Agent Enforcer — AI Configuration Enforcement Agent
#
# Syncs AI tool configurations from a central distribution API into a local
# canonical store (/var/lib/agent-enforcer/bundles/<assistant>/) and applies
# them to every user home on this host. Runs on Rocky/RHEL 9 (systemd) and
# macOS (launchd) from the same script.
#
# Usage:
#   sudo agent-enforcer register [--endpoint <url>] [--user-id <id>]
#                                [--old-license-id <id>] [--old-user-id <id>]
#                                [--no-prompt]
#   sudo agent-enforcer configure --endpoint <url>
#   agent-enforcer status
#   agent-enforcer sync
#   sudo agent-enforcer autostart <enable|disable>
#   agent-enforcer --daemon        (used by systemd / launchd)

set -euo pipefail

readonly AGENT_VERSION="1.0.0"
readonly CONFIG_FILE="/etc/agent-enforcer/config"
readonly LICENSE_FILE="/var/lib/agent-enforcer/license"
readonly STAGING_DIR="/tmp/agent-enforcer-staging"
readonly STATE_DIR="/var/lib/agent-enforcer"
readonly DEFAULT_ENDPOINT="https://alchemistfederal.com/agent-enforcer"
readonly CURSOR_MARKER="<!-- managed by agent-enforcer -->"
readonly LAUNCHD_PLIST="/Library/LaunchDaemons/com.alchemist.agent-enforcer.plist"

# ---------------------------------------------------------------------------
# Platform detection — one script for Rocky/RHEL (systemd) and macOS (launchd)
# ---------------------------------------------------------------------------

OS_NAME="$(uname -s 2>/dev/null || echo Linux)"
if [[ "$OS_NAME" == "Darwin" ]]; then
  AGENT_TYPE="MACOS"
else
  AGENT_TYPE="ROCKY9"
fi
readonly AGENT_TYPE

# Linux machine-id first (also the seam the test harness patches), then the
# macOS hardware UUID, then hostname as a last resort
_machine_id() {
  local machine_id
  machine_id=$(cat /etc/machine-id 2>/dev/null || true)
  if [[ -z "$machine_id" && "$OS_NAME" == "Darwin" ]]; then
    machine_id=$(ioreg -rd1 -c IOPlatformExpertDevice 2>/dev/null \
      | awk -F'"' '/IOPlatformUUID/{print $4}' || true)
  fi
  [[ -z "$machine_id" ]] && machine_id=$(hostname)
  echo "$machine_id"
}

# Prints each real user home directory, one per line.
# AGENT_ENFORCER_HOME_ROOT overrides the search root (tests / containers).
_home_dirs() {
  if [[ -n "${AGENT_ENFORCER_HOME_ROOT:-}" ]]; then
    find "$AGENT_ENFORCER_HOME_ROOT" -maxdepth 1 -mindepth 1 -type d 2>/dev/null
  elif [[ "$OS_NAME" == "Darwin" ]]; then
    find /Users -maxdepth 1 -mindepth 1 -type d ! -name Shared ! -name Guest 2>/dev/null
  else
    find /home -maxdepth 1 -mindepth 1 -type d 2>/dev/null
  fi
}

# Prints "uid gid" of a path
_stat_ug() {
  if [[ "$OS_NAME" == "Darwin" ]]; then
    stat -f '%u %g' "$1" 2>/dev/null || echo "0 0"
  else
    stat -c '%u %g' "$1" 2>/dev/null || echo "0 0"
  fi
}

_service_restart() {
  if [[ "$OS_NAME" == "Darwin" ]]; then
    launchctl kickstart -k "system/com.alchemist.agent-enforcer" 2>/dev/null || true
  else
    systemctl restart agent-enforcer 2>/dev/null || true
  fi
}

_service_status() {
  if [[ "$OS_NAME" == "Darwin" ]]; then
    if launchctl print "system/com.alchemist.agent-enforcer" >/dev/null 2>&1; then
      echo "active"
    else
      echo "inactive"
    fi
  else
    systemctl is-active agent-enforcer 2>/dev/null || echo "unknown"
  fi
}

_managed_settings_dir() {
  if [[ "$OS_NAME" == "Darwin" ]]; then
    echo "/Library/Application Support/ClaudeCode"
  else
    echo "/etc/claude-code"
  fi
}

usage() {
  cat >&2 <<'EOF'
Usage: agent-enforcer <command> [options]

Commands:
  register              Register this agent with the enforcement API (requires sudo)
    --endpoint <url>    API base URL (default: https://alchemistfederal.com/agent-enforcer)
    --user-id <id>      Your user ID (email, username, or any unique string)
    --old-license-id    Previous license ID (for transfers to a new host)
    --old-user-id       User ID tied to the old license (required with --old-license-id)
    --no-prompt         Non-interactive mode; --user-id becomes required

  configure             Update the API endpoint without re-registering (requires sudo)
    --endpoint <url>    New API base URL

  status                Show current configuration, bundle versions, and last sync
  describe              Show enforcement status, enforced assistants, and license summary
  sync                  Force an immediate sync from the enforcement API
  autostart <on|off>    Enable/disable start-on-boot (requires sudo; on|off, enable|disable)
  --daemon              Run as a background daemon (used by systemd / launchd)

Examples:
  sudo agent-enforcer register
  sudo agent-enforcer register --no-prompt --user-id alice@example.com
  sudo agent-enforcer register --no-prompt --user-id alice@example.com \
    --old-license-id abc123-... --old-user-id alice@old-host.com
  sudo agent-enforcer configure --endpoint https://alchemistfederal.com/agent-enforcer
  agent-enforcer status
  sudo agent-enforcer describe
  agent-enforcer sync
  sudo agent-enforcer autostart off
EOF
  exit 1
}

# ---------------------------------------------------------------------------
# banner — install-time branding, invoked from the RPM %post scriptlet
# ---------------------------------------------------------------------------

cmd_banner() {
  # Quoted heredoc: the art contains backslashes and pipes that must not expand
  cat <<'BANNER'

               _      ____  _____  _   _  _____
              / \    / ___|| ____|| \ | ||_   _|
             / _ \  | |  _ |  _|  |  \| |  | |
            / ___ \ | |_| || |___ | |\  |  | |
           /_/   \_\ \____||_____||_| \_|  |_|

   _____  _   _  _____   ___   ____    ____  _____  ____
  | ____|| \ | ||  ___| / _ \ |  _ \  / ___|| ____||  _ \
  |  _|  |  \| || |_   | | | || |_) || |    |  _|  | |_) |
  | |___ | |\  ||  _|  | |_| ||  _ < | |___ | |___ |  _ <
  |_____||_| \_||_|     \___/ |_| \_\ \____||_____||_| \_\

                   Powered by Alchemist
BANNER
  echo "                          v${AGENT_VERSION}"
  echo ""
}

# ---------------------------------------------------------------------------
# register
# ---------------------------------------------------------------------------

cmd_register() {
  if [[ $EUID -ne 0 ]]; then
    echo "Error: 'register' requires sudo." >&2
    exit 1
  fi

  local endpoint="$DEFAULT_ENDPOINT"
  local user_id=""
  local old_license_id=""
  local old_user_id=""
  local no_prompt=0

  while [[ $# -gt 0 ]]; do
    case "$1" in
      --endpoint)       endpoint="$2";        shift 2 ;;
      --user-id)        user_id="$2";         shift 2 ;;
      --old-license-id) old_license_id="$2";  shift 2 ;;
      --old-user-id)    old_user_id="$2";     shift 2 ;;
      --no-prompt)      no_prompt=1;          shift ;;
      --agent-type)     shift 2 ;;   # accepted but ignored — type is detected at runtime
      --agent-version)  shift 2 ;;   # accepted but ignored — version is from AGENT_VERSION
      *) echo "Unknown option: $1" >&2; usage ;;
    esac
  done

  if [[ $no_prompt -eq 1 ]]; then
    if [[ -z "$user_id" ]]; then
      echo "Error: --user-id is required when using --no-prompt." >&2
      exit 1
    fi
  else
    # Interactive prompts
    echo "Agent Enforcer — Registration"
    echo ""
    read -rp "Enforcement API endpoint [${DEFAULT_ENDPOINT}]: " input_endpoint
    [[ -n "$input_endpoint" ]] && endpoint="$input_endpoint"

    read -rp "Your user ID (email or unique string): " input_user_id
    user_id="${input_user_id:-}"
    if [[ -z "$user_id" ]]; then
      echo "Error: user ID is required." >&2
      exit 1
    fi

    read -rp "Old license ID (leave blank for new registration): " input_old_license
    old_license_id="${input_old_license:-}"
    if [[ -n "$old_license_id" ]]; then
      read -rp "Old user ID (the user_id tied to that license): " input_old_user
      old_user_id="${input_old_user:-}"
      if [[ -z "$old_user_id" ]]; then
        echo "Error: old user ID is required when providing an old license ID." >&2
        exit 1
      fi
    fi
  fi

  local machine_id
  machine_id=$(_machine_id)

  # Build JSON body
  local body
  body=$(printf '{"user_id":"%s","agent_type":"%s","agent_version":"%s","machine_id":"%s"' \
    "$user_id" "$AGENT_TYPE" "$AGENT_VERSION" "$machine_id")
  if [[ -n "$old_license_id" ]]; then
    body="${body},\"old_license_id\":\"${old_license_id}\",\"old_user_id\":\"${old_user_id}\""
  fi
  body="${body}}"

  echo "Registering with ${endpoint}..."

  local http_code response_body tmp_response
  tmp_response=$(mktemp)
  http_code=$(curl -s -o "$tmp_response" -w "%{http_code}" \
    -X POST "${endpoint}/register" \
    -H "Content-Type: application/json" \
    -d "$body" \
    --max-time 30 2>/dev/null) || {
    rm -f "$tmp_response"
    echo "Error: could not reach the enforcement API at ${endpoint}." >&2
    echo "  Check your network connection and endpoint URL." >&2
    exit 1
  }
  response_body=$(cat "$tmp_response")
  rm -f "$tmp_response"

  if [[ "$http_code" != "200" ]]; then
    local error_msg
    error_msg=$(echo "$response_body" | grep -o '"error":"[^"]*"' | cut -d'"' -f4 || echo "Unknown error")
    local detail_msg
    detail_msg=$(echo "$response_body" | grep -o '"detail":"[^"]*"' | cut -d'"' -f4 || true)
    echo "Error: registration failed (HTTP ${http_code})." >&2
    echo "  ${error_msg}" >&2
    [[ -n "$detail_msg" ]] && echo "  ${detail_msg}" >&2
    exit 1
  fi

  # Parse license_id from JSON response
  local license_id
  license_id=$(echo "$response_body" | python3 -c "import sys,json; print(json.load(sys.stdin).get('license_id',''))" 2>/dev/null || true)
  if [[ -z "$license_id" ]]; then
    echo "Error: registration succeeded but no license_id in response." >&2
    echo "  Response: ${response_body}" >&2
    exit 1
  fi

  # Store license securely (root:root 600 — service runs as root)
  mkdir -p "$STATE_DIR"
  cat > "$LICENSE_FILE" <<EOF
LICENSE_ID=${license_id}
USER_ID=${user_id}
MACHINE_ID=${machine_id}
ENDPOINT=${endpoint}
REGISTERED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)
EOF
  chmod 600 "$LICENSE_FILE"
  chown root:root "$LICENSE_FILE"

  # Write endpoint to config (for configure/status commands)
  mkdir -p "$(dirname "$CONFIG_FILE")"
  cat > "$CONFIG_FILE" <<EOF
ENDPOINT=${endpoint}
CONFIGURED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)
EOF
  chmod 600 "$CONFIG_FILE"

  echo ""
  echo "============================================================"
  echo "  Agent Enforcer — License Registered Successfully"
  echo "============================================================"
  echo "  License ID : ${license_id}"
  echo "  User ID    : ${user_id}"
  echo ""
  echo "  IMPORTANT: Save these values. You will need them to"
  echo "  transfer your license if you rebuild or migrate this host."
  echo "============================================================"
  echo ""

  _service_restart
  echo "Service restarted. Run 'agent-enforcer status' to verify."
}

# ---------------------------------------------------------------------------
# configure
# ---------------------------------------------------------------------------

cmd_configure() {
  if [[ $EUID -ne 0 ]]; then
    echo "Error: 'configure' requires sudo." >&2
    exit 1
  fi

  local endpoint=""
  while [[ $# -gt 0 ]]; do
    case "$1" in
      --endpoint) endpoint="$2"; shift 2 ;;
      *) echo "Unknown option: $1" >&2; usage ;;
    esac
  done

  if [[ -z "$endpoint" ]]; then
    echo "Error: --endpoint <url> is required." >&2
    usage
  fi

  mkdir -p "$(dirname "$CONFIG_FILE")"
  cat > "$CONFIG_FILE" <<EOF
ENDPOINT=${endpoint}
CONFIGURED_AT=$(date -u +%Y-%m-%dT%H:%M:%SZ)
EOF
  chmod 600 "$CONFIG_FILE"

  # Update endpoint in license file if it exists
  if [[ -f "$LICENSE_FILE" ]]; then
    # Rewrite license file preserving all fields but updating ENDPOINT
    local tmp_lic
    tmp_lic=$(mktemp)
    grep -v '^ENDPOINT=' "$LICENSE_FILE" > "$tmp_lic" || true
    echo "ENDPOINT=${endpoint}" >> "$tmp_lic"
    cp "$tmp_lic" "$LICENSE_FILE"
    rm -f "$tmp_lic"
    chmod 600 "$LICENSE_FILE"
    chown root:root "$LICENSE_FILE"
  fi

  echo "Agent Enforcer configured."
  echo "  Endpoint : ${endpoint}"
  echo ""
  _service_restart
  echo "Service restarted. Run 'agent-enforcer status' to verify."
}

# ---------------------------------------------------------------------------
# autostart — start-on-boot toggle (systemd enable / launchd bootstrap)
# ---------------------------------------------------------------------------

cmd_autostart() {
  if [[ $EUID -ne 0 ]]; then
    echo "Error: 'autostart' requires sudo." >&2
    exit 1
  fi

  local action="${1:-}"
  case "$action" in
    on|enable)   action="enable" ;;
    off|disable) action="disable" ;;
    *)
      echo "Error: autostart requires 'on' or 'off' (also accepts enable/disable)." >&2
      exit 1
      ;;
  esac

  if [[ "$OS_NAME" == "Darwin" ]]; then
    if [[ "$action" == "enable" ]]; then
      launchctl bootstrap system "$LAUNCHD_PLIST" 2>/dev/null || true
      launchctl enable "system/com.alchemist.agent-enforcer" 2>/dev/null || true
      echo "Autostart enabled (launchd daemon loaded)."
    else
      launchctl bootout "system/com.alchemist.agent-enforcer" 2>/dev/null || true
      echo "Autostart disabled (launchd daemon unloaded)."
      echo "Re-enable with: sudo agent-enforcer autostart on"
    fi
  else
    if [[ "$action" == "enable" ]]; then
      systemctl enable --now agent-enforcer 2>/dev/null || true
      echo "Autostart enabled (systemd service enabled)."
    else
      systemctl disable --now agent-enforcer 2>/dev/null || true
      echo "Autostart disabled (systemd service disabled)."
      echo "Re-enable with: sudo agent-enforcer autostart on"
    fi
  fi
}

# ---------------------------------------------------------------------------
# status
# ---------------------------------------------------------------------------

_print_applied_versions() {
  local indent="${1:-  }"
  if [[ -r "${STATE_DIR}/applied-versions" && -s "${STATE_DIR}/applied-versions" ]]; then
    while IFS='=' read -r name version; do
      [[ -z "$name" ]] && continue
      printf '%s%-15s: %s\n' "$indent" "$name" "${version:-<unknown>}"
    done < "${STATE_DIR}/applied-versions"
  else
    echo "${indent}(no bundles applied yet)"
  fi
}

cmd_status() {
  echo "=== Agent Enforcer Status ==="
  echo "  Version        : ${AGENT_VERSION}"
  echo "  Agent Type     : ${AGENT_TYPE}"
  echo ""

  if [[ -f "$CONFIG_FILE" ]]; then
    # shellcheck source=/dev/null
    source "$CONFIG_FILE"
    echo "  Endpoint       : ${ENDPOINT:-<not set>}"
    echo "  Configured at  : ${CONFIGURED_AT:-<unknown>}"
  else
    echo "  Endpoint       : <not configured>"
    echo "  Run: sudo agent-enforcer configure --endpoint <url>"
  fi

  echo ""

  if [[ -f "$LICENSE_FILE" ]]; then
    # shellcheck source=/dev/null
    source "$LICENSE_FILE"
    echo "  License ID     : ${LICENSE_ID:-<unknown>}"
    echo "  User ID        : ${USER_ID:-<unknown>}"
    echo "  Machine ID     : ${MACHINE_ID:-<unknown>}"
    echo "  Registered at  : ${REGISTERED_AT:-<unknown>}"
  else
    echo "  License        : NOT REGISTERED"
    echo "  Run: sudo agent-enforcer register"
  fi

  echo ""
  echo "  Applied bundle versions:"
  _print_applied_versions "    "
  echo ""

  if [[ -f "${STATE_DIR}/last-sync" ]]; then
    echo "  Last sync      : $(cat "${STATE_DIR}/last-sync")"
  else
    echo "  Last sync      : never"
  fi

  echo "  Service        : $(_service_status)"
}

# ---------------------------------------------------------------------------
# describe — customer-facing enforcement summary (banner + status)
# ---------------------------------------------------------------------------

cmd_describe() {
  cmd_banner

  echo "============================================================"
  echo "  Agent Enforcer — Enforcement Summary"
  echo "============================================================"

  if [[ -f "$LICENSE_FILE" && -f "$CONFIG_FILE" ]]; then
    echo "  Enforcement     : ENFORCING"
  else
    echo "  Enforcement     : NOT REGISTERED"
    echo "  Run: sudo agent-enforcer register"
  fi
  echo ""

  echo "  Enforced assistants:"
  if [[ -r "${STATE_DIR}/assistants" && -s "${STATE_DIR}/assistants" ]]; then
    while IFS='=' read -r name state; do
      [[ -z "$name" ]] && continue
      printf '    %-15s: %s\n' "$name" "$state"
    done < "${STATE_DIR}/assistants"
  else
    printf '    %-15s: %s\n' "claude-code" "enabled (default)"
    echo "    (no sync data yet — run 'agent-enforcer sync' to refresh)"
  fi
  echo ""

  echo "  Applied bundle versions:"
  _print_applied_versions "    "
  echo ""

  # License file is root:600 — a plain source as non-root would abort under
  # set -e, so degrade to a sudo hint instead
  if [[ -r "$LICENSE_FILE" ]]; then
    # shellcheck source=/dev/null
    source "$LICENSE_FILE"
    echo "  License ID      : ${LICENSE_ID:-<unknown>}"
    echo "  User ID         : ${USER_ID:-<unknown>}"
    echo "  Endpoint        : ${ENDPOINT:-<not set>}"
    echo "  Registered at   : ${REGISTERED_AT:-<unknown>}"
  elif [[ -f "$LICENSE_FILE" ]]; then
    echo "  License         : <run with sudo to view license details>"
  fi

  if [[ -f "${STATE_DIR}/last-sync" ]]; then
    echo "  Last sync       : $(cat "${STATE_DIR}/last-sync")"
  else
    echo "  Last sync       : never"
  fi

  echo "  Service         : $(_service_status)"
  echo "============================================================"
}

# ---------------------------------------------------------------------------
# sync — fetch phase: pull bundles from the API into the canonical local store
# ---------------------------------------------------------------------------

# Reads applied-versions state into a JSON object string for the sync request
_applied_versions_json() {
  if [[ -r "${STATE_DIR}/applied-versions" && -s "${STATE_DIR}/applied-versions" ]]; then
    python3 -c "
import sys
pairs = {}
with open('${STATE_DIR}/applied-versions') as f:
    for line in f:
        line = line.strip()
        if '=' in line:
            k, v = line.split('=', 1)
            pairs[k] = v
import json
print(json.dumps(pairs))
" 2>/dev/null || echo '{}'
  else
    echo '{}'
  fi
}

do_sync() {
  if [[ ! -f "$LICENSE_FILE" ]]; then
    echo "$(date -u): agent not registered — skipping sync" >> "${STATE_DIR}/sync-errors.log"
    return 0
  fi

  if [[ ! -f "$CONFIG_FILE" ]]; then
    echo "$(date -u): not configured — skipping sync" >> "${STATE_DIR}/sync-errors.log"
    return 0
  fi

  # shellcheck source=/dev/null
  source "$LICENSE_FILE"
  # shellcheck source=/dev/null
  source "$CONFIG_FILE"

  local endpoint="${ENDPOINT:-$DEFAULT_ENDPOINT}"
  local license_id="${LICENSE_ID:-}"
  local machine_id="${MACHINE_ID:-}"

  if [[ -z "$license_id" || -z "$machine_id" ]]; then
    echo "$(date -u): license file is incomplete — re-register" >> "${STATE_DIR}/sync-errors.log"
    return 1
  fi

  # Report what we have applied so the fleet console can display it
  local applied_json
  applied_json=$(_applied_versions_json)

  # Call the sync endpoint to get presigned URLs
  local tmp_resp
  tmp_resp=$(mktemp)
  local http_code
  http_code=$(curl -s -o "$tmp_resp" -w "%{http_code}" \
    -X POST "${endpoint}/sync" \
    -H "Content-Type: application/json" \
    -d "{\"license_id\":\"${license_id}\",\"machine_id\":\"${machine_id}\",\"agent_version\":\"${AGENT_VERSION}\",\"applied_versions\":${applied_json}}" \
    --max-time 30 2>/dev/null) || {
    rm -f "$tmp_resp"
    echo "$(date -u): sync API unreachable — will retry next cycle" >> "${STATE_DIR}/sync-errors.log"
    apply_bundles  # offline: re-apply from the local store anyway
    return 0  # soft failure — don't crash the daemon
  }

  local response_body
  response_body=$(cat "$tmp_resp")
  rm -f "$tmp_resp"

  if [[ "$http_code" == "403" || "$http_code" == "401" ]]; then
    echo "$(date -u): license invalid or inactive (HTTP ${http_code}) — re-register to continue" \
      >> "${STATE_DIR}/sync-errors.log"
    return 1
  fi

  if [[ "$http_code" != "200" ]]; then
    echo "$(date -u): sync failed with HTTP ${http_code} — will retry next cycle" \
      >> "${STATE_DIR}/sync-errors.log"
    apply_bundles  # transient server trouble: still re-apply local store
    return 0  # treat unknown errors as transient
  fi

  # Persist enforced-assistants state for `describe`. Must happen before the
  # empty-files early return below — a sync that returns no files still
  # carries authoritative assistant toggles. Non-empty guard: an old server
  # without the assistants key must not truncate prior state.
  mkdir -p "$STATE_DIR"
  local assistants_state
  assistants_state=$(echo "$response_body" | python3 -c "
import sys, json
d = json.load(sys.stdin).get('assistants') or {}
for name, enabled in d.items():
    print(name + '=' + ('enabled' if enabled else 'disabled'))
" 2>/dev/null || true)
  if [[ -n "$assistants_state" ]]; then
    printf '%s\n' "$assistants_state" > "${STATE_DIR}/assistants"
  fi

  # Normalize the response into per-assistant download manifests:
  #   assistant<TAB>rel_path<TAB>url<TAB>version
  # Version comes LAST because it may be empty (legacy servers) and bash
  # collapses adjacent tab delimiters between fields. Legacy responses carry
  # only {"files":{...}}, treated as claude-code with no version.
  local manifest
  manifest=$(echo "$response_body" | python3 -c "
import sys, json
d = json.load(sys.stdin)
bundles = d.get('bundles')
if not isinstance(bundles, dict) or not bundles:
    files = d.get('files') or {}
    bundles = {'claude-code': {'version': '', 'files': files}} if files else {}
for assistant, bundle in bundles.items():
    version = bundle.get('version') or ''
    for path, url in (bundle.get('files') or {}).items():
        print(assistant + '\t' + path + '\t' + url + '\t' + version)
" 2>/dev/null || true)

  if [[ -z "$manifest" ]]; then
    echo "$(date -u): no files in distribution — enforcement doc may not have been uploaded yet" \
      >> "${STATE_DIR}/sync-errors.log"
    # An empty distribution is authoritative: managed configs must not linger
    prune_bundles ""
    apply_bundles
    return 0
  fi

  # Download everything to staging — abort entirely if any download fails
  mkdir -p "$STAGING_DIR" "$STATE_DIR"
  rm -rf "${STAGING_DIR:?}"/*

  local download_ok=1
  local synced_assistants=""
  while IFS=$'\t' read -r assistant rel_path url version; do
    [[ -z "$assistant" || -z "$rel_path" || -z "$url" ]] && continue

    local dest="${STAGING_DIR}/${assistant}/${rel_path}"
    mkdir -p "$(dirname "$dest")"

    if ! curl -sf -o "$dest" "$url" --max-time 60 2>/dev/null; then
      echo "$(date -u): failed to download ${assistant}/${rel_path} — aborting sync" \
        >> "${STATE_DIR}/sync-errors.log"
      rm -rf "${STAGING_DIR:?}"/*
      download_ok=0
      break
    fi
    echo "$version" > "${STAGING_DIR}/${assistant}/.bundle-version"
    case " $synced_assistants " in
      *" $assistant "*) ;;
      *) synced_assistants="$synced_assistants $assistant" ;;
    esac
  done <<< "$manifest"

  if [[ $download_ok -eq 0 ]]; then
    return 1
  fi

  # Atomic-ish swap into the canonical store, one assistant at a time
  mkdir -p "${STATE_DIR}/bundles"
  local a
  for a in $synced_assistants; do
    rm -rf "${STATE_DIR}/bundles/${a:?}"
    mkdir -p "${STATE_DIR}/bundles/${a}"
    cp -rp "${STAGING_DIR}/${a}/." "${STATE_DIR}/bundles/${a}/"
  done
  rm -rf "${STAGING_DIR:?}"/*

  # Assistants absent from the response are no longer served — drop their
  # store dirs so apply removes their managed configs
  prune_bundles "$synced_assistants"

  # Record applied versions for status/describe and the next sync report
  : > "${STATE_DIR}/applied-versions"
  for a in $synced_assistants; do
    local v=""
    [[ -f "${STATE_DIR}/bundles/${a}/.bundle-version" ]] && v=$(cat "${STATE_DIR}/bundles/${a}/.bundle-version")
    echo "${a}=${v}" >> "${STATE_DIR}/applied-versions"
  done

  apply_bundles

  date -u +%Y-%m-%dT%H:%M:%SZ > "${STATE_DIR}/last-sync"
  echo "$(date -u): sync complete" >> "${STATE_DIR}/sync.log"
}

# Removes store dirs for assistants not in the keep list (space-separated)
prune_bundles() {
  local keep=" $1 "
  local dir a
  for dir in "${STATE_DIR}/bundles"/*/; do
    [[ -d "$dir" ]] || continue
    a=$(basename "$dir")
    case "$keep" in
      *" $a "*) ;;
      *) rm -rf "${STATE_DIR}/bundles/${a:?}" ;;
    esac
  done
}

# ---------------------------------------------------------------------------
# apply — pure-local phase: enforce whatever is in the canonical store.
# Re-run every daemon cycle even when the API is unreachable, restoring
# tampered or deleted configs offline.
# ---------------------------------------------------------------------------

apply_bundles() {
  apply_claude_code
  apply_cursor
}

apply_claude_code() {
  local store="${STATE_DIR}/bundles/claude-code"
  if [[ ! -d "$store" ]]; then
    return 0
  fi

  local home_dir claude_dir uid gid
  while IFS= read -r home_dir; do
    [[ -z "$home_dir" ]] && continue
    claude_dir="${home_dir}/.claude"
    mkdir -p "$claude_dir"
    (cd "$store" && find . -name '.bundle-version' -prune -o -type f -print) | while IFS= read -r f; do
      f="${f#./}"
      mkdir -p "$(dirname "${claude_dir}/${f}")"
      cp -p "${store}/${f}" "${claude_dir}/${f}"
    done

    read -r uid gid <<< "$(_stat_ug "$home_dir")"
    chown -R "${uid}:${gid}" "$claude_dir" 2>/dev/null || true
  done < <(_home_dirs)

  # System-level managed settings for Claude Code
  local settings_dir
  settings_dir=$(_managed_settings_dir)
  if [[ -f "${store}/settings.json" ]]; then
    mkdir -p "$settings_dir"
    cp "${store}/settings.json" "${settings_dir}/managed-settings.json"
  fi
}

apply_cursor() {
  local store="${STATE_DIR}/bundles/cursor"
  local agents_file="${store}/AGENTS.md"

  if [[ -d "$store" && -f "$agents_file" ]]; then
    local home_dir uid gid
    while IFS= read -r home_dir; do
      [[ -z "$home_dir" ]] && continue
      read -r uid gid <<< "$(_stat_ug "$home_dir")"

      # Home root — binds when the workspace is (under) the home dir
      _install_cursor_workspace "$store" "$home_dir" "$uid" "$gid"

      # Every project (git repo) under the home, searched fresh each cycle —
      # cursor has no global rules location, so coverage is per-workspace
      local git_dir project_dir
      while IFS= read -r git_dir; do
        project_dir=$(dirname "$git_dir")
        _install_cursor_workspace "$store" "$project_dir" "$uid" "$gid"
      done < <(find "$home_dir" -maxdepth 3 -name .git -type d 2>/dev/null)
    done < <(_home_dirs)
    touch "${STATE_DIR}/cursor-applied"
  elif [[ -f "${STATE_DIR}/cursor-applied" ]]; then
    # Cursor no longer served but we applied it before — remove only files we
    # own (marker-guarded), then drop the flag so this sweep runs exactly once
    local home_dir target
    while IFS= read -r home_dir; do
      [[ -z "$home_dir" ]] && continue
      while IFS= read -r target; do
        if _is_managed_cursor_file "$target"; then
          rm -f "$target"
        fi
      done < <(find "$home_dir" -maxdepth 6 \( -name AGENTS.md -o -name '*.mdc' \) -type f 2>/dev/null)
      # Drop now-empty managed rules dirs
      find "$home_dir" -maxdepth 5 -type d -path '*/.cursor/rules' -empty -delete 2>/dev/null || true
    done < <(_home_dirs)
    rm -f "${STATE_DIR}/cursor-applied"
  fi
}

# The managed marker is line 1 for AGENTS.md and sits just after the YAML
# frontmatter for .mdc rule files — check the first few lines
_is_managed_cursor_file() {
  head -n 6 "$1" 2>/dev/null | grep -qF "$CURSOR_MARKER"
}

# Installs the full cursor bundle (AGENTS.md + .cursor/rules/*.mdc) into a
# workspace. Never clobbers user-owned (non-managed) files.
_install_cursor_workspace() {
  local store="$1" workspace="$2" uid="$3" gid="$4"

  _install_cursor_file "${store}/AGENTS.md" "${workspace}/AGENTS.md" "$uid" "$gid"

  if [[ -d "${store}/.cursor/rules" ]]; then
    mkdir -p "${workspace}/.cursor/rules"
    local rules_file base
    while IFS= read -r rules_file; do
      base=$(basename "$rules_file")
      _install_cursor_file "$rules_file" "${workspace}/.cursor/rules/${base}" "$uid" "$gid"
    done < <(find "${store}/.cursor/rules" -maxdepth 1 -name '*.mdc' -type f 2>/dev/null)
    chown "${uid}:${gid}" "${workspace}/.cursor" "${workspace}/.cursor/rules" 2>/dev/null || true
  fi
}

_install_cursor_file() {
  local src="$1" target="$2" uid="$3" gid="$4"
  if [[ -f "$target" ]] && ! _is_managed_cursor_file "$target"; then
    return 0
  fi
  cp -p "$src" "$target"
  chown "${uid}:${gid}" "$target" 2>/dev/null || true
}

cmd_sync() {
  if [[ ! -f "$LICENSE_FILE" ]]; then
    echo "Agent Enforcer is not registered." >&2
    echo "Run: sudo agent-enforcer register" >&2
    exit 1
  fi
  if [[ ! -f "$CONFIG_FILE" ]]; then
    echo "Agent Enforcer is not configured." >&2
    echo "Run: sudo agent-enforcer configure --endpoint <url>" >&2
    exit 1
  fi
  echo "Running sync..."
  do_sync && echo "Sync complete." || echo "Sync failed — check ${STATE_DIR}/sync-errors.log" >&2
}

cmd_daemon() {
  mkdir -p "$STATE_DIR"
  echo "Agent Enforcer daemon started (PID $$, version ${AGENT_VERSION}, ${AGENT_TYPE})"

  if [[ ! -f "$LICENSE_FILE" ]]; then
    echo "Agent is not registered. Daemon will log this until registration is complete."
    echo "Run: sudo agent-enforcer register"
  fi

  while true; do
    if [[ ! -f "$LICENSE_FILE" ]]; then
      echo "$(date -u): not registered — run 'sudo agent-enforcer register'" \
        >> "${STATE_DIR}/sync-errors.log"
    elif [[ -f "$CONFIG_FILE" ]]; then
      do_sync 2>/dev/null || true
    fi
    sleep 900  # sync every 15 minutes
  done
}

case "${1:-}" in
  register)   shift; cmd_register "$@" ;;
  configure)  shift; cmd_configure "$@" ;;
  autostart)  shift; cmd_autostart "$@" ;;
  status)     cmd_status ;;
  describe)   cmd_describe ;;
  sync)       cmd_sync ;;
  --daemon)   cmd_daemon ;;
  banner)     cmd_banner ;;
  *)          usage ;;
esac
