#!/bin/sh
# Rowly installer: wires Claude Desktop to the Rowly MCP endpoint.
# Source: https://rowly.ai/install
# Audit:  curl -fsSL https://rowly.ai/install | less
#
# Usage:  curl -fsSL rowly.ai/install | sh
# Env:    ROWLY_KEY=rowly_live_... (skips interactive prompt if set)

set -eu

MCP_URL="https://api.rowly.ai/mcp"

# ---- output helpers ---------------------------------------------------------
if [ -t 1 ]; then
    C_RESET="$(printf '\033[0m')"
    C_BLUE="$(printf '\033[34m')"
    C_GREEN="$(printf '\033[32m')"
    C_YELLOW="$(printf '\033[33m')"
    C_RED="$(printf '\033[31m')"
    C_DIM="$(printf '\033[2m')"
else
    C_RESET=""; C_BLUE=""; C_GREEN=""; C_YELLOW=""; C_RED=""; C_DIM=""
fi

info() { printf '%s>%s %s\n' "$C_BLUE" "$C_RESET" "$1"; }
ok()   { printf '%sv%s %s\n' "$C_GREEN" "$C_RESET" "$1"; }
warn() { printf '%s!%s %s\n' "$C_YELLOW" "$C_RESET" "$1"; }
err()  { printf '%sx%s %s\n' "$C_RED" "$C_RESET" "$1" >&2; }

# ---- 1. detect platform -----------------------------------------------------
UNAME_S="$(uname -s 2>/dev/null || echo unknown)"
case "$UNAME_S" in
    Darwin)
        PLATFORM="macos"
        CONFIG_DIR="$HOME/Library/Application Support/Claude"
        ;;
    Linux)
        # WSL surfaces as Linux; both share the Linux config path
        if grep -qi microsoft /proc/version 2>/dev/null; then
            PLATFORM="wsl"
        else
            PLATFORM="linux"
        fi
        CONFIG_DIR="$HOME/.config/Claude"
        ;;
    MINGW*|MSYS*|CYGWIN*)
        err "Windows-native shells aren't supported yet. Please run this from WSL."
        exit 1
        ;;
    *)
        err "Unsupported platform: $UNAME_S"
        exit 1
        ;;
esac
CONFIG_PATH="$CONFIG_DIR/claude_desktop_config.json"
info "Platform: $PLATFORM"

# ---- 2. detect Node >= 20 ---------------------------------------------------
if ! command -v node >/dev/null 2>&1; then
    err "Node.js not found in PATH."
    case "$PLATFORM" in
        macos) printf '   Install:  %sbrew install node%s\n' "$C_DIM" "$C_RESET" ;;
        linux|wsl)
            printf '   Install:  %scurl -fsSL https://fnm.vercel.app/install | bash%s\n' "$C_DIM" "$C_RESET"
            printf '   Or use your package manager (apt install nodejs, dnf install nodejs).\n'
            ;;
    esac
    exit 1
fi

NODE_RAW="$(command -v node)"
# resolve symlinks so Claude Desktop's restricted PATH still finds the same binary
if command -v readlink >/dev/null 2>&1 && readlink -f / >/dev/null 2>&1; then
    NODE_BIN="$(readlink -f "$NODE_RAW")"
else
    NODE_BIN="$NODE_RAW"
fi

NODE_MAJOR="$("$NODE_BIN" -p 'process.versions.node.split(".")[0]' 2>/dev/null || echo "")"
case "$NODE_MAJOR" in
    ''|*[!0-9]*)
        err "Could not read Node.js version from $NODE_BIN. Try: $NODE_BIN -v"
        exit 1
        ;;
esac
if [ "$NODE_MAJOR" -lt 20 ]; then
    err "Node v$("$NODE_BIN" -v) found at $NODE_BIN, but Rowly needs >= 20."
    case "$PLATFORM" in
        macos) printf '   Upgrade:  %sbrew upgrade node%s\n' "$C_DIM" "$C_RESET" ;;
        linux|wsl) printf '   Upgrade:  %sfnm install 20 && fnm use 20%s\n' "$C_DIM" "$C_RESET" ;;
    esac
    exit 1
fi
ok "Node $("$NODE_BIN" -v) at $NODE_BIN"

# ---- 3. install mcp-remote (global) -----------------------------------------
NPM_BIN="$(command -v npm 2>/dev/null || true)"
if [ -z "$NPM_BIN" ]; then
    err "npm not found alongside node. Reinstall Node from a distribution that bundles npm."
    exit 1
fi

