#!/bin/bash
# BlackRoad OS End-to-End Test Runner
# One command to rule them all — validates the entire stack
# Usage: ./blackroad-e2e-test.sh

set -o pipefail

# ── BlackRoad Brand Colors ──────────────────────────────────────────
PINK='\033[38;5;205m'
AMBER='\033[38;5;214m'
BLUE='\033[38;5;69m'
VIOLET='\033[38;5;135m'
GREEN='\033[38;5;82m'
RED='\033[38;5;196m'
DIM='\033[2m'
BOLD='\033[1m'
RESET='\033[0m'

# ── Test Counters ───────────────────────────────────────────────────
PASS_COUNT=0
FAIL_COUNT=0
TOTAL_COUNT=0
FAILURES=()
START_TIME=$(date +%s)
TEST_TIMEOUT=35

# ── Timeout Command (macOS compat) ─────────────────────────────────
if command -v gtimeout &>/dev/null; then
  TIMEOUT_CMD="gtimeout"
elif command -v timeout &>/dev/null; then
  TIMEOUT_CMD="timeout"
else
  # Pure-bash fallback using background process
  TIMEOUT_CMD=""
fi

# ── Pi Definitions ──────────────────────────────────────────────────
declare -A PI_IPS=(
  [Alice]="192.168.4.49"
  [Cecilia]="192.168.4.96"
  [Octavia]="192.168.4.101"
  [Aria]="192.168.4.98"
  [Lucidia]="192.168.4.38"
)

declare -A PI_SSH_USERS=(
  [Alice]="pi"
  [Cecilia]="blackroad"
  [Octavia]="pi"
  [Aria]="blackroad"
  [Lucidia]="octavia"
)

# ── Cloudflare Domains (blackroad.io) ────────────────────────────────
CF_DOMAINS=(
  "blackroad.io"
  "auth.blackroad.io"
  "chat.blackroad.io"
  "portal.blackroad.io"
  "index.blackroad.io"
  "images.blackroad.io"
  "resume.blackroad.io"
  "api.blackroad.io"
  "status.blackroad.io"
  "app.blackroad.io"
  "docs.blackroad.io"
  "git.blackroad.io"
  "ollama.blackroad.io"
)

# ── Service Port Map (Pi → port list) ──────────────────────────────
# Alice: Pi-hole(80,53), PostgreSQL(5432)
# Cecilia: Ollama(11434), MinIO(9000,9001), PostgreSQL(5432), CECE API(8787)
# Octavia: OctoPrint(5000), InfluxDB(8086), GitHub Runner
# Aria: Ollama(11434), InfluxDB(8086), Headscale(8088), Portainer(9443)
declare -A PI_SERVICES
PI_SERVICES[Alice]="Pi-hole:80 DNS:53 PostgreSQL:5432"
PI_SERVICES[Cecilia]="Ollama:11434 MinIO-API:9000 MinIO-Console:9001 PostgreSQL:5432"
PI_SERVICES[Octavia]="Gitea:3100 NATS:4222 Ollama:11434"
PI_SERVICES[Aria]="Ollama:11434 InfluxDB:8086 Headscale:8088 Portainer:9443"
PI_SERVICES[Lucidia]="SSH:22 DNS:53"

# ── Helpers ─────────────────────────────────────────────────────────

header() {
  echo ""
  echo -e "${PINK}${BOLD}╔══════════════════════════════════════════════════════════════╗${RESET}"
  echo -e "${PINK}${BOLD}║${RESET}  ${AMBER}${BOLD}B L A C K R O A D   O S${RESET}   ${VIOLET}End-to-End Test Runner${RESET}         ${PINK}${BOLD}║${RESET}"
  echo -e "${PINK}${BOLD}╚══════════════════════════════════════════════════════════════╝${RESET}"
  echo -e "${DIM}  Host: $(hostname) | $(date '+%Y-%m-%d %H:%M:%S %Z') | PID $$${RESET}"
  echo ""
}

section() {
  echo ""
  echo -e "${BLUE}${BOLD}── $1 ──────────────────────────────────────────${RESET}"
}

_run_with_timeout() {
  local secs="$1"
  shift
  if [[ -n "$TIMEOUT_CMD" ]]; then
    "$TIMEOUT_CMD" "$secs" bash -c "$*" 2>&1
  else
    # Pure-bash timeout fallback for macOS without coreutils
    local output_file
    output_file=$(mktemp)
    bash -c "$*" >"$output_file" 2>&1 &
    local pid=$!
    local i=0
    while kill -0 "$pid" 2>/dev/null; do
      if [[ $i -ge $secs ]]; then
        kill -9 "$pid" 2>/dev/null
        wait "$pid" 2>/dev/null
        cat "$output_file"
        rm -f "$output_file"
        return 124
      fi
      sleep 1
      i=$((i + 1))
    done
    wait "$pid"
    local rc=$?
    cat "$output_file"
    rm -f "$output_file"
    return $rc
  fi
}

