#!/bin/bash
# m.recipes — AI benchmarking
# One-click Install Script
# 
# Usage: 
#   curl -fsSL https://m.recipes/install.sh | bash
#   curl -fsSL https://m.recipes/install.sh | bash -s -- --shuffle
#   curl -fsSL https://m.recipes/install.sh | bash -s -- --api-key YOUR_KEY
#   # Or with env (note: env must be passed to bash, not curl)
#   curl -fsSL https://m.recipes/install.sh | MRECIPES_API_KEY="YOUR_KEY" bash

set -e
# Import older installer settings without overriding canonical values.
while IFS= read -r recipe_key; do
    recipe_new="MRECIPES_${recipe_key#W_BENCH_}"
    if [ -z "${!recipe_new+x}" ]; then export "$recipe_new=${!recipe_key}"; fi
done < <(compgen -e W_BENCH_)
# Bridge canonical settings to the older globally pinned CLI during migration.
while IFS= read -r recipe_key; do
    export "W_BENCH_${recipe_key#MRECIPES_}=${!recipe_key}"
done < <(compgen -e MRECIPES_)


# Published versions are rendered from cli-releases.json when staging the site.
VERSION=""
PINNED_URL=""
PINNED_SHA=""
DEFAULT_INSTALL_DIR="${HOME:-/tmp}/.local/bin"
INSTALL_DIR="${MRECIPES_INSTALL_DIR:-$DEFAULT_INSTALL_DIR}"
BIN_NAME="mr"
# New CLI releases are published on the DMIT release endpoint.
BASE_URL="https://release.myfastools.com/releases/mrecipes/bin"

# Do not shadow a different application's existing `mr` command.
is_recipes_binary() {
    local reported
    reported=$("$1" --version 2>/dev/null) || return 1
    [[ "$reported" =~ ^(mr|w-bench)\ [0-9]+\.[0-9]+\.[0-9]+$ ]]
}


# Capture arguments to pass to autorun
UPDATE_ONLY=false
API_KEY=""
AUTORUN_ARGS=()
RECIPE_ID=""
RECIPE_OPTIONS=()
while [[ $# -gt 0 ]]; do
  case "$1" in
    --recipe|--run)
      if [[ $# -lt 2 || ! "$2" =~ ^((decision|embedding|llm)-[a-f0-9-]{36}-[bc][1-9][0-9]{0,3}|[A-Za-z0-9_][A-Za-z0-9_.-]*/[A-Za-z0-9_][A-Za-z0-9_.-]*)$ ]]; then echo '--recipe/--run requires a recipe ID or OWNER/MODEL repository' >&2; exit 2; fi
      RECIPE_ID="$2"
      shift 2
      ;;
    --benchmark|--dry-run|--no-upload)
      RECIPE_OPTIONS+=("$1")
      AUTORUN_ARGS+=("$1")
      shift
      ;;
    --update|-u|update)
      UPDATE_ONLY=true
      shift
      ;;
    --no-run)
      UPDATE_ONLY=true
      shift
      ;;
    --api-key)
      if [[ $# -lt 2 || -z "$2" || "$2" == --* ]]; then
        echo '--api-key requires a value' >&2
        exit 2
      fi
      API_KEY="$2"
      shift 2
      ;;
    --api-key=*)
      API_KEY="${1#*=}"
      shift
      ;;
    *)
      AUTORUN_ARGS+=("$1")
      shift
      ;;
  esac
done

if [ -n "$RECIPE_ID" ] && { [ "$UPDATE_ONLY" = true ] || [ "${#AUTORUN_ARGS[@]}" -ne "${#RECIPE_OPTIONS[@]}" ]; }; then
    echo 'Use --recipe ID with optional --benchmark, --dry-run or --no-upload only' >&2
    exit 2
fi

if [ -n "$RECIPE_ID" ] && [ -z "${MRECIPES_INSTALL_DIR+x}" ] && [ "$EUID" -eq 0 ] && [ -w /usr/local/bin ]; then
    INSTALL_DIR=/usr/local/bin
