import { beforeEach, describe, expect, mock, test } from "bun:test";

// Mock the S3 client send method
const mockSend = mock(() => Promise.resolve({}));
const mockGetSignedUrl = mock(() =>
	Promise.resolve("https://s3.example.com/gearbox-images/test.jpg?signed=1"),
);

// Mock modules before importing the service
mock.module("@aws-sdk/client-s3", () => ({
	S3Client: class MockS3Client {
		send = mockSend;
	},
	PutObjectCommand: class MockPutObjectCommand {
		input: Record<string, unknown>;
		constructor(input: Record<string, unknown>) {
			this.input = input;
		}
	},
	DeleteObjectCommand: class MockDeleteObjectCommand {
		input: Record<string, unknown>;
		constructor(input: Record<string, unknown>) {
			this.input = input;
		}
	},
	GetObjectCommand: class MockGetObjectCommand {
		input: Record<string, unknown>;
		constructor(input: Record<string, unknown>) {
			this.input = input;
		}
	},
}));

mock.module("@aws-sdk/s3-request-presigner", () => ({
	getSignedUrl: mockGetSignedUrl,
}));

// Set env vars before importing the service
process.env.S3_ENDPOINT = "http://localhost:3900";
process.env.S3_ACCESS_KEY = "test-access-key";
process.env.S3_SECRET_KEY = "test-secret-key";
process.env.S3_BUCKET = "gearbox-images";
process.env.S3_REGION = "garage";

// Import after mocking
const { uploadImage, deleteImage, getImageUrl, withImageUrl, withImageUrls } =
	await import("@/server/services/storage.service");

describe("storage.service", () => {
	beforeEach(() => {
		mockSend.mockClear();
		mockGetSignedUrl.mockClear();
		mockGetSignedUrl.mockResolvedValue(
			"https://s3.example.com/gearbox-images/test.jpg?signed=1",
		);
	});

	describe("uploadImage", () => {
		test("calls PutObjectCommand with correct params", async () => {
			const buffer = Buffer.from("fake-image-data");
			await uploadImage(buffer, "test-image.jpg", "image/jpeg");

			expect(mockSend).toHaveBeenCalledTimes(1);
			const command = mockSend.mock.calls[0][0] as {
				input: Record<string, unknown>;
			};
			expect(command.input.Bucket).toBe("gearbox-images");
			expect(command.input.Key).toBe("test-image.jpg");
			expect(command.input.ContentType).toBe("image/jpeg");
			expect(Buffer.isBuffer(command.input.Body)).toBe(true);
		});

		test("handles ArrayBuffer input", async () => {
			const arrayBuffer = new ArrayBuffer(10);
			await uploadImage(arrayBuffer, "test.png", "image/png");

			expect(mockSend).toHaveBeenCalledTimes(1);
			const command = mockSend.mock.calls[0][0] as {
				input: Record<string, unknown>;
			};
			expect(Buffer.isBuffer(command.input.Body)).toBe(true);
		});
	});

	describe("deleteImage", () => {
		test("calls DeleteObjectCommand with correct params", async () => {
			await deleteImage("test-image.jpg");

			expect(mockSend).toHaveBeenCalledTimes(1);
			const command = mockSend.mock.calls[0][0] as {
				input: Record<string, unknown>;
			};
			expect(command.input.Bucket).toBe("gearbox-images");
			expect(command.input.Key).toBe("test-image.jpg");
		});
	});

	describe("getImageUrl", () => {
		test("calls getSignedUrl and returns result", async () => {
			const url = await getImageUrl("test-image.jpg");

			expect(mockGetSignedUrl).toHaveBeenCalledTimes(1);
			expect(url).toBe(
				"https://s3.example.com/gearbox-images/test.jpg?signed=1",
			);
		});
	});

	describe("withImageUrl", () => {
		test("returns null imageUrl when imageFilename is null", async () => {
			const record = { id: 1, name: "Test Item", imageFilename: null };
			const result = await withImageUrl(record);

			expect(result.imageUrl).toBeNull();
			expect(result.id).toBe(1);
			expect(result.name).toBe("Test Item");
			expect(mockGetSignedUrl).not.toHaveBeenCalled();
		});

		test("returns presigned URL when imageFilename is present", async () => {
			const record = {
				id: 1,
				name: "Test Item",
				imageFilename: "photo.jpg",
			};
			const result = await withImageUrl(record);

			expect(result.imageUrl).toBe(
				"https://s3.example.com/gearbox-images/test.jpg?signed=1",
			);
			expect(result.id).toBe(1);
			expect(mockGetSignedUrl).toHaveBeenCalledTimes(1);
		});
	});

	describe("withImageUrls", () => {
		test("processes array of records correctly", async () => {
			const records = [
				{ id: 1, imageFilename: "a.jpg" },
				{ id: 2, imageFilename: null },
				{ id: 3, imageFilename: "b.png" },
			];
			const results = await withImageUrls(records);

			expect(results).toHaveLength(3);
			expect(results[0].imageUrl).toBe(
				"https://s3.example.com/gearbox-images/test.jpg?signed=1",
			);
			expect(results[1].imageUrl).toBeNull();
			expect(results[2].imageUrl).toBe(
				"https://s3.example.com/gearbox-images/test.jpg?signed=1",
			);
			// Called twice: for records[0] and records[2]
			expect(mockGetSignedUrl).toHaveBeenCalledTimes(2);
		});

		test("handles empty array", async () => {
			const results = await withImageUrls([]);
			expect(results).toHaveLength(0);
		});
	});
});