run_test() {
  local name="$1"
  shift
  TOTAL_COUNT=$((TOTAL_COUNT + 1))
  local output
  if output=$(_run_with_timeout "$TEST_TIMEOUT" "$*"); then
    PASS_COUNT=$((PASS_COUNT + 1))
    echo -e "  ${GREEN}[PASS]${RESET} ${name}"
    if [[ -n "$output" && "$VERBOSE" == "1" ]]; then
      echo -e "         ${DIM}${output}${RESET}"
    fi
    return 0
  else
    local rc=$?
    FAIL_COUNT=$((FAIL_COUNT + 1))
    FAILURES+=("$name")
    if [[ $rc -eq 124 ]]; then
      echo -e "  ${RED}[FAIL]${RESET} ${name} ${DIM}(timeout after ${TEST_TIMEOUT}s)${RESET}"
    else
      echo -e "  ${RED}[FAIL]${RESET} ${name}"
    fi
    if [[ -n "$output" ]]; then
      echo -e "         ${DIM}${output:0:200}${RESET}"
    fi
    return 1
  fi
}

# ── HEADER ──────────────────────────────────────────────────────────
header

# ════════════════════════════════════════════════════════════════════
# 1. PI MESH — ping, SSH, uptime
# ════════════════════════════════════════════════════════════════════
section "1. Pi Mesh Connectivity"

for pi in Alice Cecilia Octavia Aria Lucidia; do
  ip="${PI_IPS[$pi]}"
  user="${PI_SSH_USERS[$pi]}"

  run_test "$pi ping ($ip)" \
    "ping -c1 -W3 '$ip' >/dev/null 2>&1"

  run_test "$pi SSH" \
    "ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o BatchMode=yes '$user@$ip' 'echo ok' >/dev/null 2>&1"

  run_test "$pi uptime" \
    "ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o BatchMode=yes '$user@$ip' 'uptime' 2>/dev/null | head -1"
done

# ════════════════════════════════════════════════════════════════════
# 2. SERVICES — check ports on each Pi via SSH
# ════════════════════════════════════════════════════════════════════
section "2. Service Port Checks (via SSH)"

for pi in Alice Cecilia Octavia Aria Lucidia; do
  ip="${PI_IPS[$pi]}"
  user="${PI_SSH_USERS[$pi]}"
  services="${PI_SERVICES[$pi]}"
  for svc in $services; do
    svc_name="${svc%%:*}"
    port="${svc##*:}"
    run_test "$pi → $svc_name (:$port)" \
      "ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o BatchMode=yes '$user@$ip' 'ss -tln | grep -q :$port' 2>/dev/null"
  done
done

# ════════════════════════════════════════════════════════════════════
# 3. CLOUDFLARE — curl top 10 domains
# ════════════════════════════════════════════════════════════════════
section "3. Cloudflare Domains"

for domain in "${CF_DOMAINS[@]}"; do
  run_test "https://$domain" \
    "code=\$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 'https://$domain'); [[ \"\$code\" =~ ^(200|301|302|303|307|308|401|403|404)$ ]]"
done

# ════════════════════════════════════════════════════════════════════
# 4. API GATEWAY — health, agents, tools
# ════════════════════════════════════════════════════════════════════
section "4. API Gateway"

API_BASE="https://api.blackroad.io"

run_test "GET $API_BASE/healthz" \
  "code=\$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 '$API_BASE/healthz'); [[ \"\$code\" =~ ^(200|204)$ ]]"

run_test "GET $API_BASE/v1/models" \
  "code=\$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 '$API_BASE/v1/models'); [[ \"\$code\" =~ ^(200|401|403)$ ]]"

run_test "GET $API_BASE/v1/providers" \
  "code=\$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 '$API_BASE/v1/providers'); [[ \"\$code\" =~ ^(200|401|403)$ ]]"

# ════════════════════════════════════════════════════════════════════
# 5. MEMORY SYSTEM — log, verify, search (local CLI)
# ════════════════════════════════════════════════════════════════════
section "5. Memory System"

run_test "Memory: log entry" \
  "bash ~/memory-system.sh log 'e2e-test' 'Automated e2e test run' 2>&1"