fi
EXISTING_MR=$(command -v mr || true)
if { [ -n "$EXISTING_MR" ] && ! is_recipes_binary "$EXISTING_MR"; } ||
   { { [ -e "$INSTALL_DIR/mr" ] || [ -L "$INSTALL_DIR/mr" ]; } && ! is_recipes_binary "$INSTALL_DIR/mr"; }; then
    echo 'An unrelated mr command already exists; refusing to replace or shadow it.' >&2
    exit 1
fi

# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color

echo -e "${BLUE}"
echo "m.recipes — AI performance, measured"
echo -e "${NC}"

# Detect OS and Architecture
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)

echo -e "${YELLOW}Detecting System...${NC}"
echo "  OS: $OS"
echo "  Arch: $ARCH"

# Determine binary to download
case "$OS" in
    linux)
        case "$ARCH" in
            x86_64|amd64)
                VERSION="v1.1.71"
                PINNED_URL="https://m.recipes/api/v1/releases/mr-linux-x86_64-v1.1.71"
                PINNED_SHA="4df34bb7edc26caeea06f22d4672613d9913360d8a8e6cacbba242bbc1bdee2d"
                BINARY="mr-linux-x86_64-${VERSION}"
                ;;
            aarch64|arm64)
                VERSION=""
                PINNED_URL=""
                PINNED_SHA=""
                BINARY="mr-linux-aarch64-${VERSION}"
                ;;
            *)
                echo -e "${RED}Unsupported Architecture: $ARCH${NC}"
                exit 1
                ;;
        esac
        ;;
    darwin)
        case "$ARCH" in
            x86_64)
                VERSION=""
                PINNED_URL=""
                PINNED_SHA=""
                BINARY="mr-darwin-x86_64-${VERSION}"
                ;;
            arm64)
                VERSION=""
                PINNED_URL=""
                PINNED_SHA=""
                BINARY="mr-darwin-arm64-${VERSION}"
                ;;
            *)
                echo -e "${RED}Unsupported Architecture: $ARCH${NC}"
                exit 1
                ;;
        esac
        ;;
    *)
        echo -e "${RED}Unsupported OS: $OS${NC}"
        exit 1
        ;;
esac

