// SPDX-License-Identifier: AGPL-3.0-only
// Copyright (C) 2026 Giancarlo Erra - Altaire Limited
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import {
  getEmbeddingConfig,
  loadEmbeddingConfig,
  resetEmbeddingConfig,
} from "../../src/services/embedding-config.js";

describe("embedding-config", () => {
  const originalEnv = { ...process.env };

  beforeEach(() => {
    resetEmbeddingConfig();
    // Clear all embedding-related env vars
    delete process.env.EMBEDDING_PROVIDER;
    delete process.env.OLLAMA_MODE;
    delete process.env.OLLAMA_URL;
    delete process.env.EMBEDDING_MODEL;
    delete process.env.EMBEDDING_DIMENSIONS;
    delete process.env.EMBEDDING_CONTEXT_LENGTH;
    delete process.env.OLLAMA_API_KEY;
    delete process.env.OPENAI_API_KEY;
    delete process.env.GOOGLE_API_KEY;
  });

  afterEach(() => {
    resetEmbeddingConfig();
    // Restore original env
    process.env = { ...originalEnv };
  });

  describe("defaults (no env vars)", () => {
    it("defaults to ollama provider", () => {
      const config = loadEmbeddingConfig();
      expect(config.embeddingProvider).toBe("ollama");
    });

    it("defaults to auto mode", () => {
      const config = loadEmbeddingConfig();
      expect(config.ollamaMode).toBe("auto");
    });

    it("defaults URL to localhost:11434 in auto mode", () => {
      const config = loadEmbeddingConfig();
      expect(config.ollamaUrl).toBe("http://localhost:11434");
    });

    it("defaults model to nomic-embed-text", () => {
      const config = loadEmbeddingConfig();
      expect(config.embeddingModel).toBe("nomic-embed-text");
    });

    it("defaults dimensions to 768", () => {
      const config = loadEmbeddingConfig();
      expect(config.embeddingDimensions).toBe(768);
    });

    it("has no API key by default", () => {
      const config = loadEmbeddingConfig();
      expect(config.ollamaApiKey).toBeUndefined();
    });
  });

  describe("docker mode", () => {
    it("uses docker URL default when OLLAMA_MODE=docker", () => {
      process.env.OLLAMA_MODE = "docker";
      const config = loadEmbeddingConfig();
      expect(config.ollamaMode).toBe("docker");
      expect(config.ollamaUrl).toBe("http://localhost:11435");
    });

    it("allows overriding URL in docker mode", () => {
      process.env.OLLAMA_MODE = "docker";
      process.env.OLLAMA_URL = "http://custom:9999";
      const config = loadEmbeddingConfig();
      expect(config.ollamaUrl).toBe("http://custom:9999");
    });
  });

  describe("external mode", () => {
    it("uses external URL default when OLLAMA_MODE=external", () => {
      process.env.OLLAMA_MODE = "external";
      const config = loadEmbeddingConfig();
      expect(config.ollamaMode).toBe("external");
      expect(config.ollamaUrl).toBe("http://localhost:11434");
    });

    it("allows overriding URL in external mode", () => {
      process.env.OLLAMA_MODE = "external";
      process.env.OLLAMA_URL = "http://gpu-server:11434";
      const config = loadEmbeddingConfig();
      expect(config.ollamaUrl).toBe("http://gpu-server:11434");
    });
  });

  describe("model and dimensions overrides", () => {
    it("allows custom model", () => {
      process.env.EMBEDDING_MODEL = "mxbai-embed-large";
      const config = loadEmbeddingConfig();
      expect(config.embeddingModel).toBe("mxbai-embed-large");
    });

    it("allows custom dimensions", () => {
      process.env.EMBEDDING_DIMENSIONS = "1024";
      const config = loadEmbeddingConfig();
      expect(config.embeddingDimensions).toBe(1024);
    });

    it("parses dimensions as integer", () => {
      process.env.EMBEDDING_DIMENSIONS = "512";
      const config = loadEmbeddingConfig();
      expect(config.embeddingDimensions).toBe(512);
      expect(typeof config.embeddingDimensions).toBe("number");
    });
  });

  describe("API key", () => {
    it("reads OLLAMA_API_KEY when set", () => {
      process.env.OLLAMA_API_KEY = "test-key-123";
      const config = loadEmbeddingConfig();
      expect(config.ollamaApiKey).toBe("test-key-123");
    });

    it("is undefined when not set", () => {
      const config = loadEmbeddingConfig();
      expect(config.ollamaApiKey).toBeUndefined();
    });
  });

  describe("invalid mode", () => {
    it("throws for invalid OLLAMA_MODE", () => {
      process.env.OLLAMA_MODE = "invalid";
      expect(() => loadEmbeddingConfig()).toThrow(
        'Invalid OLLAMA_MODE: "invalid". Must be "docker", "external", or "auto".',
      );
    });
  });

  describe("singleton behavior", () => {
    it("caches config after first load", () => {
      const first = loadEmbeddingConfig();
      // Change env — should NOT affect cached config
      process.env.OLLAMA_MODE = "external";
      const second = loadEmbeddingConfig();
      expect(second).toBe(first); // same reference
      expect(second.ollamaMode).toBe("auto");
    });

    it("getEmbeddingConfig returns same as loadEmbeddingConfig", () => {
      const loaded = loadEmbeddingConfig();
      const got = getEmbeddingConfig();
      expect(got).toBe(loaded);
    });

    it("resetEmbeddingConfig clears cache", () => {
      const first = loadEmbeddingConfig();
      expect(first.ollamaMode).toBe("auto");

      resetEmbeddingConfig();
      process.env.OLLAMA_MODE = "external";
      const second = loadEmbeddingConfig();
      expect(second.ollamaMode).toBe("external");
      expect(second).not.toBe(first);
    });
  });

  describe("full external config", () => {
    it("handles complete external config with all options", () => {
      process.env.OLLAMA_MODE = "external";
      process.env.OLLAMA_URL = "http://remote-gpu:11434";
      process.env.EMBEDDING_MODEL = "mxbai-embed-large";
      process.env.EMBEDDING_DIMENSIONS = "1024";
      process.env.OLLAMA_API_KEY = "secret";

      const config = loadEmbeddingConfig();
      expect(config).toEqual({
        embeddingProvider: "ollama",
        ollamaMode: "external",
        ollamaUrl: "http://remote-gpu:11434",
        embeddingModel: "mxbai-embed-large",
        embeddingDimensions: 1024,
        embeddingContextLength: 512,
        ollamaApiKey: "secret",
      });
    });
  });

  describe("embedding provider selection", () => {
    it("defaults to ollama when EMBEDDING_PROVIDER is not set", () => {
      const config = loadEmbeddingConfig();
      expect(config.embeddingProvider).toBe("ollama");
      expect(config.embeddingModel).toBe("nomic-embed-text");
      expect(config.embeddingDimensions).toBe(768);
    });

    it("selects openai provider with correct defaults", () => {
      process.env.EMBEDDING_PROVIDER = "openai";
      const config = loadEmbeddingConfig();
      expect(config.embeddingProvider).toBe("openai");
      expect(config.embeddingModel).toBe("text-embedding-3-small");
      expect(config.embeddingDimensions).toBe(1536);
      expect(config.embeddingContextLength).toBe(8191);
    });

    it("selects google provider with correct defaults", () => {
      process.env.EMBEDDING_PROVIDER = "google";
      const config = loadEmbeddingConfig();
      expect(config.embeddingProvider).toBe("google");
      expect(config.embeddingModel).toBe("gemini-embedding-001");
      expect(config.embeddingDimensions).toBe(3072);
      expect(config.embeddingContextLength).toBe(2048);
    });

    it("throws for invalid EMBEDDING_PROVIDER", () => {
      process.env.EMBEDDING_PROVIDER = "anthropic";
      expect(() => loadEmbeddingConfig()).toThrow(
        'Invalid EMBEDDING_PROVIDER: "anthropic". Must be "ollama", "openai", or "google".',
      );
    });

    it("allows overriding model for openai provider", () => {
      process.env.EMBEDDING_PROVIDER = "openai";
      process.env.EMBEDDING_MODEL = "text-embedding-3-large";
      const config = loadEmbeddingConfig();
      expect(config.embeddingModel).toBe("text-embedding-3-large");
      expect(config.embeddingContextLength).toBe(8191);
    });

    it("allows overriding dimensions for openai provider", () => {
      process.env.EMBEDDING_PROVIDER = "openai";
      process.env.EMBEDDING_DIMENSIONS = "256";
      const config = loadEmbeddingConfig();
      expect(config.embeddingDimensions).toBe(256);
    });

    it("uses context length for unknown model", () => {
      process.env.EMBEDDING_PROVIDER = "openai";
      process.env.EMBEDDING_MODEL = "custom-embed-model";
      const config = loadEmbeddingConfig();
      expect(config.embeddingContextLength).toBe(0);
    });

    it("allows explicit context length override for any provider", () => {
      process.env.EMBEDDING_PROVIDER = "google";
      process.env.EMBEDDING_CONTEXT_LENGTH = "4096";
      const config = loadEmbeddingConfig();
      expect(config.embeddingContextLength).toBe(4096);
    });
  });
});
