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

# onboard - Validate that this Aeon instance is set up correctly (read-only).
#
# Runs a series of checks against the local repo and (when authenticated to gh)
# the remote secrets/workflows. Prints a colored checklist with a one-line fix
# instruction for every gap. Changes nothing; ./aeon init is the fixer.
#
# Usage:
#   bin/onboard                  Run every check, print summary, exit 0/1
#   bin/onboard --remote         After the checks, dispatch one heartbeat run (it
#                              costs one Actions run and model call). A green run
#                              proves Actions and the model credential work. It
#                              commits docs/status.md and only notifies when it
#                              finds something, so a quiet channel is normal.
#   bin/onboard --quiet          Suppress per-check output, print summary only
#   bin/onboard --json           Machine-readable JSON output (no colors, no fixes)
#   bin/onboard --help           Show this help

ROOT="$(cd "$(dirname "$0")/.." && pwd)"
cd "$ROOT"

REMOTE=false
QUIET=false
JSON=false

for arg in "$@"; do
  case "$arg" in
    --remote) REMOTE=true ;;
    --quiet)  QUIET=true ;;
    --json)   JSON=true; QUIET=true ;;
    --help|-h)
      sed -n '3,17p' "$0" | sed 's/^# \{0,1\}//'
      exit 0
      ;;
    *)
      echo "Unknown argument: $arg" >&2
      exit 1
      ;;
  esac
done

# --- terminal colors -------------------------------------------------------
if [[ -t 1 ]] && [[ "$JSON" != "true" ]]; then
  C_GREEN=$'\033[32m'; C_RED=$'\033[31m'; C_YELLOW=$'\033[33m'
  C_BLUE=$'\033[34m'; C_DIM=$'\033[2m'; C_RESET=$'\033[0m'; C_BOLD=$'\033[1m'
else
  C_GREEN=""; C_RED=""; C_YELLOW=""; C_BLUE=""; C_DIM=""; C_RESET=""; C_BOLD=""
fi

# --- check accumulator -----------------------------------------------------
PASS_COUNT=0
WARN_COUNT=0
FAIL_COUNT=0
JSON_ROWS=()

emit() {
  # emit <status> <name> <detail> <fix>
  local status="$1" name="$2" detail="$3" fix="$4"
  case "$status" in
    pass) PASS_COUNT=$((PASS_COUNT + 1)); icon="${C_GREEN}✓${C_RESET}" ;;
    warn) WARN_COUNT=$((WARN_COUNT + 1)); icon="${C_YELLOW}!${C_RESET}" ;;
    fail) FAIL_COUNT=$((FAIL_COUNT + 1)); icon="${C_RED}✗${C_RESET}" ;;
    *)    icon="?" ;;
  esac

  if [[ "$JSON" == "true" ]]; then
    # Escape quotes for JSON
    local n="${name//\"/\\\"}" d="${detail//\"/\\\"}" f="${fix//\"/\\\"}"
    JSON_ROWS+=("{\"status\":\"$status\",\"check\":\"$n\",\"detail\":\"$d\",\"fix\":\"$f\"}")
  elif [[ "$QUIET" != "true" ]]; then
    printf "  %s %s${C_DIM} — %s${C_RESET}\n" "$icon" "$name" "$detail"
    if [[ "$status" != "pass" && -n "$fix" ]]; then
      printf "      ${C_BLUE}fix:${C_RESET} %s\n" "$fix"
    fi
  fi
}

# --- helpers ---------------------------------------------------------------