if [ -n "$RECIPE_ID" ]; then
    if [ "$OS/$ARCH" != linux/x86_64 ] || [ 'true' != true ]; then
        echo 'A compatible Linux x86_64 recipe executor is required' >&2; exit 1
    fi
    if [[ "$RECIPE_ID" == */* ]] && [ 'true' != true ]; then
        echo 'A model-name-capable recipe executor has not been published yet' >&2; exit 1
    fi
    VERSION='v1.1.64'
    BINARY="mr-linux-x86_64-${VERSION}"
fi

if [[ ! "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
    echo -e "${RED}No pinned release for ${OS}/${ARCH}; use the published installer or build from source.${NC}"
    exit 1
fi
if [ -n "${MRECIPES_EXPECTED_VERSION:-}" ] && [ "$VERSION" != "$MRECIPES_EXPECTED_VERSION" ]; then
    echo -e "${RED}Release pointer changed during update; retry to select the new version.${NC}"
    exit 1
fi

DOWNLOAD_URL="${PINNED_URL:-${BASE_URL}/${BINARY}}"
if [ -n "$PINNED_URL" ]; then BINARY="${PINNED_URL##*/}"; fi
if [ -n "$RECIPE_ID" ]; then PINNED_SHA='46c621c4e3df8e7c8a2ef444a2f0856e0d377c055b980669b9dceaca0aa40454'; DOWNLOAD_URL='https://m.recipes/api/v1/releases/mr-linux-x86_64-v1.1.64'; fi

echo -e "${YELLOW}Downloading $BINARY...${NC}"
echo "  URL: $DOWNLOAD_URL"

# Create temp directory
TMP_DIR=$(mktemp -d)
trap 'rm -rf "$TMP_DIR"' EXIT
TMP_FILE="${TMP_DIR}/${BIN_NAME}"

# Download binary with retry
MAX_RETRIES=3
RETRY_DELAY=3

download_file() {
    local url="$1"
    local output="$2"
    
    if command -v curl &> /dev/null; then
        curl -fsSL --connect-timeout 10 --max-time 120 "$url" -o "$output"
    elif command -v wget &> /dev/null; then
        wget -q --timeout=10 "$url" -O "$output"
    else
        echo -e "${RED}curl or wget is required${NC}"
        return 1
    fi
}

for i in $(seq 1 $MAX_RETRIES); do
    echo -e "${YELLOW}Download attempt $i/$MAX_RETRIES...${NC}"
    if download_file "$DOWNLOAD_URL" "$TMP_FILE"; then
        echo -e "${GREEN}✅ Download successful${NC}"
        break
    else
        if [ $i -eq $MAX_RETRIES ]; then
            echo -e "${RED}❌ Download failed after $MAX_RETRIES attempts${NC}"
            rm -rf "$TMP_DIR"
            exit 1
        fi
        echo -e "${YELLOW}⚠️ Download failed, retrying in ${RETRY_DELAY}s...${NC}"
        sleep $RETRY_DELAY
        RETRY_DELAY=$((RETRY_DELAY * 2))
    fi
done

# A missing/malformed checksum or unavailable hash tool must stop installation.
SHA_URL="${DOWNLOAD_URL}.sha256"
if ! download_file "$SHA_URL" "$TMP_DIR/checksum"; then
    echo -e "${RED}❌ Cannot download release checksum; refusing to install${NC}"
    exit 1
fi
read -r EXPECTED_SHA _ < "$TMP_DIR/checksum" || true
if [[ ! "$EXPECTED_SHA" =~ ^[0-9a-fA-F]{64}$ ]]; then
    echo -e "${RED}❌ Invalid release checksum; refusing to install${NC}"
    exit 1
fi
if command -v sha256sum &> /dev/null; then
    ACTUAL_SHA=$(sha256sum "$TMP_FILE" | awk '{print $1}')
elif command -v shasum &> /dev/null; then
    ACTUAL_SHA=$(shasum -a 256 "$TMP_FILE" | awk '{print $1}')
else
    echo -e "${RED}❌ sha256sum or shasum is required${NC}"
    exit 1
fi
if [ -n "$PINNED_SHA" ] && [ "$ACTUAL_SHA" != "$PINNED_SHA" ]; then
    echo 'CLI checksum differs from the published pin' >&2; exit 1
fi
if [ "$ACTUAL_SHA" != "$(printf '%s' "$EXPECTED_SHA" | tr '[:upper:]' '[:lower:]')" ]; then
    echo -e "${RED}❌ Checksum mismatch; refusing to install${NC}"
    exit 1
fi
echo -e "${GREEN}✅ Checksum verified${NC}"

# Make executable
chmod +x "$TMP_FILE"
REPORTED_VERSION=$("$TMP_FILE" --version)
if [ "$REPORTED_VERSION" != "mr ${VERSION#v}" ] && [ "$REPORTED_VERSION" != "w-bench ${VERSION#v}" ]; then
    echo -e "${RED}Binary version does not match the selected release; refusing to install.${NC}"
    exit 1
fi

# Create install directory first (as current user)
mkdir -p "$INSTALL_DIR" 2>/dev/null || true

# Check if we need sudo (only after trying to create dir)
NEED_SUDO=false
if [ ! -w "$INSTALL_DIR" ]; then
    NEED_SUDO=true
fi

echo -e "${YELLOW}Installing to $INSTALL_DIR...${NC}"
# Stage on the destination filesystem, then replace atomically. A failed copy
# leaves the existing executable and all ~/.mrecipes data intact.
if [ "$NEED_SUDO" = true ]; then
    sudo mkdir -p "$INSTALL_DIR"
    STAGED_BINARY=$(sudo mktemp "${INSTALL_DIR}/.mrecipes.XXXXXX")
    sudo cp "$TMP_FILE" "$STAGED_BINARY"
    sudo chmod 755 "$STAGED_BINARY"
    sudo mv -f "$STAGED_BINARY" "${INSTALL_DIR}/${BIN_NAME}"
else
    STAGED_BINARY=$(mktemp "${INSTALL_DIR}/.mrecipes.XXXXXX")
    cp "$TMP_FILE" "$STAGED_BINARY"
    chmod 755 "$STAGED_BINARY"
    mv -f "$STAGED_BINARY" "${INSTALL_DIR}/${BIN_NAME}"
fi

# A compatibility name points to the same executable, never a second implementation.
LEGACY_PATH="$INSTALL_DIR/w-bench"
if { [ ! -e "$LEGACY_PATH" ] && [ ! -L "$LEGACY_PATH" ]; } || is_recipes_binary "$LEGACY_PATH"; then
    if [ "$NEED_SUDO" = true ]; then
        ALIAS_STAGE=$(sudo mktemp "${INSTALL_DIR}/.mr-alias.XXXXXX")
        sudo ln -sfn mr "$ALIAS_STAGE"
        sudo mv -f "$ALIAS_STAGE" "$LEGACY_PATH"
    else
        ALIAS_STAGE=$(mktemp "${INSTALL_DIR}/.mr-alias.XXXXXX")
        ln -sfn mr "$ALIAS_STAGE"
        mv -f "$ALIAS_STAGE" "$LEGACY_PATH"
    fi
fi

# Cleanup
rm -rf "$TMP_DIR"

echo -e "${GREEN}✅ Installed to ${INSTALL_DIR}/${BIN_NAME}${NC}"

# Save API key if provided (via --api-key or MRECIPES_API_KEY env)
FINAL_API_KEY="${API_KEY:-${MRECIPES_API_KEY:-}}"
if [ -n "$FINAL_API_KEY" ]; then
    APP_HOME="${HOME}/.mrecipes"
    # The general installer may still pin a pre-migration binary.
    if ! "${INSTALL_DIR}/${BIN_NAME}" --help 2>/dev/null | grep -q MRECIPES_API_KEY; then APP_HOME="${HOME}/.w-bench"; fi
    if [ ! -d "$APP_HOME" ]; then
        for recipe_legacy in .gpullmbench .w-bench; do
            if [ -d "${HOME}/$recipe_legacy" ]; then APP_HOME="${HOME}/$recipe_legacy"; break; fi
        done
    fi
    CONFIG_DIR="$APP_HOME/config"
    mkdir -p -m 700 "$CONFIG_DIR"
    STAGED_KEY=$(mktemp "${CONFIG_DIR}/.api_key.XXXXXX")
    if ! printf '%s\n' "$FINAL_API_KEY" > "$STAGED_KEY" || ! mv -f "$STAGED_KEY" "${CONFIG_DIR}/api_key"; then
        rm -f "$STAGED_KEY"
        echo 'Cannot save API key' >&2
        exit 1
    fi
    echo -e "${GREEN}🔑 API Key saved${NC}"
fi

# Export for current session
export PATH="$INSTALL_DIR:$PATH"
echo -e "${GREEN}✅ PATH set for current session${NC}"

# Ensure Profile Configuration (PATH and API Key)
# Always run this checking logic
SHELL_NAME=$(basename "$SHELL")
PROFILE_FILES=()

case "$SHELL_NAME" in
    zsh)
        PROFILE_FILES=("$HOME/.zshrc" "$HOME/.zprofile")
        ;;
    bash)
        PROFILE_FILES=("$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.profile")
        ;;
    *)
        PROFILE_FILES=("$HOME/.profile" "$HOME/.bashrc")
        ;;
