import { describe, it, expect } from "vitest";
import { GeminiAamosClient, MemoryBudgetStore, parseDate, isPlausibleFoodDate } 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-3.1-flash-lite",
      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-3.1-flash-lite");
    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("larmar men blockerar INTE när dagsbudgeten överskrids", async () => {
    const budget = new MemoryBudgetStore();
    const client = new GeminiAamosClient({
      apiKey: "test-key",
      fetchImpl: makeFetch(JPEG_BYTES),
      budgetStore: budget,
      dailyBudgetUsd: 0.00001, // i princip noll – men budgeten ska ALDRIG stoppa en skanning
    });

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

    // Produktbeslut (MIKRO 12): ingen dygnsspärr. Överskriden budget larmar i loggen
    // men skanningen fortsätter och lyckas — annars slutar appen fungera för användaren.
    expect(result.status).toBe("ok");
    expect(result.output).not.toBeNull();
  });

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

    const result = await client.runTask("DEDUPLICATE_INVENTORY", { items: [] });

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

  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");
  });
});

describe("parseDate (svenska bäst-före-stämplar)", () => {
  it("läser 2-siffrigt år och kompakta format", () => {
    expect(parseDate("25-08-30")).toBe("2025-08-30"); // YY-MM-DD
    expect(parseDate("250830")).toBe("2025-08-30"); // YYMMDD
    expect(parseDate("20250830")).toBe("2025-08-30"); // YYYYMMDD
    expect(parseDate("2025-08-30")).toBe("2025-08-30"); // ISO
    expect(parseDate("30-08-2025")).toBe("2025-08-30"); // DD-MM-YYYY
    expect(parseDate("skräp")).toBeNull();
  });
});

describe("isPlausibleFoodDate (rimlighetsspärr mot feltolkade årtal)", () => {
  const TODAY = new Date("2026-08-18T00:00:00Z");
  it("släpper igenom rimliga datum (nära i tid och framåt)", () => {
    expect(isPlausibleFoodDate("2026-09-04", TODAY)).toBe(true); // ~2 v framåt
    expect(isPlausibleFoodDate("2026-08-01", TODAY)).toBe(true); // nyss passerat
    expect(isPlausibleFoodDate("2027-06-01", TODAY)).toBe(true); // torrvara framåt
  });
  it("kastar orimliga datum – grovt feltolkat årtal", () => {
    // Grädden i fälttestet: stämpel lästes som 2024 → ~2 år bak = feltolkning, inte utgången vara.
    expect(isPlausibleFoodDate("2024-09-26", TODAY)).toBe(false);
    expect(isPlausibleFoodDate("2035-01-01", TODAY)).toBe(false); // orimligt långt fram
    expect(isPlausibleFoodDate(null, TODAY)).toBe(false);
    expect(isPlausibleFoodDate("skräp", TODAY)).toBe(false);
  });
});