run_test "Memory: verify" \
  "bash ~/memory-system.sh verify 2>&1"

run_test "Memory: search" \
  "bash ~/memory-indexer.sh search 'e2e-test' 2>&1"

# ════════════════════════════════════════════════════════════════════
# 6. TASK MARKETPLACE — post, claim, complete (local CLI)
# ════════════════════════════════════════════════════════════════════
section "6. Task Marketplace"

TASK_ID="e2e-$(date +%s)"

run_test "Task: post" \
  "bash ~/memory-task-marketplace.sh post '$TASK_ID' 'E2E Test' 'Automated test' high e2e 2>&1"

run_test "Task: claim" \
  "bash ~/memory-task-marketplace.sh claim '$TASK_ID' 2>&1"

run_test "Task: complete" \
  "bash ~/memory-task-marketplace.sh complete '$TASK_ID' 2>&1"

# ════════════════════════════════════════════════════════════════════
# 7. NATS — pub/sub round trip
# ════════════════════════════════════════════════════════════════════
section "7. NATS Messaging"

NATS_HOST="${PI_IPS[Octavia]}"
NATS_USER="${PI_SSH_USERS[Octavia]}"

run_test "NATS: port reachable (via SSH)" \
  "ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o BatchMode=yes $NATS_USER@$NATS_HOST 'ss -tln | grep -q :4222' 2>/dev/null"

if command -v nats &>/dev/null; then
  NATS_SUBJ="e2e.test.$(date +%s)"
  run_test "NATS: pub/sub round trip" \
    "nats pub '$NATS_SUBJ' 'hello from e2e' --server='nats://$NATS_HOST:4222' --timeout=5s 2>/dev/null"
else
  run_test "NATS: pub/sub (nats CLI not installed)" \
    "false"
fi

# ════════════════════════════════════════════════════════════════════
# 8. OLLAMA on Cecilia — models + inference (via SSH)
# ════════════════════════════════════════════════════════════════════
section "8. Ollama (Cecilia — via SSH)"

CECILIA_IP="${PI_IPS[Cecilia]}"

run_test "Ollama: API reachable (via SSH)" \
  "ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o BatchMode=yes blackroad@$CECILIA_IP 'curl -s -o /dev/null -w \"%{http_code}\" http://localhost:11434/ 2>/dev/null' | grep -q 200"

run_test "Ollama: list models (via SSH)" \
  "count=\$(ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o BatchMode=yes blackroad@$CECILIA_IP 'curl -s http://localhost:11434/api/tags' 2>/dev/null | python3 -c \"import sys,json; d=json.load(sys.stdin); print(len(d.get('models',[])))\" 2>/dev/null); [[ \$count -gt 0 ]]"

OLD_TIMEOUT=$TEST_TIMEOUT
TEST_TIMEOUT=60
run_test "Ollama: quick inference (tinyllama)" \
  "resp=\$(ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o BatchMode=yes blackroad@$CECILIA_IP 'curl -s --max-time 50 -X POST http://localhost:11434/api/generate -d \"{\\\"model\\\":\\\"tinyllama:latest\\\",\\\"prompt\\\":\\\"hi\\\",\\\"stream\\\":false}\" 2>/dev/null' | python3 -c \"import sys,json; d=json.load(sys.stdin); print(d.get('response',''))\" 2>/dev/null); [[ -n \"\$resp\" ]]"
TEST_TIMEOUT=$OLD_TIMEOUT

# ════════════════════════════════════════════════════════════════════
# 9. GIT SYNC — Gitea API (via SSH to Octavia)
# ════════════════════════════════════════════════════════════════════
section "9. Git Sync (Gitea — via SSH)"

OCTAVIA_IP="${PI_IPS[Octavia]}"
OCTAVIA_USER="${PI_SSH_USERS[Octavia]}"

run_test "Gitea: API reachable (via SSH)" \
  "code=\$(ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o BatchMode=yes $OCTAVIA_USER@$OCTAVIA_IP 'curl -s -o /dev/null -w \"%{http_code}\" http://localhost:3100/api/v1/repos/search?limit=1' 2>/dev/null); [[ \"\$code\" =~ ^(200|401|403)$ ]]"

run_test "Gitea: repo count > 0 (via SSH)" \
  "code=\$(ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no -o BatchMode=yes $OCTAVIA_USER@$OCTAVIA_IP 'curl -s http://localhost:3100/api/v1/repos/search?limit=1 -o /dev/null -w \"%{http_code}\"' 2>/dev/null); [[ \"\$code\" == \"200\" ]]"