esac

# Find first existing profile or create one
PROFILE_FILE=""
for pf in "${PROFILE_FILES[@]}"; do
    if [ -f "$pf" ]; then
        PROFILE_FILE="$pf"
        break
    fi
done

if [ -z "$PROFILE_FILE" ]; then
    PROFILE_FILE="${PROFILE_FILES[0]}"
fi

# 1. Config PATH (idempotent check)
if ! grep -q "$INSTALL_DIR" "$PROFILE_FILE" 2>/dev/null; then
    echo "" >> "$PROFILE_FILE"
    echo "# Added by m.recipes installer" >> "$PROFILE_FILE"
    echo "export PATH=\"$INSTALL_DIR:\$PATH\"" >> "$PROFILE_FILE"
    echo -e "${GREEN}✅ PATH configured in $PROFILE_FILE${NC}"
fi

# 2. API Key: persisted ONLY in ~/.mrecipes/config/api_key (mode 600, written
# above). The CLI reads it from there (paths::load_api_key fallback), so we do
# NOT write it into a world-readable shell profile (~/.bashrc is typically 644).
# Export for the current session only so an immediate autorun picks it up.
if [ -n "$FINAL_API_KEY" ]; then
    export MRECIPES_API_KEY="$FINAL_API_KEY"
    export W_BENCH_API_KEY="$FINAL_API_KEY"
