import { test, describe } from "node:test";
import { strict as assert } from "node:assert";
import { PRICING, calcCost, buildAnalysisPrompt } from "../gemini-helpers.js";

describe("PRICING table", () => {
  test("includes all current production model slugs", () => {
    assert.ok(PRICING["gemini-3.1-pro-preview"]);
    assert.ok(PRICING["gemini-2.5-pro"]);
    assert.ok(PRICING["gemini-3-flash-preview"]);
    assert.ok(PRICING["gemini-2.5-flash"]);
    assert.ok(PRICING["gemini-3.1-flash-lite"]);
  });

  test("has a 'default' fallback row", () => {
    assert.ok(PRICING["default"]);
    assert.equal(typeof PRICING["default"].input, "number");
  });

  test("each row has input/output/thinking rates", () => {
    for (const [model, rates] of Object.entries(PRICING)) {
      assert.equal(typeof rates.input, "number", `${model}.input`);
      assert.equal(typeof rates.output, "number", `${model}.output`);
      assert.equal(typeof rates.thinking, "number", `${model}.thinking`);
    }
  });
});

describe("calcCost", () => {
  test("uses model-specific rates when known", () => {
    const cost = calcCost("gemini-3-flash-preview", {
      promptTokenCount: 1_000_000,
      candidatesTokenCount: 1_000_000,
      thoughtsTokenCount: 0,
    });
    assert.equal(cost.inputTokens, 1_000_000);
    assert.equal(cost.outputTokens, 1_000_000);
    // 1M tokens × $0.50 input + 1M × $3.00 output = $3.50
    assert.equal(parseFloat(cost.totalCost).toFixed(2), "3.50");
  });

  test("falls back to 'default' rates for unknown model", () => {
    const cost = calcCost("invented-model-x", {
      promptTokenCount: 1_000_000,
      candidatesTokenCount: 0,
      thoughtsTokenCount: 0,
    });
    // default input rate = $1.00 per 1M
    assert.equal(parseFloat(cost.totalCost).toFixed(2), "1.00");
  });

  test("treats missing usage fields as zero", () => {
    const cost = calcCost("gemini-3-flash-preview", {});
    assert.equal(cost.inputTokens, 0);
    assert.equal(cost.outputTokens, 0);
    assert.equal(cost.thinkingTokens, 0);
    assert.equal(parseFloat(cost.totalCost), 0);
  });

  test("formats sub-cent totals as ¢", () => {
    const cost = calcCost("gemini-2.5-flash", {
      promptTokenCount: 1000,
      candidatesTokenCount: 1000,
      thoughtsTokenCount: 0,
    });
    // tiny — display in ¢
    assert.match(cost.totalCostDisplay, /¢$/);
  });

  test("formats >$0.01 totals as $X.XXXX", () => {
    const cost = calcCost("gemini-3.1-pro-preview", {
      promptTokenCount: 1_000_000,
      candidatesTokenCount: 0,
      thoughtsTokenCount: 0,
    });
    // $2.00
    assert.match(cost.totalCostDisplay, /^\$\d+\.\d{4}$/);
  });

  test("totalTokens prefers usage.totalTokenCount when provided", () => {
    const cost = calcCost("gemini-3-flash-preview", {
      promptTokenCount: 100,
      candidatesTokenCount: 50,
      thoughtsTokenCount: 25,
      totalTokenCount: 200, // server-reported total may include hidden tokens
    });
    assert.equal(cost.totalTokens, 200);
  });

  test("totalTokens falls back to sum when totalTokenCount missing", () => {
    const cost = calcCost("gemini-3-flash-preview", {
      promptTokenCount: 100,
      candidatesTokenCount: 50,
      thoughtsTokenCount: 25,
    });
    assert.equal(cost.totalTokens, 175);
  });
});

describe("buildAnalysisPrompt", () => {
  test("numbers entries with timestamp + offset", () => {
    const entries = [
      { offset: 0, text: "Welcome", duration: 5 },
      { offset: 30, text: "Topic two", duration: 60 },
    ];
    const prompt = buildAnalysisPrompt(entries);
    assert.match(prompt, /\[0\] \(0:00\) Welcome/);
    assert.match(prompt, /\[1\] \(0:30\) Topic two/);
  });

  test("substitutes the last segment index in the constraint", () => {
    const entries = [{ offset: 0, text: "x", duration: 1 }];
    const prompt = buildAnalysisPrompt(entries);
    assert.match(prompt, /from 0 to 0 must belong/);
  });

  test("handles long-running content (hours)", () => {
    const entries = [
      { offset: 0, text: "start", duration: 1 },
      { offset: 3661, text: "later", duration: 1 },
    ];
    const prompt = buildAnalysisPrompt(entries);
    assert.match(prompt, /\(1:01:01\) later/);
  });

  test("emits a JSON-output instruction", () => {
    const prompt = buildAnalysisPrompt([{ offset: 0, text: "x", duration: 1 }]);
    assert.match(prompt, /Respond with ONLY valid JSON/);
    assert.match(prompt, /"sections":/);
    assert.match(prompt, /"startIndex":/);
    assert.match(prompt, /"endIndex":/);
  });
});