# ════════════════════════════════════════════════════════════════════
# 10. LOCAL BUILDS — key projects type-check
# ════════════════════════════════════════════════════════════════════
section "10. Local Builds"

LOCAL_PROJECTS=(
  "$HOME/blackroad-web"
  "$HOME/blackroad-operator"
  "$HOME/blackroad-os-kpis"
)

for proj in "${LOCAL_PROJECTS[@]}"; do
  proj_name=$(basename "$proj")
  if [[ -d "$proj" ]]; then
    run_test "$proj_name: exists" "true"

    # Only check node_modules if package.json has dependencies
    if grep -q '"dependencies"' "$proj/package.json" 2>/dev/null || grep -q '"devDependencies"' "$proj/package.json" 2>/dev/null; then
      run_test "$proj_name: node_modules" \
        "[[ -d '$proj/node_modules' ]]"
    fi

    if [[ -f "$proj/tsconfig.json" ]]; then
      run_test "$proj_name: tsc --noEmit" \
        "cd '$proj' && npx tsc --noEmit 2>&1 | tail -5"
    fi
  else
    run_test "$proj_name: exists" "false"
  fi
done

# ════════════════════════════════════════════════════════════════════
# 11. STRIPE — auth + products
# ════════════════════════════════════════════════════════════════════
section "11. Stripe"

if command -v stripe &>/dev/null; then
  run_test "Stripe CLI: auth check" \
    "stripe config --list 2>/dev/null | grep -q 'test_mode'"

  run_test "Stripe: list products" \
    "stripe products list --limit=1 2>/dev/null | grep -q 'id'"
else
  if [[ -n "$STRIPE_SECRET_KEY" ]]; then
    run_test "Stripe API: auth" \
      "code=\$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 \
        -u \"\$STRIPE_SECRET_KEY:\" 'https://api.stripe.com/v1/balance'); \
      [[ \"\$code\" == '200' ]]"

    run_test "Stripe API: list products" \
      "code=\$(curl -s -o /dev/null -w '%{http_code}' --max-time 10 \
        -u \"\$STRIPE_SECRET_KEY:\" 'https://api.stripe.com/v1/products?limit=1'); \
      [[ \"\$code\" == '200' ]]"
  else
    run_test "Stripe: CLI or STRIPE_SECRET_KEY available" "false"
  fi
fi

# ════════════════════════════════════════════════════════════════════
# 12. RCLONE — remotes + backup dir
# ════════════════════════════════════════════════════════════════════
section "12. rclone"

if command -v rclone &>/dev/null; then
  run_test "rclone: installed" "true"

  run_test "rclone: has remotes" \
    "count=\$(rclone listremotes 2>/dev/null | wc -l); [[ \$count -gt 0 ]]"

  run_test "rclone: verify backup dir" \
    "rclone listremotes 2>/dev/null | head -1 | while read remote; do \
      rclone lsd \"\${remote}\" --max-depth 1 2>/dev/null | head -1; \
    done | grep -q '.'"
else
  run_test "rclone: installed" "false"
fi

# ════════════════════════════════════════════════════════════════════
# SUMMARY
# ════════════════════════════════════════════════════════════════════
END_TIME=$(date +%s)
ELAPSED=$((END_TIME - START_TIME))
MINUTES=$((ELAPSED / 60))
SECS=$((ELAPSED % 60))

echo ""
echo -e "${PINK}${BOLD}══════════════════════════════════════════════════════════════${RESET}"
echo -e "${AMBER}${BOLD}  RESULTS${RESET}"
echo -e "${PINK}${BOLD}══════════════════════════════════════════════════════════════${RESET}"
echo ""

if [[ $FAIL_COUNT -eq 0 ]]; then
  echo -e "  ${GREEN}${BOLD}ALL TESTS PASSED${RESET}  ${GREEN}${PASS_COUNT}/${TOTAL_COUNT}${RESET}"
else
  echo -e "  ${AMBER}${BOLD}${PASS_COUNT}/${TOTAL_COUNT} tests passed${RESET}  ${RED}(${FAIL_COUNT} failed)${RESET}"
  echo ""
  echo -e "  ${RED}${BOLD}Failures:${RESET}"
  for f in "${FAILURES[@]}"; do
    echo -e "    ${RED}x${RESET} $f"
  done
fi

echo ""
echo -e "  ${DIM}Completed in ${MINUTES}m ${SECS}s${RESET}"
echo ""

exit $FAIL_COUNT