repo_slug() {
  # Resolve owner/repo from git remote. Falls back to "" if not a github remote.
  local url
  url="$(git config --get remote.origin.url 2>/dev/null || echo "")"
  if [[ "$url" == git@github.com:* ]]; then
    echo "${url#git@github.com:}" | sed 's/\.git$//'
  elif [[ "$url" == https://github.com/* ]]; then
    echo "${url#https://github.com/}" | sed 's/\.git$//'
  else
    echo ""
  fi
}

REPO_SLUG="$(repo_slug)"

GH_READY=false
if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then GH_READY=true; fi

# One `gh secret list` for the whole run (names only, never values).
SECRET_NAMES=""
SECRETS_READ=false
if [[ "$GH_READY" == "true" && -n "$REPO_SLUG" ]]; then
  if SECRET_NAMES="$(gh secret list -R "$REPO_SLUG" --json name --jq '.[].name' 2>/dev/null)"; then
    SECRETS_READ=true
  fi
fi

has_secret() {
  # has_secret <NAME> - true if the secret is configured on the remote repo.
  # Returns 2 if the secret list could not be read (caller decides).
  [[ "$SECRETS_READ" == "true" ]] || return 2
  grep -qx "$1" <<<"$SECRET_NAMES"
}

# --- header ----------------------------------------------------------------

if [[ "$JSON" != "true" && "$QUIET" != "true" ]]; then
  printf "\n${C_BOLD}Aeon onboarding check${C_RESET}"
  if [[ -n "$REPO_SLUG" ]]; then
    printf " ${C_DIM}— %s${C_RESET}" "$REPO_SLUG"
  fi
  printf "\n\n"
fi

# --- 0. this checkout is an instance, and gh targets it ------------------

case "$(printf '%s' "$REPO_SLUG" | tr '[:upper:]' '[:lower:]')" in
  "")
    emit fail "instance repo" "no GitHub remote (origin)" \
      "./aeon init  (creates your repo from the aeonfun/aeon template and points this folder at it), or set up in the browser: https://www.aeon.fun/connect"
    ;;
  aeonfun/aeon|aaronjmars/aeon)
    emit fail "instance repo" "origin is $REPO_SLUG, the shared template - not your own instance" \
      "./aeon init  (creates your repo from the template; secrets and runs then land on yours), or set up in the browser: https://www.aeon.fun/connect"
    ;;
  *)
    emit pass "instance repo" "$REPO_SLUG" ""
    if [[ "$GH_READY" == "true" ]]; then
      gh_default="$(gh repo set-default --view 2>/dev/null || true)"
      [[ "$gh_default" == "no default"* ]] && gh_default=""
      if [[ "$(printf '%s' "$gh_default" | tr '[:upper:]' '[:lower:]')" == "$(printf '%s' "$REPO_SLUG" | tr '[:upper:]' '[:lower:]')" ]]; then
        emit pass "gh default repo" "$gh_default" ""
      else
        emit warn "gh default repo" "${gh_default:-not set} (secrets set from the dashboard/CLI may go elsewhere)" \
          "gh repo set-default $REPO_SLUG"
      fi
    fi
    ;;
esac

# --- 1. workflow files exist ----------------------------------------------

for wf in aeon.yml messages.yml chain-runner.yml; do
  if [[ -f ".github/workflows/$wf" ]]; then
    emit pass "workflow .github/workflows/$wf" "present" ""
  else
    emit fail "workflow .github/workflows/$wf" "missing" \
      "Re-sync from upstream: git remote add upstream https://github.com/aeonfun/aeon.git && git fetch upstream && git checkout upstream/main -- .github/workflows/$wf"
  fi
done

# --- 2. aeon.yml structure -------------------------------------------------

if [[ -f aeon.yml ]]; then
  enabled_count=$(grep -cE '^\s*[a-z][a-z0-9_-]+: \{ *enabled: true' aeon.yml || true)
  enabled_count=${enabled_count//[!0-9]/}
  : "${enabled_count:=0}"
  if [[ "$enabled_count" -ge 1 ]]; then
    emit pass "aeon.yml" "$enabled_count skill(s) enabled" ""
  else
    emit warn "aeon.yml" "no skills enabled" \
      "Open aeon.yml and flip 'enabled: false' → 'enabled: true' on at least one skill (heartbeat is enabled by default in the template)."
  fi
else
  emit fail "aeon.yml" "missing" \
    "Restore from upstream: git checkout upstream/main -- aeon.yml"
fi

# --- STRATEGY.md (north-star imported into every run) ----------------------

if [[ -f STRATEGY.md ]]; then
  if grep -q '^> \*\*Status:\*\* unconfigured defaults' STRATEGY.md; then
    emit warn "STRATEGY.md" "present but still using template defaults" \
      "Edit STRATEGY.md — set your north-star metric, priorities, audience, and constraints. Every skill reads it (it's imported into CLAUDE.md), so tailoring it steers all output."
  else
    emit pass "STRATEGY.md" "customized" ""
  fi
else
  emit warn "STRATEGY.md" "missing (skills run without a shared strategy)" \
    "Restore the template: git checkout upstream/main -- STRATEGY.md  then edit it for your goal."
fi

# --- 3. memory writable ----------------------------------------------------

if [[ ! -d memory ]]; then
  emit fail "memory/" "directory missing" \
    "mkdir -p memory/{logs,topics,issues} && touch memory/MEMORY.md"
elif ! ( touch memory/.onboard-write-test 2>/dev/null && rm memory/.onboard-write-test 2>/dev/null ); then
  emit fail "memory/ writable" "permission denied" \
    "chmod -R u+w memory/"
else
  if [[ -f memory/MEMORY.md ]]; then
    emit pass "memory/" "writable, MEMORY.md present" ""
  else
    emit warn "memory/" "writable but MEMORY.md missing" \
      "Create with: printf '# Long-term Memory\\n' > memory/MEMORY.md"
  fi
fi

# --- 4. model credential (from the credential manifest) -------------------
# Which secrets can run the configured harness comes from the generated
# manifest (harness-adapter/harnesses.json `credentials`, most preferred first,
# mirroring scripts/resolve-harness.sh) plus, for claude, every gateway key in
# harness-adapter/gateways.json - so a gateway-only setup (OpenRouter, Bankr, ...)
# passes instead of being told it has no Anthropic key.

HARNESS="$(grep -E '^harness:' aeon.yml 2>/dev/null | head -1 | sed 's/^harness:[[:space:]]*//; s/[[:space:]]*#.*$//' | tr -d "\"' " || true)"
HARNESS="${HARNESS:-claude}"
MANIFEST="harness-adapter/harnesses.json"
GATEWAYS="harness-adapter/gateways.json"

