import { describe, expect, test } from "bun:test";
import {
	appendImagesDeduped,
	imageFileKey,
} from "@/lib/multi-image";

// ---------------------------------------------------------------------------
// Multi-image helpers – behavioral tests
//
// These functions are pure; they own the "append + dedup" contract.
// Tests describe what the system DOES, not how it's implemented.
// ---------------------------------------------------------------------------

describe("imageFileKey – stable identity for dedup", () => {
	test("returns a string combining name and size", () => {
		const file = new File(["hello"], "flyer.png", {
			type: "image/png",
			lastModified: 123,
		});
		const key = imageFileKey(file);
		expect(typeof key).toBe("string");
		expect(key).toContain("flyer.png");
		expect(key).toContain(String(file.size));
		expect(key).toContain(String(file.lastModified));
	});

	test("two files with the same name, size, and lastModified produce the same key", () => {
		const a = new File(["hello"], "a.png", {
			type: "image/png",
			lastModified: 123,
		});
		const b = new File(["hello"], "a.png", {
			type: "image/png",
			lastModified: 123,
		});
		expect(imageFileKey(a)).toBe(imageFileKey(b));
	});

	test("two files with the same name but different content produce different keys", () => {
		const a = new File(["hello"], "a.png", {
			type: "image/png",
			lastModified: 123,
		});
		const b = new File(["hello world"], "a.png", {
			type: "image/png",
			lastModified: 123,
		});
		expect(imageFileKey(a)).not.toBe(imageFileKey(b));
	});

	test("two files with different names but same content produce different keys", () => {
		const a = new File(["hello"], "a.png", {
			type: "image/png",
			lastModified: 123,
		});
		const b = new File(["hello"], "b.png", {
			type: "image/png",
			lastModified: 123,
		});
		expect(imageFileKey(a)).not.toBe(imageFileKey(b));
	});

	test("clipboard fallback files with the same fallback name and size stay distinct when lastModified differs", () => {
		const a = new File(["abcd"], "clipboard-image", {
			type: "image/png",
			lastModified: 100,
		});
		const b = new File(["wxyz"], "clipboard-image", {
			type: "image/png",
			lastModified: 101,
		});

		expect(a.size).toBe(b.size);
		expect(imageFileKey(a)).not.toBe(imageFileKey(b));
	});
});

describe("appendImagesDeduped – append with deduplication", () => {
	const makeFile = (
		name: string,
		content = "data",
		lastModified = 123,
	) => new File([content], name, { type: "image/png", lastModified });

	test("appends new files to an empty list", () => {
		const result = appendImagesDeduped([], [makeFile("a.png")]);
		expect(result).toHaveLength(1);
		expect(result[0].name).toBe("a.png");
	});

	test("appends new files to an existing list", () => {
		const existing = [makeFile("a.png")];
		const incoming = [makeFile("b.png")];
		const result = appendImagesDeduped(existing, incoming);
		expect(result).toHaveLength(2);
		expect(result.map((f) => f.name)).toEqual(["a.png", "b.png"]);
	});

	test("silently ignores incoming files that are exact duplicates (same name+size)", () => {
		const existing = [makeFile("a.png")];
		const incoming = [makeFile("a.png")]; // identical name + content = same size
		const result = appendImagesDeduped(existing, incoming);
		expect(result).toHaveLength(1);
		expect(result[0].name).toBe("a.png");
	});

	test("appends non-duplicate files even when some duplicates are in the batch", () => {
		const existing = [makeFile("a.png")];
		const incoming = [makeFile("a.png"), makeFile("b.png")];
		const result = appendImagesDeduped(existing, incoming);
		expect(result).toHaveLength(2);
		expect(result.map((f) => f.name)).toEqual(["a.png", "b.png"]);
	});

	test("preserves existing list order — new files are appended at the end", () => {
		const existing = [makeFile("first.png"), makeFile("second.png")];
		const incoming = [makeFile("third.png")];
		const result = appendImagesDeduped(existing, incoming);
		expect(result.map((f) => f.name)).toEqual([
			"first.png",
			"second.png",
			"third.png",
		]);
	});

	test("returns a new array (does not mutate the existing list)", () => {
		const existing = [makeFile("a.png")];
		const incoming = [makeFile("b.png")];
		const result = appendImagesDeduped(existing, incoming);
		expect(result).not.toBe(existing); // new reference
		expect(existing).toHaveLength(1); // original untouched
	});

	test("handles multiple incoming files with internal duplicates", () => {
		// Two identical files in the same incoming batch
		const existing: File[] = [];
		const incoming = [makeFile("a.png"), makeFile("a.png"), makeFile("b.png")];
		const result = appendImagesDeduped(existing, incoming);
		expect(result).toHaveLength(2);
		expect(result.map((f) => f.name)).toEqual(["a.png", "b.png"]);
	});

	test("keeps distinct clipboard fallback files that share the same name and byte size", () => {
		const existing: File[] = [];
		const incoming = [
			makeFile("clipboard-image", "abcd", 100),
			makeFile("clipboard-image", "wxyz", 101),
		];

		const result = appendImagesDeduped(existing, incoming);

		expect(result).toHaveLength(2);
		expect(result[0].lastModified).toBe(100);
		expect(result[1].lastModified).toBe(101);
	});
});
