import { describe, it, expect } from "vitest";
import { GeminiAamosClient, MemoryBudgetStore } from "../src/gemini.js";
import type { AamosRequestEnvelope } from "../src/tasks.js";

const FIXTURE_RESPONSE = {
  candidates: [
    {
      content: {
        parts: [
          {
            text: JSON.stringify({
              items: [
                {
                  produkt: "Mellanmjölk 1,5% fett",
                  varumarke: "Arla",
                  kvantitet: 1,
                  enhet: "liter",
                  kategori: "Mejeri",
                  bastaFore: null,
                  sistaForbruk: null,
                  konfidens: 0.98,
                },
                {
                  produkt: "Smör",
                  varumarke: "Arla",
                  kvantitet: "500 g",
                  enhet: "g",
                  kategori: "Mejeri",
                  bastaFore: null,
                  konfidens: 0.95,
                },
              ],
              imageQualityIssues: [],
            }),
          },
        ],
      },
    },
  ],
  usageMetadata: { promptTokenCount: 392, candidatesTokenCount: 121 },
};

function makeFetch(imageBytes: Buffer): typeof fetch {
  return async (url: string | URL | Request, init?: RequestInit) => {
    const urlStr = url.toString();
    if (urlStr.includes("/mock-s3/")) {
      return new Response(imageBytes, { status: 200, headers: { "content-type": "image/jpeg" } });
    }
    if (urlStr.includes("/models/") && init?.method === "POST") {
      return new Response(JSON.stringify(FIXTURE_RESPONSE), { status: 200, headers: { "content-type": "application/json" } });
    }
    return new Response("not found", { status: 404 });
  };
}

// A tiny 1x1 JPEG.
const JPEG_BYTES = Buffer.from(
  "/9j/4AAQSkZJRgABAQEASABIAAD/2wBDAP//////////////////////////////////////////////////////////////////////////////////////wAALCAABAAEBAREA/8QAFAABAAAAAAAAAAAAAAAAAAAAA//EABQQAQAAAAAAAAAAAAAAAAAAAAD/2gAIAQEAAD8Af//Z",
  "base64",
);

describe("GeminiAamosClient", () => {
  it("analyzes fridge image with recorded fixture", async () => {
    const budget = new MemoryBudgetStore();
    const client = new GeminiAamosClient({
      apiKey: "test-key",
      model: "gemini-2.5-flash",
      fetchImpl: makeFetch(JPEG_BYTES),
      budgetStore: budget,
      dailyBudgetUsd: 10,
    });

    const result = await client.runTask("ANALYZE_FRIDGE_IMAGE", {
      imageUrls: ["http://localhost/v1/mock-s3/fridge.jpg"],
      locationType: "fridge",
      marketLocale: "sv-SE",
      knownItems: [],
    });

    expect(result.status).toBe("ok");
    expect(result.output).not.toBeNull();
    expect(result.modelVersion).toBe("gemini-2.5-flash");
    expect(result.costUsd).toBeGreaterThan(0);
    expect(result.inputTokens).toBe(392);
    expect(result.outputTokens).toBe(121);

    expect(result.output).not.toBeNull();
    const items = result.output!.items;
    expect(items).toHaveLength(2);
    expect(items[0]?.detectedName).toBe("Mellanmjölk 1,5% fett");
    expect(items[0]?.brand).toBe("Arla");
    expect(items[0]?.estimatedQuantity).toBe(1);
    expect(items[0]?.unit).toBe("LITER");
    expect(items[0]?.confidence).toBe(0.98);
    expect(items[1]?.detectedName).toBe("Smör");
    expect(items[1]?.estimatedQuantity).toBe(500);
    expect(items[1]?.unit).toBe("GRAM");

    const spend = await budget.getDailySpendUsd();
    expect(spend).toBe(result.costUsd);
  });

  it("blocks call when daily budget exceeded", async () => {
    const budget = new MemoryBudgetStore();
    const client = new GeminiAamosClient({
      apiKey: "test-key",
      fetchImpl: makeFetch(JPEG_BYTES),
      budgetStore: budget,
      dailyBudgetUsd: 0.00001, // essentially zero
    });

    const result = await client.runTask("ANALYZE_FRIDGE_IMAGE", {
      imageUrls: ["http://localhost/v1/mock-s3/fridge.jpg"],
      locationType: "fridge",
      marketLocale: "sv-SE",
      knownItems: [],
    });

    expect(result.status).toBe("failed");
    expect(result.error).toContain("budget");
  });

  it("rejects unsupported task types", async () => {
    const client = new GeminiAamosClient({
      apiKey: "test-key",
      fetchImpl: makeFetch(JPEG_BYTES),
    });

    const result = await client.runTask(
      "READ_RECEIPT",
      { imageUrls: ["http://localhost/v1/mock-s3/receipt.jpg"], marketLocale: "sv-SE" },
    );

    expect(result.status).toBe("failed");
    expect(result.error).toContain("READ_RECEIPT");
  });

  it("validates input before calling Gemini", async () => {
    const client = new GeminiAamosClient({
      apiKey: "test-key",
      fetchImpl: makeFetch(JPEG_BYTES),
    });

    const result = await client.runTask("ANALYZE_FRIDGE_IMAGE", {
      imageUrls: [],
      locationType: "fridge",
      marketLocale: "sv-SE",
      knownItems: [],
    } as unknown as { imageUrls: string[]; locationType: string; marketLocale: string; knownItems: string[] });

    expect(result.status).toBe("failed");
    expect(result.error).toContain("Kontraktsfel");
  });
});
