/**
 * AzureEmbeddingProvider — talks to Azure OpenAI Embeddings via the
 * Loxia backend proxy at `${baseUrl}/llm/embed`.
 *
 * Why through the backend (not direct to Azure):
 *   - Same auth path as chat: ApiKeyManager holds the Loxia key, the
 *     backend holds the Azure key. No raw Azure credentials in the CLI.
 *   - Brand routing happens server-side: autopilot and onbuzz both
 *     point at the same shared embedding deployment per current
 *     architectural decision (single endpoint for both brands).
 *   - Lets us swap models / deployments without redeploying the CLI.
 *
 * Backend contract (must match autopilot-backend `/llm/embed` route):
 *
 *   POST ${baseUrl}/llm/embed
 *   Authorization: Bearer <loxiaApiKey>
 *   Content-Type:  application/json
 *
 *   { "input": ["text 1", "text 2"], "model": "text-embedding-3-small" }
 *
 *   →  { "data": [{ "embedding": [0.1, …] }, …],
 *        "usage": { "prompt_tokens": N, "total_tokens": N },
 *        "model": "text-embedding-3-small" }
 *
 * NOTE: The backend `/llm/embed` route is the server-side counterpart
 * that proxies to Azure OpenAI Embeddings. If it doesn't exist yet,
 * this provider will fail with PROVIDER_UNAVAILABLE on the first call.
 * That's intentional — surface the missing infrastructure clearly.
 */

import { EmbeddingProvider, EmbeddingError, EMBEDDING_ERROR_CODES } from '../providerInterface.js';
import { postEmbeddings, extractOpenAIShape } from './cloudHttpTransport.js';

/**
 * Default Azure embedding model. Loxia routes both autopilot + onbuzz
 * to the same deployment per the shared-endpoint decision; the deployment
 * name lives on the backend side.
 */
const DEFAULT_MODEL = 'text-embedding-3-small';
const DEFAULT_DIMS = 1536;
const DEFAULT_MAX_BATCH = 16;
const DEFAULT_MAX_TOKENS = 8191;

export class AzureEmbeddingProvider extends EmbeddingProvider {
  /**
   * @param {object} opts
   * @param {string} opts.baseUrl    - Loxia backend base URL (matches aiService).
   * @param {() => string|null} opts.getApiKey  - Callback returning the current
   *        Loxia API key. Callback (not stored string) so key rotation in the
   *        ApiKeyManager is picked up immediately without re-instantiating.
   * @param {string} [opts.modelId]
   * @param {number} [opts.dimensions]
   * @param {number} [opts.maxBatchSize]
   * @param {number} [opts.timeoutMs]
   * @param {typeof fetch} [opts.fetchImpl] - Injectable for tests.
   */
  constructor({
    baseUrl,
    getApiKey,
    modelId = DEFAULT_MODEL,
    dimensions = DEFAULT_DIMS,
    maxBatchSize = DEFAULT_MAX_BATCH,
    timeoutMs,
    fetchImpl,
  }) {
    super();
    if (!baseUrl) {
      throw new Error('AzureEmbeddingProvider requires a baseUrl');
    }
    if (typeof getApiKey !== 'function') {
      throw new Error('AzureEmbeddingProvider requires a getApiKey() callback');
    }
    this._baseUrl = baseUrl.replace(/\/+$/, '');
    this._getApiKey = getApiKey;
    this._modelId = modelId;
    this._dimensions = dimensions;
    this._maxBatchSize = maxBatchSize;
    this._timeoutMs = timeoutMs;
    this._fetchImpl = fetchImpl;
  }

  getInfo() {
    return {
      name: 'azure',
      modelId: this._modelId,
      dimensions: this._dimensions,
      maxBatchSize: this._maxBatchSize,
      maxTokensPerText: DEFAULT_MAX_TOKENS,
      distinguishesQueryAndDocument: false, // OpenAI-family models don't
      isLocal: false,
    };
  }

  async health() {
    const key = this._getApiKey();
    if (!key) return { ok: false, reason: 'No Loxia API key available — sign in or set one in Settings.' };
    return { ok: true };
  }

  async _embed(texts, _kind) {
    const apiKey = this._getApiKey();
    if (!apiKey) {
      throw new EmbeddingError(
        EMBEDDING_ERROR_CODES.AUTH_ERROR,
        'No Loxia API key available — sign in or set one in Settings.',
        { provider: 'azure', transient: false },
      );
    }
    return postEmbeddings({
      url: `${this._baseUrl}/llm/embed`,
      headers: {
        Authorization: `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
      },
      body: { input: texts, model: this._modelId },
      providerName: 'azure',
      extractVectors: extractOpenAIShape,
      timeoutMs: this._timeoutMs,
      fetchImpl: this._fetchImpl,
    });
  }
}