fi

echo ""
if [ -n "$RECIPE_ID" ]; then
    echo "Starting recipe: mr $RECIPE_ID"
    rm -rf "$TMP_DIR"
    trap - EXIT
    exec "${INSTALL_DIR}/${BIN_NAME}" "$RECIPE_ID" "${RECIPE_OPTIONS[@]}"
fi

echo -e "${BLUE}Usage:${NC}"
echo "  mr autotest --dry-run   # Inspect the automatic coverage plan"
echo "  mr autotest --yes       # Execute applicable tasks and retain evidence"
echo "  mr autorun              # Run LLM benchmark"
echo "  mr autorun --mode image # Run Image benchmark"
echo "  mr autorun --shuffle    # Randomize model order"
echo "  mr autorun --reverse    # Large models first"
echo "  mr autorun --fill-gaps  # Prioritize missing results"
echo "  mr autorun --duration 5 # Faster: 5s per concurrency level (default 30s)"
echo "  mr serve --model Qwen/Qwen2.5-7B-Instruct-AWQ --port 8000  # Start API endpoint"
echo "  mr --help               # View help"
echo ""
echo -e "${BLUE}Node Mode (Real-time Inference):${NC}"
echo "  mr serve --mode online --dynamic             # Load models assigned by platform jobs"
echo "  mr serve --mode online --model MODEL_ID       # Start a fixed model"
echo "  mr update                                    # Update to latest version"
echo ""
echo -e "${BLUE}Model Source:${NC}"
echo "  curl -fsSL https://m.recipes/install.sh | MRECIPES_MODEL_SOURCE=modelscope bash"
echo "  curl -fsSL https://m.recipes/install.sh | MRECIPES_MODEL_SOURCE=huggingface bash"
echo ""
echo -e "${YELLOW}View Leaderboard: https://m.recipes${NC}"

# Default: install-only, unless args are specified
if [ "$UPDATE_ONLY" = false ] && [ ${#AUTORUN_ARGS[@]} -gt 0 ]; then
    echo ""
    echo -e "${BLUE}🚀 Starting LLM benchmark${NC}"
    "${INSTALL_DIR}/${BIN_NAME}" autorun "${AUTORUN_ARGS[@]}"
else
    echo ""
    echo -e "${GREEN}✅ Installation complete!${NC}"
    echo ""
    echo -e "${YELLOW}To start using mr, either:${NC}"
    echo -e "  1. Open a new terminal, or"
    echo -e "  2. Run: ${CYAN}source ~/.bashrc${NC}  (or ~/.zshrc for zsh)"
    echo ""
    echo -e "Then inspect your plan: ${CYAN}mr autotest --dry-run${NC}"
    echo ""
    echo -e "${YELLOW}In a non-login shell (Colab/Jupyter '!' cells, CI), PATH isn't loaded — run by absolute path:${NC}"
    echo -e "  ${CYAN}${INSTALL_DIR}/${BIN_NAME} autotest --dry-run${NC}"
    echo -e "${YELLOW}Or one-shot install + run (recommended for Colab):${NC}"
    echo -e "  ${CYAN}curl -fsSL https://m.recipes/install.sh | bash -s -- --shuffle --max-params 14${NC}"
    echo -e "${YELLOW}Faster run (shorter measurement window per concurrency level):${NC}"
    echo -e "  ${CYAN}curl -fsSL https://m.recipes/install.sh | bash -s -- --shuffle --max-params 14 --duration 5${NC}"
fi
