/**
 * LocalEmbedding Unit Tests
 *
 * Tests for LocalEmbedding class including:
 * - Basic class structure
 * - Vector normalization logic
 * - Error handling patterns
 */

import { describe, expect, beforeEach, afterEach, vi, it } from "vitest";

describe("LocalEmbedding", () => {
  beforeEach(() => {
    vi.resetAllMocks();
  });

  afterEach(() => {
    vi.unstubAllGlobals();
  });

  it("exports LocalEmbedding class", async () => {
    const { LocalEmbedding } = await import("./index.js");
    expect(LocalEmbedding).toBeDefined();
    expect(typeof LocalEmbedding).toBe("function");
  });

  it("creates LocalEmbedding instance with correct parameters", async () => {
    const { LocalEmbedding } = await import("./index.js");
    const embedding = new LocalEmbedding("model.gguf", "/cache");
    expect(embedding).toBeDefined();
  });

  it("creates LocalEmbedding instance without cache directory", async () => {
    const { LocalEmbedding } = await import("./index.js");
    const embedding = new LocalEmbedding("model.gguf");
    expect(embedding).toBeDefined();
  });

  it("has embed method", async () => {
    const { LocalEmbedding } = await import("./index.js");
    const embedding = new LocalEmbedding("model.gguf");
    expect(typeof embedding.embed).toBe("function");
  });

  it("normalizes vector to unit magnitude", async () => {
    // Test the normalization logic directly
    const vector = [3, 4];
    const magnitude = Math.sqrt(vector.reduce((sum, x) => sum + x * x, 0));
    const normalized = vector.map((val) => val / magnitude);

    const normalizedMagnitude = Math.sqrt(normalized.reduce((sum, x) => sum + x * x, 0));
    expect(normalizedMagnitude).toBeCloseTo(1.0, 10);
  });

  it("handles zero vector without division by zero", async () => {
    // Test zero vector handling
    const zeroVector = [0, 0, 0, 0];
    const magnitude = Math.sqrt(zeroVector.reduce((sum, x) => sum + x * x, 0));

    // When magnitude is 0, we should not divide
    const result = magnitude > 0 ? zeroVector.map((val) => val / magnitude) : zeroVector;
    expect(result).toEqual([0, 0, 0, 0]);
  });

  it("sanitizes non-finite values", async () => {
    // Test sanitization of NaN and Infinity
    const vector = [1, Number.NaN, Number.POSITIVE_INFINITY, Number.NEGATIVE_INFINITY];
    const sanitized = vector.map((val) => (Number.isFinite(val) ? val : 0));
    expect(sanitized).toEqual([1, 0, 0, 0]);
  });

  it("sanitizes vector before normalization", async () => {
    // Combined test: sanitize then normalize
    const vector = [3, Number.NaN, 4, 0];
    const sanitized = vector.map((val) => (Number.isFinite(val) ? val : 0));
    const magnitude = Math.sqrt(sanitized.reduce((sum, x) => sum + x * x, 0));
    const normalized = sanitized.map((val) => val / magnitude);

    const normalizedMagnitude = Math.sqrt(normalized.reduce((sum, x) => sum + x * x, 0));
    expect(normalizedMagnitude).toBeCloseTo(1.0, 10);
    expect(normalized.every((val) => Number.isFinite(val))).toBe(true);
  });
});
