#!/bin/bash
# ekg-read - Command-line interface for reading notes from ekg
#
# Usage:
#   ekg-read --tag tag1 --tag tag2 -n 10 -w 100
#   ekg-read <note-id>
#   ekg-read --semantic-search "query string"
#   ekg-read --search "query string"
#   ekg-read --latest -n 5
#
# This script uses emacsclient to communicate with a running Emacs daemon,
# providing fast note reading for agents and scripts.

set -e

# Default values
TAGS=()
NOTE_ID=""
SEMANTIC_SEARCH=""
TEXT_SEARCH=""
NUM=10
MAX_WORDS=100
DAEMON_NAME=""
MODE=""

# Parse command-line arguments
while [[ $# -gt 0 ]]; do
    case $1 in
        --tag)
            TAGS+=("$2")
            shift 2
            ;;
        -n|--num)
            NUM="$2"
            shift 2
            ;;
        -w|--words)
            MAX_WORDS="$2"
            shift 2
            ;;
        --semantic-search)
            SEMANTIC_SEARCH="$2"
            MODE="semantic"
            shift 2
            ;;
        --search)
            TEXT_SEARCH="$2"
            MODE="text"
            shift 2
            ;;
        --latest)
            MODE="latest"
            shift
            ;;
        --daemon)
            DAEMON_NAME="$2"
            shift 2
            ;;
        --help)
            echo "Usage: ekg-read [OPTIONS]"
            echo ""
            echo "Read notes from ekg and output as JSON."
            echo ""
            echo "Options:"
            echo "  --tag TAG           Filter by tag (can be specified multiple times, AND logic)"
            echo "  -n, --num NUM       Maximum number of notes to return (default: 10)"
            echo "  -w, --words NUM     Maximum words per note (default: 100)"
            echo "  --semantic-search Q Semantic search using embeddings"
            echo "  --search QUERY      Full-text search"
            echo "  --latest            Return the latest modified notes"
            echo "  --daemon NAME       Name of the Emacs daemon to use (optional)"
            echo "  --help              Show this help message"
            echo ""
            echo "Examples:"
            echo "  # Read notes with multiple tags"
            echo "  ekg-read --tag 'project/my-project' --tag 'status' -n 5"
            echo ""
            echo "  # Read a specific note by ID"
            echo "  ekg-read 12345678"
            echo ""
            echo "  # Semantic search"
            echo "  ekg-read --semantic-search 'machine learning concepts'"
            echo ""
            echo "  # Text search"
            echo "  ekg-read --search 'important deadline'"
            echo ""
            echo "  # Latest modified notes"
            echo "  ekg-read --latest -n 5"
            exit 0
            ;;
        *)
            # If it's a number, treat it as a note ID
            if [[ "$1" =~ ^[0-9]+$ ]]; then
                NOTE_ID="$1"
                MODE="id"
                shift
            else
                echo "Error: Unknown option $1"
                echo "Use --help for usage information"
                exit 1
            fi
            ;;
    esac
done

# Determine mode if not set
if [[ -z "$MODE" ]]; then
    if [[ ${#TAGS[@]} -gt 0 ]]; then
        MODE="tags"
    else
        echo "Error: Must provide either tags, note ID, --semantic-search, --search, or --latest"
        echo "Use --help for usage information"
        exit 1
    fi
fi

# Validate numeric arguments
if ! [[ "$NUM" =~ ^[0-9]+$ ]]; then
    echo "Error: --num must be a positive integer, got: $NUM"
    exit 1
fi

if ! [[ "$MAX_WORDS" =~ ^[0-9]+$ ]]; then
    echo "Error: --words must be a positive integer, got: $MAX_WORDS"
    exit 1
fi

# Build the elisp expression based on mode
case $MODE in
    tags)
        # Build tags list for elisp
        TAGS_ELISP="("
        for tag in "${TAGS[@]}"; do
            escaped_tag="${tag//\"/\\\"}"
            TAGS_ELISP+="\"$escaped_tag\" "
        done
        TAGS_ELISP="${TAGS_ELISP% })"

        READ_EXPR="(ekg-agent-read-notes :tags '$TAGS_ELISP :num $NUM :max-words $MAX_WORDS)"
        ;;

    id)
        READ_EXPR="(ekg-agent-read-notes :note-id $NOTE_ID :max-words $MAX_WORDS)"
        ;;

    semantic)
        # Escape the search query
        SEARCH_ELISP="${SEMANTIC_SEARCH//\\/\\\\}"
        SEARCH_ELISP="${SEARCH_ELISP//\"/\\\"}"

        READ_EXPR="(ekg-agent-read-notes :semantic-search \"$SEARCH_ELISP\" :num $NUM :max-words $MAX_WORDS)"
        ;;

    text)
        # Escape the search query
        SEARCH_ELISP="${TEXT_SEARCH//\\/\\\\}"
        SEARCH_ELISP="${SEARCH_ELISP//\"/\\\"}"

        READ_EXPR="(ekg-agent-read-notes :text-search \"$SEARCH_ELISP\" :num $NUM :max-words $MAX_WORDS)"
        ;;

    latest)
        READ_EXPR="(ekg-agent-read-notes :latest t :num $NUM :max-words $MAX_WORDS)"
        ;;
esac

# Build emacsclient command
EMACSCLIENT_CMD="emacsclient"
if [[ -n "$DAEMON_NAME" ]]; then
    EMACSCLIENT_CMD+=" -s $DAEMON_NAME"
fi

# Base64-encode the JSON to avoid emacsclient protocol escaping issues.
# emacsclient --eval interprets \n in output as literal newlines, which can
# corrupt output containing JSON with embedded newlines.
ELISP_EXPR="(progn
  (require 'ekg-agent)
  (base64-encode-string (encode-coding-string $READ_EXPR 'utf-8) t))"

# Execute via emacsclient
if ! OUTPUT=$($EMACSCLIENT_CMD --eval "$ELISP_EXPR" 2>&1); then
    echo "Error: Failed to read notes. Is the Emacs daemon running?" >&2
    echo "Start a daemon with: emacs --daemon${DAEMON_NAME:+=$DAEMON_NAME}" >&2
    echo "$OUTPUT" >&2
    exit 1
fi

# Strip outer quotes from emacsclient output, then base64-decode
echo "$OUTPUT" | sed 's/^"//; s/"$//' | base64 --decode
