#!/bin/bash
#
# codevibe - CodeVibe CLI
#
# Manages authentication, updates, and plugin lifecycle.
# Authentication is shared across all CodeVibe plugins (Claude, Codex, Gemini).
#
# Usage:
#   codevibe login              # Sign in via browser
#   codevibe logout             # Sign out
#   codevibe status             # Show auth status
#   codevibe update             # Update all CodeVibe packages + Claude plugin
#   codevibe version            # Show installed versions
#   codevibe reset-device       # Reset device identity (destructive)
#   codevibe --help             # Show this help
#
# Environment:
#   ENVIRONMENT                 # Set to 'production' (default) or 'development'
#

export ENVIRONMENT="${ENVIRONMENT:-production}"

# Colors
PURPLE='\033[0;35m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BOLD='\033[1m'
DIM='\033[2m'
NC='\033[0m'

# Resolve symlinks to find the actual script location
SOURCE="${BASH_SOURCE[0]}"
while [ -L "$SOURCE" ]; do
  DIR="$(cd "$(dirname "$SOURCE")" && pwd)"
  SOURCE="$(readlink "$SOURCE")"
  [[ "$SOURCE" != /* ]] && SOURCE="$DIR/$SOURCE"
done
SCRIPT_DIR="$(cd "$(dirname "$SOURCE")" && pwd)"
PACKAGE_DIR="$(dirname "$SCRIPT_DIR")"

# ─── Help ────────────────────────────────────────────────────────────

show_help() {
  echo ""
  echo -e "${BOLD}CodeVibe${NC} — Control AI coding agents from your phone"
  echo ""
  echo "Usage: codevibe <command>"
  echo ""
  echo "Commands:"
  echo "  login          Sign in with Apple or Google (opens browser)"
  echo "  logout         Sign out"
  echo "  status         Show authentication status"
  echo "  update         Update all CodeVibe packages and Claude plugin"
  echo "  version        Show installed package versions"
  echo "  reset-device   Reset device identity (destructive)"
  echo ""
  echo "Agent wrappers:"
  echo "  codevibe-claude    Start Claude Code with mobile sync"
  echo "  codevibe-gemini    Start Gemini CLI with mobile sync"
  echo "  codevibe-codex     Start Codex CLI with mobile sync"
  echo ""
  echo -e "${DIM}https://quantiya.ai/codevibe${NC}"
  echo ""
}

# ─── Version ─────────────────────────────────────────────────────────

show_version() {
  echo ""
  echo -e "${BOLD}CodeVibe Versions${NC}"
  echo ""

  # Meta-package version
  if [ -f "$PACKAGE_DIR/package.json" ]; then
    META_VER=$(node -p "require('$PACKAGE_DIR/package.json').version" 2>/dev/null || echo "?")
    echo -e "  @quantiya/codevibe              ${GREEN}$META_VER${NC}"
  fi

  # Core version
  CORE_PKG="$PACKAGE_DIR/node_modules/@quantiya/codevibe-core/package.json"
  if [ -f "$CORE_PKG" ]; then
    CORE_VER=$(node -p "require('$CORE_PKG').version" 2>/dev/null || echo "?")
    echo -e "  @quantiya/codevibe-core          ${GREEN}$CORE_VER${NC}"
  fi

  # Plugin versions
  for plugin in claude gemini codex; do
    PKG="$PACKAGE_DIR/node_modules/@quantiya/codevibe-${plugin}-plugin/package.json"
    if [ -f "$PKG" ]; then
      VER=$(node -p "require('$PKG').version" 2>/dev/null || echo "?")
      echo -e "  @quantiya/codevibe-${plugin}-plugin  ${GREEN}$VER${NC}"
    fi
  done

  # Claude Code version
  if command -v claude >/dev/null 2>&1; then
    CLAUDE_VER=$(claude --version 2>/dev/null | head -1 || echo "?")
    echo ""
    echo -e "  Claude Code                      ${GREEN}$CLAUDE_VER${NC}"
  fi

  echo ""
}

# ─── Update ──────────────────────────────────────────────────────────
#
# NOTE: Claude Code's plugin lifecycle has known bugs as of Claude Code 2.1.x:
#   1. `claude plugin update` lies — reports "already at latest version" based on
#      the installed plugin's own package.json, never consults the marketplace
#      manifest for the authoritative latest version.
#   2. `claude plugin install` doesn't refetch — if a cache directory for the
#      plugin exists, it reuses it silently instead of downloading the version
#      pinned in the marketplace manifest.
#   3. `claude plugin uninstall` doesn't delete the cached plugin directory —
#      only removes the installed_plugins.json entry, leaving stale code on disk
#      for the next install to silently reuse.
#
# This function works around all three by detecting when the installed version
# on disk doesn't match the version the marketplace says it should be, then
# manually clearing all stale cache paths before reinstalling and verifying.
#
# If Anthropic fixes these bugs, the cache-nuking path is a no-op (the version
# comparison will always match) and the code stays correct.

_read_marketplace_version() {
  # Read the pinned source.version for codevibe-claude from the marketplace manifest.
  # Echoes the version string, or empty on failure.
  local manifest="$HOME/.claude/plugins/marketplaces/codevibe-marketplace/.claude-plugin/marketplace.json"
  [ -f "$manifest" ] || return 0
  node -p "
    try {
      const m = require('$manifest');
      const p = (m.plugins || []).find(x => x.name === 'codevibe-claude');
      (p && p.source && p.source.version) || '';
    } catch (e) { '' }
  " 2>/dev/null
}

_read_installed_version() {
  # Read the installed version of codevibe-claude from installed_plugins.json.
  # Echoes the version string, or empty if not installed.
  local db="$HOME/.claude/plugins/installed_plugins.json"
  [ -f "$db" ] || return 0
  node -p "
    try {
      const ip = require('$db');
      const e = (ip.plugins || {})['codevibe-claude@codevibe-marketplace'];
      (e && e[0] && e[0].version) || '';
    } catch (e) { '' }
  " 2>/dev/null
}

_nuke_claude_plugin_cache() {
  # Remove every path Claude Code's plugin loader might read stale code from.
  rm -rf "$HOME/.claude/plugins/cache/codevibe-marketplace/codevibe-claude"
  rm -rf "$HOME/.claude/plugins/npm-cache/node_modules/@quantiya/codevibe-claude-plugin"
  rm -rf "$HOME/.claude/plugins/npm-cache/node_modules/codevibe-claude-plugin"
  rm -f  "$HOME/.claude/plugins/npm-cache/package-lock.json"
}

do_update() {
  echo ""
  echo -e "${BOLD}${PURPLE}CodeVibe Update${NC}"
  echo ""

  # Show current meta-package version
  if [ -f "$PACKAGE_DIR/package.json" ]; then
    CUR_VER=$(node -p "require('$PACKAGE_DIR/package.json').version" 2>/dev/null || echo "?")
    echo -e "  ${DIM}Current meta-package: $CUR_VER${NC}"
  fi
  echo ""

  # ─── Step 1: Update the global meta-package (pulls latest core + plugins) ──
  echo -e "${PURPLE}▸${NC} Updating @quantiya/codevibe..."
  # Capture output so we can check npm's exit status directly instead of the
  # pipe's (tail's exit status would mask npm failures without `set -o pipefail`).
  local npm_output
  if ! npm_output=$(npm install -g --force @quantiya/codevibe@latest 2>&1); then
    echo "$npm_output" | tail -10
    echo ""
    echo -e "${YELLOW}!${NC} Meta-package update failed. Check the output above, fix the issue, and retry 'codevibe update'."
    return 1
  fi
  echo "$npm_output" | tail -3
  echo -e "${GREEN}✓${NC} Meta-package updated"

  # ─── Step 2: Update the Claude Code plugin ─────────────────────────────────
  if ! command -v claude >/dev/null 2>&1; then
    echo ""
    echo -e "${YELLOW}!${NC} claude CLI not found — skipping Claude Code plugin update"
    echo ""
    echo -e "${BOLD}Updated versions:${NC}"
    show_version
    return 0
  fi

  echo ""
  echo -e "${PURPLE}▸${NC} Updating Claude Code plugin..."

  # Refresh the marketplace manifest so we see the latest pinned version.
  # Errors here are non-fatal at this step — the previously-cached manifest may
  # still be usable — but the EXPECTED_VERSION read below is the real gate.
  if claude plugin marketplace update codevibe-marketplace; then
    echo -e "${GREEN}✓${NC} Marketplace refreshed"
  else
    echo -e "${YELLOW}!${NC} Marketplace refresh failed (falling back to cached manifest)"
  fi

  local EXPECTED_VERSION
  local INSTALLED_VERSION
  EXPECTED_VERSION=$(_read_marketplace_version)
  INSTALLED_VERSION=$(_read_installed_version)

  # EXPECTED_VERSION is required — without it we have no ground truth to verify
  # against, so the whole cache-bug workaround is blind. Fail loudly rather than
  # claim success we can't prove.
  if [ -z "$EXPECTED_VERSION" ]; then
    echo ""
    echo -e "${YELLOW}!${NC} Could not read expected version from marketplace manifest."
    echo -e "  ${DIM}Expected at: \$HOME/.claude/plugins/marketplaces/codevibe-marketplace/.claude-plugin/marketplace.json${NC}"
    echo -e "  ${DIM}This usually means the marketplace refresh failed or the manifest schema has drifted.${NC}"
    echo -e "  ${DIM}Try: claude plugin marketplace update codevibe-marketplace${NC}"
    return 1
  fi

  echo -e "  ${DIM}installed: ${INSTALLED_VERSION:-<none>}   marketplace: ${EXPECTED_VERSION}${NC}"

  # ─── Step 3: Decide if we need to force a cache-clearing reinstall ─────────
  local NEEDS_REINSTALL=false
  if [ -z "$INSTALLED_VERSION" ] || [ "$INSTALLED_VERSION" != "$EXPECTED_VERSION" ]; then
    NEEDS_REINSTALL=true
    if [ -n "$INSTALLED_VERSION" ]; then
      echo -e "${YELLOW}!${NC} Cache is stale (${INSTALLED_VERSION} on disk, ${EXPECTED_VERSION} in marketplace)."
      echo -e "  ${DIM}Working around Claude Code plugin update bug...${NC}"
    fi
  fi

  if [ "$NEEDS_REINSTALL" = "true" ]; then
    # Must uninstall + nuke cache BEFORE install. `claude plugin install` will
    # silently reuse an existing cache dir even if the marketplace version changed.
    claude plugin uninstall codevibe-claude@codevibe-marketplace >/dev/null 2>&1 || true
    _nuke_claude_plugin_cache

    if claude plugin install codevibe-claude@codevibe-marketplace; then
      echo -e "${GREEN}✓${NC} Claude plugin reinstalled"
    else
      echo ""
      echo -e "${YELLOW}!${NC} Claude plugin install failed — try running 'codevibe update' again"
      return 1
    fi
  else
    # Version already matches. Still call install to make sure installed_plugins.json
    # is in sync, but treat any error as non-fatal since we're not actually changing state.
    claude plugin install codevibe-claude@codevibe-marketplace >/dev/null 2>&1 || true
    echo -e "${GREEN}✓${NC} Claude plugin already at ${INSTALLED_VERSION}"
  fi

  # ─── Step 4: Verify the installed version is now what we expected ──────────
  # EXPECTED_VERSION is guaranteed non-empty here (we bailed earlier otherwise),
  # so the verification is unconditional.
  INSTALLED_VERSION=$(_read_installed_version)
  if [ "$INSTALLED_VERSION" != "$EXPECTED_VERSION" ]; then
    echo ""
    echo -e "${YELLOW}!${NC} WARNING: Installed version (${INSTALLED_VERSION:-<none>}) still doesn't match expected (${EXPECTED_VERSION})."
    echo -e "  ${DIM}Try: rm -rf ~/.claude/plugins/cache/codevibe-marketplace/codevibe-claude && codevibe update${NC}"
    return 1
  fi

  # ─── Step 5: Tell the user about running sessions ──────────────────────────
  if [ "$NEEDS_REINSTALL" = "true" ]; then
    echo ""
    echo -e "${YELLOW}!${NC} Running Claude Code sessions still have the OLD plugin server in memory."
    echo -e "  ${DIM}Exit and restart any active session to pick up the new plugin.${NC}"
  fi

  echo ""

  # Show new versions
  echo -e "${BOLD}Updated versions:${NC}"
  show_version
}

# ─── Command routing ─────────────────────────────────────────────────

case "$1" in
  --help|-h|help|"")
    show_help
    exit 0
    ;;
  version|--version|-v)
    show_version
    exit 0
    ;;
  update)
    do_update
    exit $?
    ;;
  login|logout|status|reset-device)
    # Delegate to codevibe-core CLI
    CORE_CLI="$PACKAGE_DIR/node_modules/@quantiya/codevibe-core/bin/codevibe.js"
    if [ -f "$CORE_CLI" ]; then
      exec node "$CORE_CLI" "$@"
    else
      echo "Error: @quantiya/codevibe-core not found. Try reinstalling: npm install -g @quantiya/codevibe" >&2
      exit 1
    fi
    ;;
  *)
    echo "Unknown command: $1" >&2
    echo "Run 'codevibe --help' for usage." >&2
    exit 1
    ;;
esac
