#!/usr/bin/env bash
# sthes — terminal thesaurus lookup
# Inspired by BreadOnPenguins/scripts/define
# Backends: Oxford Dictionaries API (set OD_APP_ID + OD_APP_KEY)
#           sdcv with thesaurus StarDict dict (offline, set STHES_USE_SDCV=1)
#           dictionaryapi.dev synonyms (free fallback)

set -euo pipefail

BOLD='\033[1m'
DIM='\033[2m'
BLUE='\033[34m'
CYAN='\033[36m'
GREEN='\033[32m'
YELLOW='\033[33m'
MAGENTA='\033[35m'
RESET='\033[0m'

usage() {
    echo -e "${BOLD}sthes${RESET} — look up synonyms and antonyms"
    echo ""
    echo -e "  ${BOLD}Usage:${RESET} sthes <word>"
    echo ""
    echo -e "  ${BOLD}Backends (checked in order):${RESET}"
    echo "    1. Oxford Dictionaries API  — set OD_APP_ID and OD_APP_KEY"
    echo "    2. sdcv (offline thesaurus) — set STHES_USE_SDCV=1"
    echo "    3. dictionaryapi.dev        — free, no config needed"
    exit 0
}

die() { echo -e "\033[31merror:\033[0m $1" >&2; exit 1; }

