#!/bin/bash
# Neuron Telegram Gateway
# Polls Telegram for new messages, forwards to the soul at localhost:7770, sends responses back.
# Supports plain text chat + commands: /memory, /remember, /status
#
# Token resolution order:
#   1. $TELEGRAM_BOT_TOKEN env var
#   2. macOS Keychain: security find-generic-password -s neuron-telegram-bot -a neuron -w

set -euo pipefail

TOKEN="${TELEGRAM_BOT_TOKEN:-$(security find-generic-password -s neuron-telegram-bot -a neuron -w 2>/dev/null || true)}"
SOUL_URL="http://localhost:7770"
OFFSET=0
POLL_TIMEOUT=30

if [[ -z "$TOKEN" ]]; then
  echo "ERROR: No Telegram bot token. Set TELEGRAM_BOT_TOKEN or store in keychain." >&2
  echo "See: ~/Development/neuron-technologies/neuron/docs/telegram-bot-setup.md" >&2
  exit 1
fi

TG="https://api.telegram.org/bot${TOKEN}"

log() { echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"; }

# Send a Telegram message back to a chat
send_message() {
  local chat_id="$1"
  local text="$2"
  curl -s -X POST "${TG}/sendMessage" \
    -H "Content-Type: application/json" \
    -d "$(jq -n --argjson cid "$chat_id" --arg t "$text" \
      '{chat_id: $cid, text: $t, parse_mode: "Markdown"}')" \
    > /dev/null
}

# Store a memory in the soul
store_memory() {
  local content="$1"
  local label="${2:-telegram:conversation}"
  curl -s -X POST "${SOUL_URL}/api/neuron/memory" \
    -H "Content-Type: application/json" \
    -d "$(jq -n --arg c "$content" --arg l "$label" \
      '{content: $c, label: $l}')" \
    > /dev/null
}

# Chat with the soul; echoes the response text
soul_chat() {
  local message="$1"
  local from="${2:-unknown}"
  local response
  response=$(curl -s -X POST "${SOUL_URL}/api/chat" \
    -H "Content-Type: application/json" \
    -d "$(jq -n --arg m "$message" --arg f "$from" \
      '{message: $m, from: $f}')" 2>/dev/null)
  # Extract .response — fall back to raw body on parse failure
  jq -r '.response // empty' <<< "$response" 2>/dev/null || echo "$response"
}

# Search soul memories; echoes formatted results
soul_recall() {
  local query="$1"
  local limit="${2:-3}"
  local raw
  raw=$(curl -s -X POST "${SOUL_URL}/api/neuron/recall" \
    -H "Content-Type: application/json" \
    -d "$(jq -n --arg q "$query" --argjson l "$limit" \
      '{query: $q, limit: $l}')" 2>/dev/null)
  # Format top results as a numbered list (truncate long nodes to 300 chars)
  jq -r 'if type == "array" then
    to_entries | .[:3] | map(
      (.index + 1 | tostring) + ". " + (.value.content | .[0:300] | gsub("\n";" "))
    ) | join("\n\n")
  else
    "No results found."
  end' <<< "$raw" 2>/dev/null || echo "No results found."
}

# Check if soul is reachable
soul_health() {
  curl -s --max-time 3 "${SOUL_URL}/" > /dev/null 2>&1 && echo "up" || echo "down"
}

handle_update() {
  local update="$1"
  local chat_id msg_text from_name update_id

  update_id=$(jq -r '.update_id' <<< "$update")
  chat_id=$(jq -r '.message.chat.id // empty' <<< "$update")
  msg_text=$(jq -r '.message.text // empty' <<< "$update")
  from_name=$(jq -r '.message.from.first_name // "stranger"' <<< "$update")

  # Skip non-message updates (inline queries, etc.)
  if [[ -z "$chat_id" || -z "$msg_text" ]]; then
    OFFSET=$((update_id + 1))
    return
  fi

  log "[$update_id] from=$from_name chat=$chat_id text=${msg_text:0:60}"

  # Route by command prefix
  if [[ "$msg_text" == /status* ]]; then
    local health
    health=$(soul_health)
    if [[ "$health" == "up" ]]; then
      send_message "$chat_id" "Soul is *online* at ${SOUL_URL} ✓"
    else
      send_message "$chat_id" "Soul appears to be *offline* (${SOUL_URL} unreachable)."
    fi

  elif [[ "$msg_text" == /memory* ]]; then
    local query="${msg_text#/memory}"
    query="${query# }"
    if [[ -z "$query" ]]; then
      send_message "$chat_id" "Usage: /memory <query>"
    else
      local results
      results=$(soul_recall "$query" 3)
      if [[ -n "$results" ]]; then
        send_message "$chat_id" "*Memories matching \"${query}\":*

${results}"
      else
        send_message "$chat_id" "No memories found for \"${query}\"."
      fi
    fi

  elif [[ "$msg_text" == /remember* ]]; then
    local content="${msg_text#/remember}"
    content="${content# }"
    if [[ -z "$content" ]]; then
      send_message "$chat_id" "Usage: /remember <text to store>"
    else
      store_memory "Telegram (${from_name}): ${content}" "telegram:explicit"
      send_message "$chat_id" "Stored: _${content}_"
    fi

  else
    # Plain text — forward to soul chat
    local soul_response
    soul_response=$(soul_chat "$msg_text" "$from_name" 2>/dev/null || true)

    if [[ -z "$soul_response" ]]; then
      soul_response="Neuron is resting — try again in a moment."
    fi

    send_message "$chat_id" "$soul_response"

    # Capture conversation as a memory (fire-and-forget)
    store_memory "Telegram conversation with ${from_name}: [user] ${msg_text} [soul] ${soul_response}" \
      "telegram:conversation" &
  fi

  OFFSET=$((update_id + 1))
}

log "Neuron Telegram gateway starting (soul=${SOUL_URL}, poll_timeout=${POLL_TIMEOUT}s)"

while true; do
  # Long-poll for updates
  UPDATES=$(curl -s --max-time $((POLL_TIMEOUT + 5)) \
    "${TG}/getUpdates?offset=${OFFSET}&timeout=${POLL_TIMEOUT}" 2>/dev/null || true)

  if [[ -z "$UPDATES" ]]; then
    log "WARN: Empty response from Telegram; retrying in 5s"
    sleep 5
    continue
  fi

  OK=$(jq -r '.ok // false' <<< "$UPDATES" 2>/dev/null)
  if [[ "$OK" != "true" ]]; then
    DESC=$(jq -r '.description // "unknown error"' <<< "$UPDATES" 2>/dev/null)
    log "WARN: Telegram API error: ${DESC}; retrying in 10s"
    sleep 10
    continue
  fi

  # Iterate over each update
  COUNT=$(jq '.result | length' <<< "$UPDATES" 2>/dev/null || echo 0)
  if [[ "$COUNT" -gt 0 ]]; then
    for i in $(seq 0 $((COUNT - 1))); do
      update=$(jq ".result[$i]" <<< "$UPDATES")
      handle_update "$update"
    done
  fi

  # Avoid hammering the API if something is very wrong
  sleep 1
done
