#!/usr/bin/env bash
# lettabot-tts - Generate speech audio via configurable TTS provider
#
# Usage: lettabot-tts <text> [output_path]
#
# Environment:
#   TTS_PROVIDER         - Optional. "elevenlabs" (default) or "openai".
#
#   ElevenLabs:
#     ELEVENLABS_API_KEY   - Required. API key.
#     ELEVENLABS_VOICE_ID  - Optional. Voice ID (default: onwK4e9ZLuTAKqWW03F9).
#     ELEVENLABS_MODEL_ID  - Optional. Model ID (default: eleven_multilingual_v2).
#
#   OpenAI:
#     OPENAI_API_KEY           - Required. API key.
#     OPENAI_TTS_VOICE         - Optional. Voice name (default: alloy).
#     OPENAI_TTS_MODEL         - Optional. Model (default: tts-1).
#     OPENAI_TTS_BASE_URL      - Optional. Base URL for OpenAI-compatible TTS API
#                                (default: https://api.openai.com). Use this to
#                                point at a local server such as Kokoro or Piper.
#     OPENAI_TTS_FORMAT        - Optional. Response format (default: omitted,
#                                letting the server choose). Set to "opus", "mp3",
#                                "wav", etc. when the server requires an explicit
#                                format. Note: some compatible servers ignore or
#                                error on this field.
#     LETTABOT_TTS_PAD_SECONDS - Optional. Seconds of silence to append via ffmpeg
#                                to prevent audio cutoff in some clients (e.g. 0.5).
#                                Requires ffmpeg. Skipped silently if not set.

set -euo pipefail

TEXT="${1:?Usage: lettabot-tts <text> [output_path]}"

# The session subprocess CWD is set to workingDir (bot.ts:642), which is the
# same base directory that <send-file> directives resolve from. This means
# $(pwd) and LETTABOT_WORKING_DIR produce paths in the correct coordinate space.
OUTBOUND_DIR="${LETTABOT_WORKING_DIR:-$(pwd)}/data/outbound"

PROVIDER="${TTS_PROVIDER:-elevenlabs}"

require_cmd() {
  if ! command -v "$1" >/dev/null 2>&1; then
    echo "Error: Required command '$1' is not installed or not on PATH" >&2
    exit 1
  fi
}

preflight() {
  require_cmd curl
  require_cmd jq
  if [ -n "${LETTABOT_TTS_PAD_SECONDS:-}" ]; then
    require_cmd ffmpeg
  fi
}

preflight

# Ensure output directory exists
mkdir -p "$OUTBOUND_DIR"

# Use collision-safe random filenames when output path is not explicitly provided.
if [ -n "${2:-}" ]; then
  OUTPUT="$2"
else
  # Clean stale voice files older than 1 hour
  find "$OUTBOUND_DIR" -name 'voice-*.ogg' -mmin +60 -delete 2>/dev/null || true
  OUTPUT=$(mktemp "${OUTBOUND_DIR}/voice-XXXXXXXXXX.ogg")
fi

# ---------------------------------------------------------------------------
# Provider: ElevenLabs
# ---------------------------------------------------------------------------
tts_elevenlabs() {
  if [ -z "${ELEVENLABS_API_KEY:-}" ]; then
    echo "Error: ELEVENLABS_API_KEY is not set" >&2
    exit 1
  fi

  local voice_id="${ELEVENLABS_VOICE_ID:-onwK4e9ZLuTAKqWW03F9}"
  local model_id="${ELEVENLABS_MODEL_ID:-eleven_multilingual_v2}"

  local http_code
  http_code=$(curl -sS -w "%{http_code}" -o "$OUTPUT" \
    "https://api.elevenlabs.io/v1/text-to-speech/${voice_id}" \
    -H "xi-api-key: ${ELEVENLABS_API_KEY}" \
    -H "Content-Type: application/json" \
    -d "$(jq -n \
      --arg text "$TEXT" \
      --arg model "$model_id" \
      '{
        text: $text,
        model_id: $model,
        output_format: "ogg_opus"
      }'
    )")

  if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
    echo "Error: ElevenLabs API returned HTTP $http_code (model=$model_id voice_id=$voice_id)" >&2
    if [ -s "$OUTPUT" ]; then
      echo "Error response preview:" >&2
      head -c 2000 "$OUTPUT" >&2 || true
      echo >&2
    fi
    rm -f "$OUTPUT"
    exit 1
  fi

  if [ ! -s "$OUTPUT" ]; then
    echo "Error: ElevenLabs TTS response was empty" >&2
    rm -f "$OUTPUT"
    exit 1
  fi
}