if "$NODE_BIN" -e 'require.resolve("mcp-remote/package.json")' >/dev/null 2>&1; then
    ok "mcp-remote already installed"
else
    info "Installing mcp-remote globally..."
    if ! "$NPM_BIN" install -g mcp-remote >/dev/null 2>&1; then
        warn "npm install -g failed (likely EACCES); retrying with sudo (will prompt for password)..."
        if ! sudo "$NPM_BIN" install -g mcp-remote >/dev/null 2>&1; then
            err "Failed to install mcp-remote. Try manually: npm install -g mcp-remote"
            exit 2
        fi
    fi
    ok "mcp-remote installed"
fi

# resolve absolute path to mcp-remote's proxy script so Claude Desktop never needs npx
MCP_REMOTE_BIN="$("$NODE_BIN" -e 'console.log(require.resolve("mcp-remote/dist/proxy.js"))' 2>/dev/null || echo "")"
if [ -z "$MCP_REMOTE_BIN" ] || [ ! -f "$MCP_REMOTE_BIN" ]; then
    err "Could not resolve mcp-remote/dist/proxy.js after install."
    exit 2
fi
ok "mcp-remote entry: $MCP_REMOTE_BIN"

# ---- 4. obtain API key ------------------------------------------------------
if [ -z "${ROWLY_KEY:-}" ]; then
    if [ ! -r /dev/tty ]; then
        err "ROWLY_KEY not set and no TTY available. Re-run with: ROWLY_KEY=... sh install.sh"
        exit 1
    fi
    # read from /dev/tty because stdin is the curl pipe; -echo so the key
    # isn't shoulder-surfed
    printf 'Paste your Rowly API key (rowly_live_... or rowly_test_...): '
    stty_was=""
    if command -v stty >/dev/null 2>&1; then
        stty_was="$(stty -g </dev/tty 2>/dev/null || echo "")"
        stty -echo </dev/tty 2>/dev/null || true
    fi
    read -r ROWLY_KEY < /dev/tty
    if [ -n "$stty_was" ]; then
        stty "$stty_was" </dev/tty 2>/dev/null || true
    fi
    printf '\n'
fi

case "$ROWLY_KEY" in
    rowly_live_*|rowly_test_*) ;;
    *) err "Invalid key format. Expected prefix rowly_live_ or rowly_test_."; exit 1 ;;
esac

# ---- 5. backup + write config ----------------------------------------------
mkdir -p "$CONFIG_DIR"

BACKUP_PATH=""
if [ -f "$CONFIG_PATH" ]; then
    BACKUP_PATH="$CONFIG_PATH.bak.$(date +%Y%m%d-%H%M%S)"
    cp "$CONFIG_PATH" "$BACKUP_PATH"
    ok "Backup: $BACKUP_PATH"
fi

info "Updating $CONFIG_PATH"
"$NODE_BIN" -e '
    const fs = require("fs");
    const [, , configPath, nodeBin, mcpBin, mcpUrl, key] = process.argv;
    let cfg = {};
    // Preserve existing file mode (config holds a bearer token; if the user
    // already chmod 600-ed it, do not regress to 644).
    let mode = 0o600;
    if (fs.existsSync(configPath)) {
        mode = fs.statSync(configPath).mode & 0o777;
        const raw = fs.readFileSync(configPath, "utf8").trim();
        if (raw) {
            try { cfg = JSON.parse(raw); }
            catch { cfg = {}; }
        }
    }
    if (!cfg.mcpServers || typeof cfg.mcpServers !== "object") cfg.mcpServers = {};
    cfg.mcpServers.rowly = {
        command: nodeBin,
        args: [mcpBin, mcpUrl, "--header", "Authorization:Bearer " + key]
    };
    fs.writeFileSync(configPath, JSON.stringify(cfg, null, 2) + "\n", { mode });
    fs.chmodSync(configPath, mode);
' "$CONFIG_PATH" "$NODE_BIN" "$MCP_REMOTE_BIN" "$MCP_URL" "$ROWLY_KEY"

ok "Rowly added to Claude Desktop"

# ---- 6. final instructions --------------------------------------------------
printf '\n'
printf '%sNext steps:%s\n' "$C_GREEN" "$C_RESET"
printf '  1. Restart Claude Desktop (Cmd/Ctrl+Q then reopen).\n'
printf '  2. Open a new chat and try: %s"What docs do I have in Rowly?"%s\n' "$C_DIM" "$C_RESET"
[ -n "$BACKUP_PATH" ] && printf '\nIf anything breaks: cp "%s" "%s"\n' "$BACKUP_PATH" "$CONFIG_PATH"
printf '\n'