[[ $# -lt 1 || "$1" == "-h" || "$1" == "--help" ]] && usage
word="$1"
[[ "$word" =~ [^a-zA-Z-] ]] && die "invalid word: $word"

# helper: wrap a comma-separated list at terminal width
wrap_list() {
    local color="$1"
    shift
    local words=("$@")
    local line="    "
    local term_width
    term_width=$(tput cols 2>/dev/null || echo 80)

    for w in "${words[@]}"; do
        if (( ${#line} + ${#w} + 2 > term_width )); then
            echo -e "${color}${line}${RESET}"
            line="    ${w}, "
        else
            line+="${w}, "
        fi
    done
    # trim trailing comma+space
    line="${line%, }"
    [[ -n "$line" ]] && echo -e "${color}${line}${RESET}"
}

# --- Backend 1: Oxford Dictionaries API (Thesaurus endpoint) ---
oxford_lookup() {
    local resp
    resp=$(curl -sf --connect-timeout 5 --max-time 10 \
        -H "app_id: $OD_APP_ID" \
        -H "app_key: $OD_APP_KEY" \
        "https://od-api-v2.oxforddictionaries.com/api/v2/thesaurus/en/${word,,}") || return 1

    echo -e "${BOLD}${BLUE}${word}${RESET}"
    echo ""

    # Extract synonyms and antonyms by part of speech
    echo "$resp" | jq -r '
        .results[].lexicalEntries[] |
        .lexicalCategory.id as $pos |
        .entries[].senses[] |
        {
            pos: $pos,
            syns: [.synonyms[]?.text] | join("|"),
            ants: [.antonyms[]?.text] | join("|")
        } |
        "\(.pos)~\(.syns)~\(.ants)"
    ' 2>/dev/null | while IFS='~' read -r pos syns ants; do
        echo -e "  ${GREEN}${pos}${RESET}"

        if [[ -n "$syns" ]]; then
            echo -e "  ${BOLD}synonyms:${RESET}"
            IFS='|' read -ra syn_arr <<< "$syns"
            wrap_list "$CYAN" "${syn_arr[@]}"
        fi

        if [[ -n "$ants" ]]; then
            echo -e "  ${BOLD}antonyms:${RESET}"
            IFS='|' read -ra ant_arr <<< "$ants"
            wrap_list "$MAGENTA" "${ant_arr[@]}"
        fi
        echo ""
    done
}

# --- Backend 2: sdcv (offline thesaurus) ---
sdcv_lookup() {
    command -v sdcv >/dev/null 2>&1 || return 1
    # Try Collins Thesaurus or Moby Thesaurus if available
    local result
    result=$(sdcv -n -u "Collins Thesaurus" "$word" 2>/dev/null) \
        || result=$(sdcv -n -u "Moby Thesaurus" "$word" 2>/dev/null) \
        || result=$(sdcv -n "$word" 2>/dev/null) \
        || return 1
    [[ "$result" == *"Nothing similar"* ]] && return 1
    echo -e "${BOLD}${BLUE}${word}${RESET}"
    echo ""
    echo "$result" | sed 's/^/  /'
    echo ""
}

# --- Backend 3: Free Dictionary API (fallback — synonyms from meanings) ---
free_lookup() {
    local resp
    resp=$(curl -sf --connect-timeout 5 --max-time 10 \
        "https://api.dictionaryapi.dev/api/v2/entries/en_US/${word}") || return 1
    [[ "$resp" == *"No Definitions Found"* ]] && return 1

    echo -e "${BOLD}${BLUE}${word}${RESET}"
    echo ""

    local found_any=0

    echo "$resp" | jq -r '
        .[].meanings[] |
        .partOfSpeech as $pos |
        {
            pos: $pos,
            def: (.definitions[0].definition // ""),
            syns: ([.synonyms[]?] + [.definitions[].synonyms[]?] | unique | join("|")),
            ants: ([.antonyms[]?] + [.definitions[].antonyms[]?] | unique | join("|"))
        } |
        "\(.pos)~\(.def)~\(.syns)~\(.ants)"
    ' 2>/dev/null | while IFS='~' read -r pos def syns ants; do
        [[ -z "$syns" && -z "$ants" ]] && continue
        echo -e "  ${GREEN}${pos}${RESET}  ${DIM}${def}${RESET}"

        if [[ -n "$syns" ]]; then
            echo -e "  ${BOLD}synonyms:${RESET}"
            IFS='|' read -ra syn_arr <<< "$syns"
            wrap_list "$CYAN" "${syn_arr[@]}"
            found_any=1
        fi

        if [[ -n "$ants" ]]; then
            echo -e "  ${BOLD}antonyms:${RESET}"
            IFS='|' read -ra ant_arr <<< "$ants"
            wrap_list "$MAGENTA" "${ant_arr[@]}"
            found_any=1
        fi
        echo ""
    done

    # If free API had no synonyms, try datamuse as last resort
    if [[ "$found_any" == "0" ]]; then
        local dm
        dm=$(curl -sf --connect-timeout 5 --max-time 10 \
            "https://api.datamuse.com/words?rel_syn=${word}&max=20") || return 1
        local dm_words
        dm_words=$(echo "$dm" | jq -r '.[].word' 2>/dev/null)
        [[ -z "$dm_words" ]] && return 1

        echo -e "  ${BOLD}synonyms:${RESET}"
        local arr=()
        while IFS= read -r w; do arr+=("$w"); done <<< "$dm_words"
        wrap_list "$CYAN" "${arr[@]}"
        echo ""

        # also get antonyms from datamuse
        local dm_ant
        dm_ant=$(curl -sf --connect-timeout 5 --max-time 10 \
            "https://api.datamuse.com/words?rel_ant=${word}&max=20") || true
        local ant_words
        ant_words=$(echo "$dm_ant" | jq -r '.[].word' 2>/dev/null)
        if [[ -n "$ant_words" ]]; then
            echo -e "  ${BOLD}antonyms:${RESET}"
            local ant_arr=()
            while IFS= read -r w; do ant_arr+=("$w"); done <<< "$ant_words"
            wrap_list "$MAGENTA" "${ant_arr[@]}"
            echo ""
        fi
    fi

    echo -e "  ${DIM}source: dictionaryapi.dev / datamuse${RESET}"
}

# --- Dispatch ---
if [[ -n "${OD_APP_ID:-}" && -n "${OD_APP_KEY:-}" ]]; then
    oxford_lookup && exit 0
    echo -e "${DIM}Oxford API failed, trying fallback...${RESET}" >&2
fi

if [[ "${STHES_USE_SDCV:-0}" == "1" ]]; then
    sdcv_lookup && exit 0
    echo -e "${DIM}sdcv failed, trying fallback...${RESET}" >&2
fi

free_lookup || die "no results found for '${word}'"