if ! command -v jq >/dev/null 2>&1 || [[ ! -f "$MANIFEST" ]]; then
  emit warn "model credential" "cannot read $MANIFEST (needs jq)" \
    "Install jq, or check Settings -> Secrets and variables -> Actions for the $HARNESS credential (docs/harnesses.md lists them)."
else
  # resolve-harness.sh runs an unknown harness name as claude, so check claude.
  if ! jq -e --arg h "$HARNESS" '.harnesses[] | select(.id == $h)' "$MANIFEST" >/dev/null 2>&1; then
    emit warn "harness" "aeon.yml harness '$HARNESS' is not a known harness, so runs fall back to claude" \
      "Set harness: to one of $(jq -r '[.harnesses[].id] | join(", ")' "$MANIFEST") (./aeon config set harness <name>)."
    HARNESS=claude
  fi
  # "<SECRET>\t<label>" for every credential that can run this harness, in order.
  # A manifest from before `credentials` existed falls back to its auth summary.
  cand="$(jq -r --arg h "$HARNESS" '.harnesses[] | select(.id == $h) | . as $x
    | ($x.credentials // (([($x.auth.native_oauth // [])[], ($x.auth.native_key // [])[]] | map({secret: ., label: .}))
        + (if $x.auth.openrouter then [{secret: "OPENROUTER_API_KEY", label: "OpenRouter"}] else [] end)))[]
    | "\(.secret)\t\(.label)"' "$MANIFEST")"
  if [[ -n "$(jq -r --arg h "$HARNESS" '.harnesses[] | select(.id == $h) | .gateways // empty' "$MANIFEST")" && -f "$GATEWAYS" ]]; then
    cand="$cand"$'\n'"$(jq -r '.gateways[] | select(.transport != "native") | .label as $l | .secrets[] | "\(.)\t\($l) gateway"' "$GATEWAYS")"
  fi
  if [[ "$SECRETS_READ" != "true" ]]; then
    emit warn "model credential" "could not verify ($HARNESS; gh cannot read $REPO_SLUG secrets)" \
      "gh auth login, then rerun bin/onboard. Any one of these works: $(cut -f1 <<<"$cand" | tr '\n' ' ')"
  else
    found=""
    while IFS=$'\t' read -r secret label; do
      [[ -n "$secret" ]] || continue
      if has_secret "$secret"; then found="$secret ($label)"; break; fi
    done <<<"$cand"
    if [[ -n "$found" ]]; then
      emit pass "model credential" "$HARNESS via $found" ""
    else
      first_cmd="$(jq -r --arg h "$HARNESS" '[.harnesses[] | select(.id == $h) | (.credentials // [])[] | select(.aeon_cmd != null)][0]
        | if . == null then "./aeon auth --harness \($h)" else "\(.aeon_cmd)  (get one: \(.get_url))" end' "$MANIFEST")"
      or_hint=""
      if grep -q "^OPENROUTER_API_KEY"$'\t' <<<"$cand"; then
        or_hint="  One OpenRouter key also works: ./aeon secrets set OPENROUTER_API_KEY --stdin"
      fi
      emit fail "model credential" "no credential for the $HARNESS harness is set" \
        "./aeon init  (guided), or: $first_cmd.$or_hint"
    fi
  fi