# ---------------------------------------------------------------------------
# Provider: OpenAI
# ---------------------------------------------------------------------------
tts_openai() {
  if [ -z "${OPENAI_API_KEY:-}" ]; then
    echo "Error: OPENAI_API_KEY is not set" >&2
    exit 1
  fi

  local voice="${OPENAI_TTS_VOICE:-alloy}"
  local model="${OPENAI_TTS_MODEL:-tts-1}"
  local base_url="${OPENAI_TTS_BASE_URL:-https://api.openai.com}"
  local fmt="${OPENAI_TTS_FORMAT:-}"

  # Build JSON body; omit response_format when not set so OpenAI-compatible
  # servers that don't support the field (e.g. Kokoro, Piper) work correctly.
  local body
  body=$(jq -n \
    --arg text "$TEXT" \
    --arg model "$model" \
    --arg voice "$voice" \
    --arg fmt "$fmt" \
    'if $fmt != "" then {model: $model, input: $text, voice: $voice, response_format: $fmt}
     else              {model: $model, input: $text, voice: $voice} end')

  local http_code
  http_code=$(curl -sS -w "%{http_code}" -o "$OUTPUT" \
    "${base_url}/v1/audio/speech" \
    -H "Authorization: Bearer ${OPENAI_API_KEY}" \
    -H "Content-Type: application/json" \
    -d "$body")

  if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
    echo "Error: OpenAI TTS API returned HTTP $http_code (model=$model voice=$voice)" >&2
    if [ -s "$OUTPUT" ]; then
      echo "Error response preview:" >&2
      head -c 2000 "$OUTPUT" >&2 || true
      echo >&2
    fi
    rm -f "$OUTPUT"
    exit 1
  fi

  if [ ! -s "$OUTPUT" ]; then
    echo "Error: OpenAI TTS response was empty" >&2
    rm -f "$OUTPUT"
    exit 1
  fi
}

# ---------------------------------------------------------------------------
# Dispatch
# ---------------------------------------------------------------------------
case "$PROVIDER" in
  elevenlabs) tts_elevenlabs ;;
  openai)     tts_openai ;;
  *)
    echo "Error: Unknown TTS_PROVIDER: $PROVIDER (supported: elevenlabs, openai)" >&2
    exit 1
    ;;
esac

# ---------------------------------------------------------------------------
# Optional: pad trailing silence to prevent audio cutoff in some clients.
# Set LETTABOT_TTS_PAD_SECONDS (e.g. "0.5") to enable. Requires ffmpeg.
# ---------------------------------------------------------------------------
if [ -n "${LETTABOT_TTS_PAD_SECONDS:-}" ]; then
  PAD_TMP=$(mktemp "${OUTPUT%.ogg}_pad_XXXXXX.ogg")
  if ffmpeg -y -i "$OUTPUT" -af "apad=pad_dur=${LETTABOT_TTS_PAD_SECONDS}" \
       -c:a libopus -b:a 64k "$PAD_TMP" 2>/dev/null; then
    mv "$PAD_TMP" "$OUTPUT"
  else
    rm -f "$PAD_TMP"
    echo "Warning: ffmpeg padding failed, using unpadded audio" >&2
  fi
fi

echo "$OUTPUT"