fi

# --- 5. at least one notification channel ---------------------------------

channels_found=()
channels_unverified=false

check_channel() {
  local label="$1"; shift
  local all_present=true
  local any_unverified=false
  for s in "$@"; do
    if has_secret "$s"; then
      :
    else
      case "$?" in
        1) all_present=false ;;
        *) any_unverified=true ;;
      esac
    fi
  done
  if [[ "$all_present" == "true" && "$any_unverified" == "false" ]]; then
    channels_found+=("$label")
  elif [[ "$any_unverified" == "true" && "$all_present" == "true" ]]; then
    channels_unverified=true
  fi
}

check_channel "Telegram" TELEGRAM_BOT_TOKEN TELEGRAM_CHAT_ID
check_channel "Discord"  DISCORD_WEBHOOK_URL
check_channel "Slack"    SLACK_WEBHOOK_URL
check_channel "Email"    RESEND_API_KEY NOTIFY_EMAIL_TO

if [[ ${#channels_found[@]} -ge 1 ]]; then
  joined=$(printf "%s, " "${channels_found[@]}")
  joined=${joined%, }
  emit pass "notification channel" "$joined configured" ""
elif [[ "$channels_unverified" == "true" ]]; then
  emit warn "notification channel" "could not verify (gh not authenticated)" \
    "Authenticate with 'gh auth login' or check Settings → Secrets for one of: TELEGRAM_BOT_TOKEN+TELEGRAM_CHAT_ID, DISCORD_WEBHOOK_URL, SLACK_WEBHOOK_URL, RESEND_API_KEY+NOTIFY_EMAIL_TO."
else
  emit fail "notification channel" "no channel configured" \
    "Pick one - Telegram is fastest: ./aeon init walks you through the @BotFather token and links your chat. Or set TELEGRAM_BOT_TOKEN + TELEGRAM_CHAT_ID with ./aeon secrets set <NAME> --stdin"
fi

# --- 6. GitHub Actions has actually run -----------------------------------

if [[ -n "$REPO_SLUG" && "$GH_READY" == "true" ]]; then
  run_count=$(gh run list -R "$REPO_SLUG" --workflow=messages.yml --limit 1 --json status --jq 'length' 2>/dev/null || echo "0")
  if [[ "${run_count:-0}" -ge 1 ]]; then
    emit pass "GitHub Actions" "messages.yml has run at least once" ""
  else
    emit warn "GitHub Actions" "no runs yet for messages.yml" \
      "./aeon init (or Aeon Connect) enables Actions for you. Forks start with Actions off: open https://github.com/$REPO_SLUG/actions and enable workflows (or Settings -> Actions -> General)."
  fi
else
  emit warn "GitHub Actions" "could not query run history" \
    "Run 'gh auth login' and rerun bin/onboard to verify."
fi

# --- 6b. Actions may open & auto-merge PRs --------------------------------
# install-skill ships each install as an auto-merged PR, which the in-Actions
# GITHUB_TOKEN can only do when this repo setting is on. It defaults off on a
# fresh fork and does not inherit — so a fork that never clicked the dashboard
# "Install" button (which sets it automatically) would strand installs on a
# branch. Surface it here for the CLI/cron path.

if [[ -n "$REPO_SLUG" && "$GH_READY" == "true" ]]; then
  pr_perm=$(gh api "repos/$REPO_SLUG/actions/permissions/workflow" --jq '.can_approve_pull_request_reviews' 2>/dev/null || echo "unknown")
  if [[ "$pr_perm" == "true" ]]; then
    emit pass "Actions can open PRs" "create-and-approve-PRs enabled" ""
  elif [[ "$pr_perm" == "false" ]]; then
    emit warn "Actions can open PRs" "disabled — install-skill can't open/merge its PR, so installs strand on a branch" \
      "./aeon init turns it on (keeping your default token permission), or tick Settings -> Actions -> General -> Allow GitHub Actions to create and approve pull requests."
  else
    emit warn "Actions can open PRs" "could not query the setting" \
      "Check Settings → Actions → General → Workflow permissions → 'Allow GitHub Actions to create and approve pull requests'."
  fi
fi

# --- 7. local memory log evidence -----------------------------------------

if compgen -G "memory/logs/*.md" > /dev/null 2>&1; then
  log_count=$(find memory/logs -maxdepth 1 -name "*.md" -type f 2>/dev/null | wc -l | tr -d ' ')
  emit pass "skill activity log" "$log_count daily log file(s) under memory/logs/" ""
else
  emit warn "skill activity log" "no entries under memory/logs/ yet" \
    "Logs appear after the first scheduled skill run. Trigger one manually: gh workflow run aeon.yml -f skill=heartbeat -R $REPO_SLUG"
fi

# --- 8. optional cross-repo PAT -------------------------------------------

if has_secret GH_GLOBAL; then
  emit pass "GH_GLOBAL (cross-repo PAT)" "configured" ""
else
  case "$?" in
    1)
      emit warn "GH_GLOBAL (cross-repo PAT)" "not configured (optional)" \
        "Needed for skills that reach other repos (github-monitor, pr-review, feature) and to save refreshed logins (grok, MCP OAuth). ./aeon auth --github copies your gh token (needs scopes repo + workflow), or add a classic PAT with repo + workflow: ./aeon secrets set GH_GLOBAL --stdin"
      ;;
    *)
      :  # gh unavailable — skip silently to avoid noise
      ;;
  esac
fi

# --- summary ---------------------------------------------------------------

if [[ "$JSON" == "true" ]]; then
  printf '{"summary":{"pass":%d,"warn":%d,"fail":%d},"checks":[' "$PASS_COUNT" "$WARN_COUNT" "$FAIL_COUNT"
  first=true
  for row in "${JSON_ROWS[@]}"; do
    if [[ "$first" == "true" ]]; then first=false; else printf ','; fi
    printf '%s' "$row"
  done
  printf ']}\n'
else
  printf "\n${C_BOLD}Summary${C_RESET}: ${C_GREEN}%d pass${C_RESET}, ${C_YELLOW}%d warn${C_RESET}, ${C_RED}%d fail${C_RESET}\n" \
    "$PASS_COUNT" "$WARN_COUNT" "$FAIL_COUNT"
  if [[ "$FAIL_COUNT" -eq 0 && "$WARN_COUNT" -eq 0 ]]; then
    printf "\nAll set. Aeon should run on its next cron tick.\n\n"
  elif [[ "$FAIL_COUNT" -eq 0 ]]; then
    printf "\nAeon will run, but some optional pieces are missing (warnings above).\n\n"
  else
    printf "\nFix the ${C_RED}✗${C_RESET} items above, then rerun ${C_BOLD}bin/onboard${C_RESET}.\n\n"
  fi
fi

# --- optional remote dispatch ---------------------------------------------

if [[ "$REMOTE" == "true" ]]; then
  if [[ -z "$REPO_SLUG" ]]; then
    echo "Cannot dispatch remote check — no GitHub remote configured." >&2
    exit 1
  fi
  if ! command -v gh >/dev/null 2>&1; then
    echo "Cannot dispatch remote check — gh CLI not installed." >&2
    exit 1
  fi
  case "$(printf '%s' "$REPO_SLUG" | tr '[:upper:]' '[:lower:]')" in
    aeonfun/aeon|aaronjmars/aeon)
      echo "Refusing to dispatch on $REPO_SLUG, the shared template. Run ./aeon init first (or set up in the browser: https://www.aeon.fun/connect)." >&2
      exit 1 ;;
  esac
  # heartbeat is the template's always-on health skill: one run exercises
  # Actions, the model credential and notifications end to end.
  echo "Dispatching a heartbeat run on $REPO_SLUG ..."
  if gh workflow run aeon.yml -R "$REPO_SLUG" -f skill=heartbeat >/dev/null 2>&1; then
    echo "Dispatched. Watch it with: gh run list -R $REPO_SLUG --workflow=aeon.yml --limit 1"
    echo "A green run means Actions and the model credential work. heartbeat commits docs/status.md and"
    echo "only notifies when it finds something, so no message is normal on a fresh instance."
  else
    echo "Failed to dispatch. Verify Actions are enabled and that aeon.yml workflow exists." >&2
    exit 1
  fi
fi

# Exit code: 0 if no failures, 1 otherwise.
if [[ "$FAIL_COUNT" -gt 0 ]]; then
  exit 1
fi
exit 0
