import type { APIError } from "@better-auth/core/error";
import { getTestInstance } from "better-auth/test";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { apiKey, API_KEY_ERROR_CODES as ERROR_CODES } from ".";
import { apiKeyClient } from "./client";
import type { ApiKey } from "./types";
import { isAPIError } from "./utils";

describe("api-key", async () => {
	const { client, auth, signInWithTestUser } = await getTestInstance(
		{
			plugins: [
				apiKey({
					enableMetadata: true,
					permissions: {
						defaultPermissions: {
							files: ["read"],
						},
					},
				}),
			],
		},
		{
			clientOptions: {
				plugins: [apiKeyClient()],
			},
		},
	);
	const { headers, user } = await signInWithTestUser();

	afterEach(() => {
		vi.useRealTimers();
	});

	// =========================================================================
	// CREATE API KEY
	// =========================================================================

	it("should fail to create API keys from client without headers", async () => {
		const apiKeyFail = await client.apiKey.create();

		expect(apiKeyFail.data).toBeNull();
		expect(apiKeyFail.error).toBeDefined();
		expect(apiKeyFail.error?.status).toEqual(401);
		expect(apiKeyFail.error?.statusText).toEqual("UNAUTHORIZED");
		expect(apiKeyFail.error?.message).toEqual(
			ERROR_CODES.UNAUTHORIZED_SESSION.message,
		);
	});

	let firstApiKey: ApiKey;

	it("should successfully create API keys from client with headers", async () => {
		const apiKey = await client.apiKey.create({}, { headers: headers });
		if (apiKey.data) {
			firstApiKey = apiKey.data;
		}

		expect(apiKey.data).not.toBeNull();
		expect(apiKey.data?.key).toBeDefined();
		expect(apiKey.data?.referenceId).toEqual(user.id);
		expect(apiKey.data?.name).toBeNull();
		expect(apiKey.data?.prefix).toBeNull();
		expect(apiKey.data?.refillInterval).toBeNull();
		expect(apiKey.data?.refillAmount).toBeNull();
		expect(apiKey.data?.lastRefillAt).toBeNull();
		expect(apiKey.data?.enabled).toEqual(true);
		expect(apiKey.data?.rateLimitTimeWindow).toEqual(86400000);
		expect(apiKey.data?.rateLimitMax).toEqual(10);
		expect(apiKey.data?.requestCount).toEqual(0);
		expect(apiKey.data?.remaining).toBeNull();
		expect(apiKey.data?.lastRequest).toBeNull();
		expect(apiKey.data?.expiresAt).toBeNull();
		expect(apiKey.data?.createdAt).toBeDefined();
		expect(apiKey.data?.updatedAt).toBeDefined();
		expect(apiKey.data?.metadata).toBeNull();
		expect(apiKey.error).toBeNull();
	});

	interface Err {
		body: {
			code: string | undefined;
			message: string | undefined;
		};
		status: string;
		statusCode: string;
	}

	it("should fail to create API Keys from server without headers and userId", async () => {
		const res: { data: ApiKey | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.createApiKey({ body: {} });
			res.data = apiKey;
		} catch (error: any) {
			res.error = error;
		}

		expect(res.data).toBeNull();
		expect(res.error).toBeDefined();
		expect(res.error?.statusCode).toEqual(401);
		expect(res.error?.status).toEqual("UNAUTHORIZED");
		expect(res.error?.body.message).toEqual(
			ERROR_CODES.UNAUTHORIZED_SESSION.message,
		);
	});

	it("should fail to create api keys from the client if user id is provided", async () => {
		const { headers, user } = await signInWithTestUser();
		const response = await client.apiKey.create({
			userId: user.id,
		});
		expect(response.error?.status).toBe(401);
		const newUser = await auth.api.signUpEmail({
			body: {
				email: "new-email@email.com",
				password: "password",
				name: "test-name",
			},
		});
		const response2 = await client.apiKey.create(
			{
				userId: newUser.user.id,
			},
			{
				headers,
			},
		);
		expect(response2.error?.status).toBe(401);
	});

	it("should successfully create API keys from server with userId", async () => {
		const apiKey = await auth.api.createApiKey({
			body: {
				userId: user.id,
			},
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.key).toBeDefined();
		expect(apiKey.referenceId).toEqual(user.id);
		expect(apiKey.name).toBeNull();
		expect(apiKey.prefix).toBeNull();
		expect(apiKey.refillInterval).toBeNull();
		expect(apiKey.refillAmount).toBeNull();
		expect(apiKey.lastRefillAt).toBeNull();
		expect(apiKey.enabled).toEqual(true);
		expect(apiKey.rateLimitTimeWindow).toEqual(86400000);
		expect(apiKey.rateLimitMax).toEqual(10);
		expect(apiKey.requestCount).toEqual(0);
		expect(apiKey.remaining).toBeNull();
		expect(apiKey.lastRequest).toBeNull();
		expect(apiKey.rateLimitEnabled).toBe(true);
	});

	it("should have the real value from rateLimitEnabled", async () => {
		const apiKey = await auth.api.createApiKey({
			body: {
				userId: user.id,
				rateLimitEnabled: false,
			},
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.rateLimitEnabled).toBe(false);
	});

	it("should have true if the rate limit is undefined", async () => {
		const apiKey = await auth.api.createApiKey({
			body: {
				userId: user.id,
				rateLimitEnabled: undefined,
			},
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.rateLimitEnabled).toBe(true);
	});

	it("should require name in API keys if configured", async () => {
		const { auth, signInWithTestUser } = await getTestInstance(
			{
				plugins: [
					apiKey({
						requireName: true,
					}),
				],
			},
			{
				clientOptions: {
					plugins: [apiKeyClient()],
				},
			},
		);

		const { user } = await signInWithTestUser();
		let err: any;
		try {
			await auth.api.createApiKey({
				body: {
					userId: user.id,
				},
			});
		} catch (error) {
			err = error;
		}
		expect(err).toBeDefined();
		expect(err.body.message).toBe(ERROR_CODES.NAME_REQUIRED.message);
	});

	it("should respect rateLimit configuration from plugin options", async () => {
		const { auth, signInWithTestUser } = await getTestInstance(
			{
				plugins: [
					apiKey({
						rateLimit: {
							enabled: false,
							timeWindow: 1000,
							maxRequests: 10,
						},
						enableMetadata: true,
					}),
				],
			},
			{
				clientOptions: {
					plugins: [apiKeyClient()],
				},
			},
		);

		const { user } = await signInWithTestUser();
		const apiKeyResult = await auth.api.createApiKey({
			body: {
				userId: user.id,
			},
		});

		expect(apiKeyResult).not.toBeNull();
		expect(apiKeyResult.rateLimitEnabled).toBe(false);
		expect(apiKeyResult.rateLimitTimeWindow).toBe(1000);
		expect(apiKeyResult.rateLimitMax).toBe(10);
	});

	it("should create the API key with the given name", async () => {
		const apiKey = await auth.api.createApiKey({
			body: {
				name: "test-api-key",
			},
			headers,
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.name).toEqual("test-api-key");
	});

	it("should create the API key with a name that's shorter than the allowed minimum", async () => {
		const result: { data: ApiKey | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.createApiKey({
				body: {
					name: "test-api-key-that-is-shorter-than-the-allowed-minimum",
				},
				headers,
			});
			result.data = apiKey;
		} catch (error: any) {
			result.error = error;
		}
		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.status).toEqual("BAD_REQUEST");
		expect(result.error?.body.message).toEqual(
			ERROR_CODES.INVALID_NAME_LENGTH.message,
		);
	});

	it("should create the API key with a name that's longer than the allowed maximum", async () => {
		const result: { data: ApiKey | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.createApiKey({
				body: {
					name: "test-api-key-that-is-longer-than-the-allowed-maximum",
				},
				headers,
			});
			result.data = apiKey;
		} catch (error: any) {
			result.error = error;
		}
		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.status).toEqual("BAD_REQUEST");
		expect(result.error?.body.message).toEqual(
			ERROR_CODES.INVALID_NAME_LENGTH.message,
		);
	});

	it("should create the API key with the given prefix", async () => {
		const prefix = "test-api-key_";
		const apiKey = await auth.api.createApiKey({
			body: {
				prefix: prefix,
			},
			headers,
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.prefix).toEqual(prefix);
		expect(apiKey.key.startsWith(prefix)).toEqual(true);
	});

	it("should create the API key with a prefix that's shorter than the allowed minimum", async () => {
		const result: { data: ApiKey | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.createApiKey({
				body: {
					prefix: "test-api-key-that-is-shorter-than-the-allowed-minimum",
				},
				headers,
			});
			result.data = apiKey;
		} catch (error: any) {
			result.error = error;
		}
		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.status).toEqual("BAD_REQUEST");
		expect(result.error?.body.message).toEqual(
			ERROR_CODES.INVALID_PREFIX_LENGTH.message,
		);
	});

	it("should create the API key with a prefix that's longer than the allowed maximum", async () => {
		const result: { data: ApiKey | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.createApiKey({
				body: {
					prefix: "test-api-key-that-is-longer-than-the-allowed-maximum",
				},
				headers,
			});
			result.data = apiKey;
		} catch (error: any) {
			result.error = error;
		}
		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.status).toEqual("BAD_REQUEST");
		expect(result.error?.body.message).toEqual(
			ERROR_CODES.INVALID_PREFIX_LENGTH.message,
		);
	});

	it("should create an API key with a custom expiresIn", async () => {
		const expiresIn = 60 * 60 * 24 * 7; // 7 days
		const expectedResult = Date.now() + expiresIn;
		const apiKey = await auth.api.createApiKey({
			body: {
				expiresIn: expiresIn,
			},
			headers,
		});
		expect(apiKey).not.toBeNull();
		expect(apiKey.expiresAt).toBeDefined();
		expect(apiKey.expiresAt?.getTime()).toBeGreaterThanOrEqual(expectedResult);
	});

	it("should support disabling key hashing", async () => {
		const { auth, signInWithTestUser } = await getTestInstance(
			{
				plugins: [
					apiKey({
						disableKeyHashing: true,
					}),
				],
			},
			{
				clientOptions: {
					plugins: [apiKeyClient()],
				},
			},
		);
		const { headers } = await signInWithTestUser();

		const apiKey2 = await auth.api.createApiKey({
			body: {},
			headers,
		});
		const res = await (await auth.$context).adapter.findOne<ApiKey>({
			model: "apikey",
			where: [
				{
					field: "id",
					value: apiKey2.id,
				},
			],
		});
		expect(res?.key).toEqual(apiKey2.key);
	});

	it("should be able to verify with key hashing disabled", async () => {
		const { auth, signInWithTestUser } = await getTestInstance(
			{
				plugins: [
					apiKey({
						disableKeyHashing: true,
					}),
				],
			},
			{
				clientOptions: {
					plugins: [apiKeyClient()],
				},
			},
		);
		const { headers } = await signInWithTestUser();

		const apiKey2 = await auth.api.createApiKey({
			body: {},
			headers,
		});

		const result = await auth.api.verifyApiKey({ body: { key: apiKey2.key } });
		expect(result.valid).toEqual(true);
	});

	it("should fail to create a key with a custom expiresIn value when customExpiresTime is disabled", async () => {
		const { auth, signInWithTestUser } = await getTestInstance(
			{
				plugins: [
					apiKey({
						enableMetadata: true,
						keyExpiration: {
							disableCustomExpiresTime: true,
						},
					}),
				],
			},
			{
				clientOptions: {
					plugins: [apiKeyClient()],
				},
			},
		);

		const { headers } = await signInWithTestUser();
		const result: { data: ApiKey | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey2 = await auth.api.createApiKey({
				body: {
					expiresIn: 10000,
				},
				headers,
			});
			result.data = apiKey2;
		} catch (error: any) {
			result.error = error;
		}

		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.body.message).toEqual(
			ERROR_CODES.KEY_DISABLED_EXPIRATION.message,
		);
	});

	it("should create an API key with an expiresIn that's smaller than the allowed minimum", async () => {
		const result: { data: ApiKey | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const expiresIn = 60 * 60 * 24 * 0.5; // half a day
			const apiKey = await auth.api.createApiKey({
				body: {
					expiresIn: expiresIn,
				},
				headers,
			});
			result.data = apiKey;
		} catch (error: any) {
			result.error = error;
		}
		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.status).toEqual("BAD_REQUEST");
		expect(result.error?.body.message).toEqual(
			ERROR_CODES.EXPIRES_IN_IS_TOO_SMALL.message,
		);
	});

	it("should fail to create an API key with an expiresIn that's larger than the allowed maximum", async () => {
		const result: { data: ApiKey | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const expiresIn = 60 * 60 * 24 * 365 * 10; // 10 year
			const apiKey = await auth.api.createApiKey({
				body: {
					expiresIn: expiresIn,
				},
				headers,
			});
			result.data = apiKey;
		} catch (error: any) {
			result.error = error;
		}
		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.status).toEqual("BAD_REQUEST");
		expect(result.error?.body.message).toEqual(
			ERROR_CODES.EXPIRES_IN_IS_TOO_LARGE.message,
		);
	});

	it("should fail to create API key with custom refillAndAmount from client auth", async () => {
		const apiKey = await client.apiKey.create(
			{
				refillAmount: 10,
			},
			{ headers },
		);

		expect(apiKey.data).toBeNull();
		expect(apiKey.error).toBeDefined();
		expect(apiKey.error?.statusText).toEqual("BAD_REQUEST");
		expect(apiKey.error?.message).toEqual(
			ERROR_CODES.SERVER_ONLY_PROPERTY.message,
		);

		const apiKey2 = await client.apiKey.create(
			{
				refillInterval: 1001,
			},
			{ headers },
		);

		expect(apiKey2.data).toBeNull();
		expect(apiKey2.error).toBeDefined();
		expect(apiKey2.error?.statusText).toEqual("BAD_REQUEST");
		expect(apiKey2.error?.message).toEqual(
			ERROR_CODES.SERVER_ONLY_PROPERTY.message,
		);
	});

	it("should fail to create API key when refill interval is provided, but no refill amount", async () => {
		const res: { data: ApiKey | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.createApiKey({
				body: {
					refillInterval: 1000,
					userId: user.id,
				},
			});
			res.data = apiKey;
		} catch (error: any) {
			res.error = error;
		}

		expect(res.data).toBeNull();
		expect(res.error).toBeDefined();
		expect(res.error?.status).toEqual("BAD_REQUEST");
		expect(res.error?.body.message).toEqual(
			ERROR_CODES.REFILL_INTERVAL_AND_AMOUNT_REQUIRED.message,
		);
	});

	it("should fail to create API key when refill amount is provided, but no refill interval", async () => {
		const res: { data: ApiKey | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.createApiKey({
				body: {
					refillAmount: 10,
					userId: user.id,
				},
			});
			res.data = apiKey;
		} catch (error: any) {
			res.error = error;
		}

		expect(res.data).toBeNull();
		expect(res.error).toBeDefined();
		expect(res.error?.status).toEqual("BAD_REQUEST");
		expect(res.error?.body.message).toEqual(
			ERROR_CODES.REFILL_AMOUNT_AND_INTERVAL_REQUIRED.message,
		);
	});

	it("should create the API key with the given refill interval & refill amount", async () => {
		const refillInterval = 10000;
		const refillAmount = 10;
		const apiKey = await auth.api.createApiKey({
			body: {
				refillInterval: refillInterval,
				refillAmount: refillAmount,
				userId: user.id,
			},
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.refillInterval).toEqual(refillInterval);
		expect(apiKey.refillAmount).toEqual(refillAmount);
	});

	it("should create API Key with custom remaining", async () => {
		const remaining = 10;
		const apiKey = await auth.api.createApiKey({
			body: {
				remaining: remaining,
				userId: user.id,
			},
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.remaining).toEqual(remaining);
	});

	it("should create API Key with remaining explicitly set to null", async () => {
		const apiKey = await auth.api.createApiKey({
			body: {
				remaining: null,
				userId: user.id,
			},
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.remaining).toBeNull();
	});

	it("should create API Key with remaining explicitly set to null and refillAmount and refillInterval are also set", async () => {
		const refillAmount = 10; // Arbitrary non-null value
		const refillInterval = 1000;
		const apiKey = await auth.api.createApiKey({
			body: {
				remaining: null,
				refillAmount: refillAmount,
				refillInterval: refillInterval,
				userId: user.id,
			},
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.remaining).toBeNull();
		expect(apiKey.refillAmount).toBe(refillAmount);
		expect(apiKey.refillInterval).toBe(refillInterval);
	});

	it("should create API Key with remaining explicitly set to 0 and refillAmount also set", async () => {
		const remaining = 0;
		const refillAmount = 10; // Arbitrary non-null value
		const refillInterval = 1000;
		const apiKey = await auth.api.createApiKey({
			body: {
				remaining: remaining,
				refillAmount: refillAmount,
				refillInterval: refillInterval,
				userId: user.id,
			},
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.remaining).toBe(remaining);
		expect(apiKey.refillAmount).toBe(refillAmount);
		expect(apiKey.refillInterval).toBe(refillInterval);
	});

	it("should create API Key with remaining undefined and default value of null is respected with refillAmount and refillInterval provided", async () => {
		const refillAmount = 10; // Arbitrary non-null value
		const refillInterval = 1000;
		const apiKey = await auth.api.createApiKey({
			body: {
				refillAmount: refillAmount,
				refillInterval: refillInterval,
				userId: user.id,
			},
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.remaining).toBeNull();
		expect(apiKey.refillAmount).toBe(refillAmount);
		expect(apiKey.refillInterval).toBe(refillInterval);
	});

	it("should create API key with invalid metadata", async () => {
		const result: { data: ApiKey | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.createApiKey({
				body: {
					metadata: "invalid",
				},
				headers,
			});
			result.data = apiKey;
		} catch (error: any) {
			result.error = error;
		}
		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.status).toEqual("BAD_REQUEST");
		expect(result.error?.body.message).toEqual(
			ERROR_CODES.INVALID_METADATA_TYPE.message,
		);
	});

	it("should create API key with valid metadata", async () => {
		const metadata = {
			test: "test",
		};
		const apiKey = await auth.api.createApiKey({
			body: {
				metadata: metadata,
			},
			headers,
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.metadata).toEqual(metadata);

		const res = await auth.api.getApiKey({
			query: {
				id: apiKey.id,
			},
			headers,
		});

		expect(res).not.toBeNull();
		if (res) {
			expect(res.metadata).toEqual(metadata);
		}
	});

	it("create API key's returned metadata should be an object", async () => {
		const metadata = {
			test: "test-123",
		};
		const apiKey = await auth.api.createApiKey({
			body: {
				metadata: metadata,
			},
			headers,
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.metadata.test).toBeDefined();
		expect(apiKey.metadata.test).toEqual(metadata.test);
	});

	it("create API key with with metadata when metadata is disabled (should fail)", async () => {
		const { auth, signInWithTestUser } = await getTestInstance(
			{
				plugins: [
					apiKey({
						enableMetadata: false,
					}),
				],
			},
			{
				clientOptions: {
					plugins: [apiKeyClient()],
				},
			},
		);
		const { headers } = await signInWithTestUser();

		const metadata = {
			test: "test-123",
		};
		const result: { data: ApiKey | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.createApiKey({
				body: {
					metadata: metadata,
				},
				headers,
			});
			result.data = apiKey;
		} catch (error: any) {
			result.error = error;
		}

		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.status).toEqual("BAD_REQUEST");
		expect(result.error?.body.message).toEqual(
			ERROR_CODES.METADATA_DISABLED.message,
		);
	});

	it("should have the first 6 characters of the key as the start property", async () => {
		const { data: apiKey } = await client.apiKey.create(
			{},
			{ headers: headers },
		);

		expect(apiKey?.start).toBeDefined();
		expect(apiKey?.start?.length).toEqual(6);
		expect(apiKey?.start).toEqual(apiKey?.key?.substring(0, 6));
	});

	it("should have the start property as null if shouldStore is false", async () => {
		const { client, signInWithTestUser } = await getTestInstance(
			{
				plugins: [
					apiKey({
						startingCharactersConfig: {
							shouldStore: false,
						},
					}),
				],
			},
			{
				clientOptions: {
					plugins: [apiKeyClient()],
				},
			},
		);
		const { headers } = await signInWithTestUser();

		const { data: apiKey2 } = await client.apiKey.create(
			{},
			{ headers: headers },
		);

		expect(apiKey2?.start).toBeNull();
	});

	it("should use the defined charactersLength if provided", async () => {
		const customLength = 3;
		const { client, signInWithTestUser } = await getTestInstance(
			{
				plugins: [
					apiKey({
						startingCharactersConfig: {
							shouldStore: true,
							charactersLength: customLength,
						},
					}),
				],
			},
			{
				clientOptions: {
					plugins: [apiKeyClient()],
				},
			},
		);
		const { headers } = await signInWithTestUser();

		const { data: apiKey2 } = await client.apiKey.create(
			{},
			{ headers: headers },
		);

		expect(apiKey2?.start).toBeDefined();
		expect(apiKey2?.start?.length).toEqual(customLength);
		expect(apiKey2?.start).toEqual(apiKey2?.key?.substring(0, customLength));
	});

	it("should fail to create API key with custom rate-limit options from client auth", async () => {
		const apiKey = await client.apiKey.create(
			{
				rateLimitMax: 15,
			},
			{ headers },
		);

		expect(apiKey.data).toBeNull();
		expect(apiKey.error).toBeDefined();
		expect(apiKey.error?.statusText).toEqual("BAD_REQUEST");
		expect(apiKey.error?.message).toEqual(
			ERROR_CODES.SERVER_ONLY_PROPERTY.message,
		);

		const apiKey2 = await client.apiKey.create(
			{
				rateLimitTimeWindow: 1001,
			},
			{ headers },
		);

		expect(apiKey2.data).toBeNull();
		expect(apiKey2.error).toBeDefined();
		expect(apiKey2.error?.statusText).toEqual("BAD_REQUEST");
		expect(apiKey2.error?.message).toEqual(
			ERROR_CODES.SERVER_ONLY_PROPERTY.message,
		);
	});

	it("should successfully apply custom rate-limit options on the newly created API key", async () => {
		const apiKey = await auth.api.createApiKey({
			body: {
				rateLimitMax: 15,
				rateLimitTimeWindow: 1000,
				userId: user.id,
			},
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey?.rateLimitMax).toEqual(15);
		expect(apiKey?.rateLimitTimeWindow).toEqual(1000);
	});

	// =========================================================================
	// VERIFY API KEY
	// =========================================================================

	it("verify API key without key and userId", async () => {
		const apiKey = await auth.api.verifyApiKey({
			body: {
				key: firstApiKey.key,
			},
		});
		expect(apiKey.key).not.toBe(null);
		expect(apiKey.valid).toBe(true);
	});

	it("verify API key with invalid key (should fail)", async () => {
		const apiKey = await auth.api.verifyApiKey({
			body: {
				key: "invalid",
			},
		});
		expect(apiKey.valid).toBe(false);
		expect(apiKey.error?.code).toBe("INVALID_API_KEY");
	});

	let rateLimitedApiKey: ApiKey;

	const {
		client: rateLimitClient,
		auth: rateLimitAuth,
		signInWithTestUser: rateLimitTestUser,
	} = await getTestInstance(
		{
			plugins: [
				apiKey({
					rateLimit: {
						enabled: true,
						timeWindow: 1000,
					},
				}),
			],
		},
		{
			clientOptions: {
				plugins: [apiKeyClient()],
			},
		},
	);

	const { headers: rateLimitUserHeaders } = await rateLimitTestUser();

	it("should fail to verify API key 20 times in a row due to rate-limit", async () => {
		const { data: apiKey2 } = await rateLimitClient.apiKey.create(
			{},
			{ headers: rateLimitUserHeaders },
		);
		if (!apiKey2) return;
		rateLimitedApiKey = apiKey2;
		for (let i = 0; i < 20; i++) {
			const response = await rateLimitAuth.api.verifyApiKey({
				body: {
					key: apiKey2.key,
				},
				headers: rateLimitUserHeaders,
			});
			if (i >= 10) {
				expect(response.error?.code).toBe("RATE_LIMITED");
			} else {
				expect(response.error).toBeNull();
			}
		}
	});

	it("should allow us to verify API key after rate-limit window has passed", async () => {
		vi.useFakeTimers();
		await vi.advanceTimersByTimeAsync(1000);
		const response = await rateLimitAuth.api.verifyApiKey({
			body: {
				key: rateLimitedApiKey.key,
			},
			headers: rateLimitUserHeaders,
		});
		expect(response.error).toBeNull();
		expect(response?.valid).toBe(true);
	});

	it("should check if verifying an API key's remaining count does go down", async () => {
		const remaining = 10;
		const { data: apiKey } = await client.apiKey.create(
			{
				remaining: remaining,
			},
			{ headers: headers },
		);
		if (!apiKey) return;
		const afterVerificationOnce = await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
			},
			headers,
		});
		expect(afterVerificationOnce?.valid).toEqual(true);
		expect(afterVerificationOnce?.key?.remaining).toEqual(remaining - 1);
		const afterVerificationTwice = await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
			},
			headers,
		});
		expect(afterVerificationTwice?.valid).toEqual(true);
		expect(afterVerificationTwice?.key?.remaining).toEqual(remaining - 2);
	});

	it("should fail if the API key has no remaining", async () => {
		const apiKey = await auth.api.createApiKey({
			body: {
				remaining: 1,
				userId: user.id,
			},
		});
		if (!apiKey) return;
		// run verify once to make the remaining count go down to 0
		await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
			},
			headers,
		});
		const afterVerification = await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
			},
			headers,
		});
		expect(afterVerification.error?.code).toBe("USAGE_EXCEEDED");
	});

	it("should fail if the API key is expired", async () => {
		vi.useRealTimers();
		const { headers } = await signInWithTestUser();
		const apiKey2 = await client.apiKey.create(
			{
				expiresIn: 60 * 60 * 24,
			},
			{ headers: headers, throw: true },
		);
		vi.useFakeTimers();
		await vi.advanceTimersByTimeAsync(1000 * 60 * 60 * 24 * 2);
		const afterVerification = await auth.api.verifyApiKey({
			body: {
				key: apiKey2.key,
			},
			headers,
		});
		expect(afterVerification.error?.code).toEqual("KEY_EXPIRED");
		vi.useRealTimers();
	});

	// =========================================================================
	// UPDATE API KEY
	// =========================================================================

	interface Err {
		body: {
			code: string | undefined;
			message: string | undefined;
		};
		status: string;
		statusCode: string;
	}

	it("should fail to update API key name without headers or userId", async () => {
		const res: { data: ApiKey | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.updateApiKey({
				body: {
					keyId: firstApiKey.id,
					name: "test-api-key",
				},
			});
			res.data = apiKey as ApiKey;
		} catch (error: any) {
			res.error = error;
		}
		expect(res.data).toBeNull();
		expect(res.error).toBeDefined();
		expect(res.error?.statusCode).toEqual(401);
		expect(res.error?.status).toEqual("UNAUTHORIZED");
		expect(res.error?.body.message).toEqual(
			ERROR_CODES.UNAUTHORIZED_SESSION.message,
		);
	});

	it("should update API key name with headers", async () => {
		const newName = "Hello World";
		const apiKey = await auth.api.updateApiKey({
			body: {
				keyId: firstApiKey.id,
				name: newName,
			},
			headers,
		});
		expect(apiKey).toBeDefined();
		expect(apiKey.name).not.toEqual(firstApiKey.name);
		expect(apiKey.name).toEqual(newName);
	});

	it("should fail to update API key name with a length larger than the allowed maximum", async () => {
		let error: APIError | null = null;
		await auth.api
			.updateApiKey({
				body: {
					keyId: firstApiKey.id,
					name: "test-api-key-that-is-longer-than-the-allowed-maximum",
				},
				headers,
			})
			.catch((e) => {
				if (isAPIError(e)) {
					error = e;
					expect(error?.status).toEqual("BAD_REQUEST");
					expect(error?.body?.message).toEqual(
						ERROR_CODES.INVALID_NAME_LENGTH.message,
					);
				}
			});
		expect(error).not.toBeNull();
	});

	it("should fail to update API key name with a length smaller than the allowed minimum", async () => {
		let error: APIError | null = null;
		await auth.api
			.updateApiKey({
				body: {
					keyId: firstApiKey.id,
					name: "",
				},
				headers,
			})
			.catch((e) => {
				if (isAPIError(e)) {
					error = e;
					expect(error?.status).toEqual("BAD_REQUEST");
					expect(error?.body?.message).toEqual(
						ERROR_CODES.INVALID_NAME_LENGTH.message,
					);
				}
			});
		expect(error).not.toBeNull();
	});

	it("should fail to update API key with no values to update", async () => {
		let error: APIError | null = null;
		await auth.api
			.updateApiKey({
				body: {
					keyId: firstApiKey.id,
				},
				headers,
			})
			.catch((e) => {
				if (isAPIError(e)) {
					error = e;
					expect(error?.status).toEqual("BAD_REQUEST");
					expect(error?.body?.message).toEqual(
						ERROR_CODES.NO_VALUES_TO_UPDATE.message,
					);
				}
			});
		expect(error).not.toBeNull();
	});

	it("should update API key expiresIn value", async () => {
		const expiresIn = 60 * 60 * 24 * 7; // 7 days
		const expectedResult = Date.now() + expiresIn;
		const apiKey = await auth.api.updateApiKey({
			body: {
				keyId: firstApiKey.id,
				expiresIn: expiresIn,
			},
			headers,
		});
		expect(apiKey).not.toBeNull();
		expect(apiKey.expiresAt).toBeDefined();
		expect(apiKey.expiresAt?.getTime()).toBeGreaterThanOrEqual(expectedResult);
	});

	it("should fail to update expiresIn value if `disableCustomExpiresTime` is enabled", async () => {
		const { client, auth, signInWithTestUser } = await getTestInstance(
			{
				plugins: [
					apiKey({
						keyExpiration: {
							disableCustomExpiresTime: true,
						},
					}),
				],
			},
			{
				clientOptions: {
					plugins: [apiKeyClient()],
				},
			},
		);
		const { headers } = await signInWithTestUser();

		const { data: firstApiKey } = await client.apiKey.create({}, { headers });

		if (!firstApiKey) return;

		const result: { data: Partial<ApiKey> | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.updateApiKey({
				body: {
					keyId: firstApiKey.id,
					expiresIn: 1000 * 60 * 60 * 24 * 7, // 7 days
				},
				headers,
			});
			result.data = apiKey;
		} catch (error: any) {
			result.error = error;
		}
		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.status).toEqual("BAD_REQUEST");
		expect(result.error?.body.message).toEqual(
			ERROR_CODES.KEY_DISABLED_EXPIRATION.message,
		);
	});

	it("should fail to update expiresIn value if it's smaller than the allowed minimum", async () => {
		const { client, auth, signInWithTestUser } = await getTestInstance(
			{
				plugins: [
					apiKey({
						keyExpiration: {
							minExpiresIn: 1,
						},
					}),
				],
			},
			{
				clientOptions: {
					plugins: [apiKeyClient()],
				},
			},
		);
		const { headers } = await signInWithTestUser();

		const { data: firstApiKey } = await client.apiKey.create({}, { headers });

		if (!firstApiKey) return;

		const result: { data: Partial<ApiKey> | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.updateApiKey({
				body: {
					keyId: firstApiKey.id,
					expiresIn: 1,
				},
				headers,
			});
			result.data = apiKey;
		} catch (error: any) {
			result.error = error;
		}
		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.status).toEqual("BAD_REQUEST");
		expect(result.error?.body.message).toEqual(
			ERROR_CODES.EXPIRES_IN_IS_TOO_SMALL.message,
		);
	});

	it("should fail to update expiresIn value if it's larger than the allowed maximum", async () => {
		const { client, auth, signInWithTestUser } = await getTestInstance(
			{
				plugins: [
					apiKey({
						keyExpiration: {
							maxExpiresIn: 1,
						},
					}),
				],
			},
			{
				clientOptions: {
					plugins: [apiKeyClient()],
				},
			},
		);
		const { headers } = await signInWithTestUser();

		const { data: firstApiKey } = await client.apiKey.create({}, { headers });

		if (!firstApiKey) return;

		const result: { data: Partial<ApiKey> | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.updateApiKey({
				body: {
					keyId: firstApiKey.id,
					expiresIn: 1000 * 60 * 60 * 24 * 365 * 10, // 10 years
				},
				headers,
			});
			result.data = apiKey;
		} catch (error: any) {
			result.error = error;
		}
		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.status).toEqual("BAD_REQUEST");
		expect(result.error?.body.message).toEqual(
			ERROR_CODES.EXPIRES_IN_IS_TOO_LARGE.message,
		);
	});

	it("should update API key remaining count", async () => {
		const remaining = 100;
		const apiKey = await auth.api.updateApiKey({
			body: {
				keyId: firstApiKey.id,
				remaining: remaining,
				userId: user.id,
			},
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.remaining).toEqual(remaining);
	});

	it("should fail update the refillInterval value since it requires refillAmount as well", async () => {
		const result: { data: Partial<ApiKey> | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.updateApiKey({
				body: {
					keyId: firstApiKey.id,
					refillInterval: 1000,
					userId: user.id,
				},
			});
			result.data = apiKey;
		} catch (error: any) {
			result.error = error;
		}
		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.status).toEqual("BAD_REQUEST");
		expect(result.error?.body.message).toEqual(
			ERROR_CODES.REFILL_INTERVAL_AND_AMOUNT_REQUIRED.message,
		);
	});

	it("should fail update the refillAmount value since it requires refillInterval as well", async () => {
		const result: { data: Partial<ApiKey> | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.updateApiKey({
				body: {
					keyId: firstApiKey.id,
					refillAmount: 10,
					userId: user.id,
				},
			});
			result.data = apiKey;
		} catch (error: any) {
			result.error = error;
		}
		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.status).toEqual("BAD_REQUEST");
		expect(result.error?.body.message).toEqual(
			ERROR_CODES.REFILL_AMOUNT_AND_INTERVAL_REQUIRED.message,
		);
	});

	it("should update the refillInterval and refillAmount value", async () => {
		const refillInterval = 10000;
		const refillAmount = 100;
		const apiKey = await auth.api.updateApiKey({
			body: {
				keyId: firstApiKey.id,
				refillInterval: refillInterval,
				refillAmount: refillAmount,
				userId: user.id,
			},
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.refillInterval).toEqual(refillInterval);
		expect(apiKey.refillAmount).toEqual(refillAmount);
	});

	it("should update API key enable value", async () => {
		const newValue = false;
		const apiKey = await auth.api.updateApiKey({
			body: {
				keyId: firstApiKey.id,
				enabled: newValue,
				userId: user.id,
			},
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.enabled).toEqual(newValue);
	});

	it("should fail to update metadata with invalid metadata type", async () => {
		const result: { data: Partial<ApiKey> | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.updateApiKey({
				body: {
					keyId: firstApiKey.id,
					metadata: "invalid",
					userId: user.id,
				},
			});
			result.data = apiKey;
		} catch (error: any) {
			result.error = error;
		}
		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.status).toEqual("BAD_REQUEST");
		expect(result.error?.body.message).toEqual(
			ERROR_CODES.INVALID_METADATA_TYPE.message,
		);
	});

	it("should update metadata with valid metadata type", async () => {
		const metadata = {
			test: "test-123",
		};
		const apiKey = await auth.api.updateApiKey({
			body: {
				keyId: firstApiKey.id,
				metadata: metadata,
				userId: user.id,
			},
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.metadata).toEqual(metadata);
	});

	it("update API key's returned metadata should be an object", async () => {
		const metadata = {
			test: "test-12345",
		};
		const apiKey = await auth.api.updateApiKey({
			body: {
				keyId: firstApiKey.id,
				metadata: metadata,
				userId: user.id,
			},
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.metadata?.test).toBeDefined();
		expect(apiKey.metadata?.test).toEqual(metadata.test);
	});

	// =========================================================================
	// API KEY LASTREQUEST BUG FIX (#5309)
	// =========================================================================

	it("should not modify lastRequest when updating API key configuration", async () => {
		const key = await auth.api.createApiKey({
			body: {
				userId: user.id,
			},
		});
		expect(key.lastRequest).toBeNull();

		const updated = await auth.api.updateApiKey({
			body: {
				keyId: key.id,
				name: "updated-name",
				userId: user.id,
			},
		});

		expect(updated.lastRequest).toBeNull();
	});

	it("should not auto-decrement remaining when updating API key", async () => {
		const key = await auth.api.createApiKey({
			body: {
				remaining: 100,
				userId: user.id,
			},
		});
		expect(key.remaining).toBe(100);

		const updated = await auth.api.updateApiKey({
			body: {
				keyId: key.id,
				metadata: { foo: "bar" },
				userId: user.id,
			},
		});

		expect(updated.remaining).toBe(100);
	});

	it("should allow explicit remaining updates via body parameter", async () => {
		const key = await auth.api.createApiKey({
			body: {
				remaining: 100,
				userId: user.id,
			},
		});

		const updated = await auth.api.updateApiKey({
			body: {
				keyId: key.id,
				remaining: 50,
				userId: user.id,
			},
		});

		expect(updated.remaining).toBe(50);
		expect(updated.lastRequest).toBeNull();
	});

	it("verifyApiKey should still update lastRequest", async () => {
		const key = await auth.api.createApiKey({
			body: {
				userId: user.id,
			},
		});
		expect(key.lastRequest).toBeNull();

		const verified = await auth.api.verifyApiKey({
			body: { key: key.key },
		});
		expect(verified.valid).toBe(true);

		const updated = await auth.api.getApiKey({
			query: { id: key.id, configId: "default" },
			headers,
		});
		expect(updated.lastRequest).not.toBeNull();
		expect(updated.lastRequest).toBeInstanceOf(Date);
	});

	it("verifyApiKey should still decrement remaining", async () => {
		const key = await auth.api.createApiKey({
			body: {
				remaining: 100,
				userId: user.id,
			},
		});

		await auth.api.verifyApiKey({
			body: { key: key.key },
		});

		const updated = await auth.api.getApiKey({
			query: { id: key.id, configId: "default" },
			headers,
		});
		expect(updated.remaining).toBe(99);
	});

	// =========================================================================
	// GET API KEY
	// =========================================================================

	it("should get an API key by id", async () => {
		const apiKey = await client.apiKey.get({
			query: {
				id: firstApiKey.id,
			},
			fetchOptions: {
				headers,
			},
		});
		expect(apiKey.data).not.toBeNull();
		expect(apiKey.data?.id).toBe(firstApiKey.id);
	});

	it("should fail to get an API key by ID that doesn't exist", async () => {
		const result = await client.apiKey.get(
			{
				query: {
					id: "invalid",
				},
			},
			{ headers },
		);
		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.status).toEqual(404);
	});

	it("should successfully receive an object metadata from an API key", async () => {
		const apiKey = await client.apiKey.get(
			{
				query: {
					id: firstApiKey.id,
				},
			},
			{
				headers,
			},
		);
		expect(apiKey).not.toBeNull();
		expect(apiKey.data?.metadata).toBeDefined();
		expect(apiKey.data?.metadata).toBeInstanceOf(Object);
	});

	// =========================================================================
	// LIST API KEY
	// =========================================================================

	it("should fail to list API keys without headers", async () => {
		const result: {
			data: { apiKeys: Partial<ApiKey>[]; total: number } | null;
			error: Err | null;
		} = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.listApiKeys({});
			result.data = apiKey;
		} catch (error: any) {
			result.error = error;
		}

		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.status).toEqual("UNAUTHORIZED");
	});

	it("should list API keys with headers", async () => {
		const apiKeys = await auth.api.listApiKeys({
			headers,
		});

		expect(apiKeys).not.toBeNull();
		expect(apiKeys.apiKeys.length).toBeGreaterThan(0);
		expect(apiKeys.total).toBeGreaterThan(0);
	});

	it("should list API keys with metadata as an object", async () => {
		const apiKeys = await auth.api.listApiKeys({
			headers,
		});

		expect(apiKeys).not.toBeNull();
		expect(apiKeys.apiKeys.length).toBeGreaterThan(0);
		apiKeys.apiKeys.map((apiKey) => {
			if (apiKey.metadata) {
				expect(apiKey.metadata).toBeInstanceOf(Object);
			}
		});
	});

	// =========================================================================
	// LIST API KEY PAGINATION
	// =========================================================================

	describe("list API keys pagination", () => {
		it("should return paginated response with total count", async () => {
			const result = await auth.api.listApiKeys({
				headers,
			});

			expect(result).not.toBeNull();
			expect(result.apiKeys).toBeDefined();
			expect(Array.isArray(result.apiKeys)).toBe(true);
			expect(typeof result.total).toBe("number");
			expect(result.total).toBeGreaterThanOrEqual(result.apiKeys.length);
		});

		it("should limit the number of returned API keys", async () => {
			// Create multiple API keys for testing
			await auth.api.createApiKey({
				body: { name: "pagination-key-1" },
				headers,
			});
			await auth.api.createApiKey({
				body: { name: "pagination-key-2" },
				headers,
			});
			await auth.api.createApiKey({
				body: { name: "pagination-key-3" },
				headers,
			});

			const result = await auth.api.listApiKeys({
				query: { limit: 2 },
				headers,
			});

			expect(result.apiKeys.length).toBeLessThanOrEqual(2);
			expect(result.limit).toBe(2);
			expect(result.total).toBeGreaterThanOrEqual(3);
		});

		it("should skip API keys with offset", async () => {
			const allResults = await auth.api.listApiKeys({
				headers,
			});

			const offsetResults = await auth.api.listApiKeys({
				query: { offset: 1 },
				headers,
			});

			expect(offsetResults.offset).toBe(1);
			expect(offsetResults.apiKeys.length).toBe(allResults.apiKeys.length - 1);
		});

		it("should support pagination with both limit and offset", async () => {
			const page1 = await auth.api.listApiKeys({
				query: { limit: 2, offset: 0 },
				headers,
			});

			const page2 = await auth.api.listApiKeys({
				query: { limit: 2, offset: 2 },
				headers,
			});

			expect(page1.apiKeys.length).toBeLessThanOrEqual(2);
			expect(page2.apiKeys.length).toBeLessThanOrEqual(2);
			expect(page1.limit).toBe(2);
			expect(page1.offset).toBe(0);
			expect(page2.offset).toBe(2);

			// Ensure no overlap between pages
			const page1Ids = page1.apiKeys.map((k) => k.id);
			const page2Ids = page2.apiKeys.map((k) => k.id);
			const overlap = page1Ids.filter((id) => page2Ids.includes(id));
			expect(overlap.length).toBe(0);
		});

		it("should sort API keys by createdAt ascending", async () => {
			const result = await auth.api.listApiKeys({
				query: { sortBy: "createdAt", sortDirection: "asc" },
				headers,
			});

			expect(result.apiKeys.length).toBeGreaterThan(1);
			for (let i = 1; i < result.apiKeys.length; i++) {
				const prev = new Date(result.apiKeys[i - 1]!.createdAt).getTime();
				const curr = new Date(result.apiKeys[i]!.createdAt).getTime();
				expect(curr).toBeGreaterThanOrEqual(prev);
			}
		});

		it("should sort API keys by createdAt descending", async () => {
			const result = await auth.api.listApiKeys({
				query: { sortBy: "createdAt", sortDirection: "desc" },
				headers,
			});

			expect(result.apiKeys.length).toBeGreaterThan(1);
			for (let i = 1; i < result.apiKeys.length; i++) {
				const prev = new Date(result.apiKeys[i - 1]!.createdAt).getTime();
				const curr = new Date(result.apiKeys[i]!.createdAt).getTime();
				expect(curr).toBeLessThanOrEqual(prev);
			}
		});

		it("should sort API keys by name", async () => {
			// Create keys with specific names for sorting test
			await auth.api.createApiKey({
				body: { name: "aaa-sort-test" },
				headers,
			});
			await auth.api.createApiKey({
				body: { name: "zzz-sort-test" },
				headers,
			});

			const ascResult = await auth.api.listApiKeys({
				query: { sortBy: "name", sortDirection: "asc" },
				headers,
			});

			const descResult = await auth.api.listApiKeys({
				query: { sortBy: "name", sortDirection: "desc" },
				headers,
			});

			// Filter to only named keys for comparison
			const ascNamed = ascResult.apiKeys.filter((k) =>
				k.name?.includes("-sort-test"),
			);
			const descNamed = descResult.apiKeys.filter((k) =>
				k.name?.includes("-sort-test"),
			);

			expect(ascNamed[0]!.name).toBe("aaa-sort-test");
			expect(descNamed[0]!.name).toBe("zzz-sort-test");
		});

		it("should combine sorting with pagination", async () => {
			const result = await auth.api.listApiKeys({
				query: {
					limit: 3,
					offset: 0,
					sortBy: "createdAt",
					sortDirection: "desc",
				},
				headers,
			});

			expect(result.apiKeys.length).toBeLessThanOrEqual(3);
			expect(result.limit).toBe(3);

			// Verify sorting is applied
			for (let i = 1; i < result.apiKeys.length; i++) {
				const prev = new Date(result.apiKeys[i - 1]!.createdAt).getTime();
				const curr = new Date(result.apiKeys[i]!.createdAt).getTime();
				expect(curr).toBeLessThanOrEqual(prev);
			}
		});

		it("should return empty array when offset exceeds total", async () => {
			const allResults = await auth.api.listApiKeys({
				headers,
			});

			const result = await auth.api.listApiKeys({
				query: { offset: allResults.total + 100 },
				headers,
			});

			expect(result.apiKeys.length).toBe(0);
			expect(result.total).toBe(allResults.total);
		});

		it("should handle string query parameters for limit and offset", async () => {
			const result = await auth.api.listApiKeys({
				query: { limit: "2", offset: "1" } as any,
				headers,
			});

			expect(result.limit).toBe(2);
			expect(result.offset).toBe(1);
			expect(result.apiKeys.length).toBeLessThanOrEqual(2);
		});
	});

	// =========================================================================
	// Sessions from API keys
	// =========================================================================

	describe("enableSessionForAPIKeys", () => {
		it("should get session from an API key", async () => {
			const { client, auth, signInWithTestUser } = await getTestInstance(
				{
					plugins: [
						apiKey({
							enableSessionForAPIKeys: true,
						}),
					],
				},
				{
					clientOptions: {
						plugins: [apiKeyClient()],
					},
				},
			);

			const { headers: userHeaders } = await signInWithTestUser();

			const { data: apiKey2 } = await client.apiKey.create(
				{},
				{ headers: userHeaders },
			);
			if (!apiKey2) return;
			const headers = new Headers();
			headers.set("x-api-key", apiKey2.key);

			const session = await auth.api.getSession({
				headers: headers,
			});

			expect(session?.session).toBeDefined();
		});

		it("should not get session from an API key if enableSessionForAPIKeys is false", async () => {
			const { client, auth, signInWithTestUser } = await getTestInstance(
				{
					plugins: [
						apiKey({
							enableSessionForAPIKeys: false,
						}),
					],
				},
				{
					clientOptions: {
						plugins: [apiKeyClient()],
					},
				},
			);
			const { headers: userHeaders } = await signInWithTestUser();
			const { data: apiKey2 } = await client.apiKey.create(
				{},
				{ headers: userHeaders },
			);
			if (!apiKey2) return;
			const headers = new Headers();
			headers.set("x-api-key", apiKey2.key);
			const session = await auth.api.getSession({
				headers: headers,
			});
			expect(session).toBeNull();
		});

		it("should get the Response object when asResponse is true", async () => {
			const { client, auth, signInWithTestUser } = await getTestInstance(
				{
					plugins: [
						apiKey({
							enableSessionForAPIKeys: true,
						}),
					],
				},
				{
					clientOptions: {
						plugins: [apiKeyClient()],
					},
				},
			);
			const { headers: userHeaders } = await signInWithTestUser();
			const { data: apiKey2 } = await client.apiKey.create(
				{},
				{ headers: userHeaders },
			);
			if (!apiKey2) return;
			const headers = new Headers();
			headers.set("x-api-key", apiKey2.key);
			const res = await auth.api.getSession({
				headers: headers,
				asResponse: true,
			});
			expect(res).toBeInstanceOf(Response);
		});
	});

	// =========================================================================
	// DELETE API KEY
	// =========================================================================

	it("should fail to delete an API key by ID without headers", async () => {
		const result: { data: { success: boolean } | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.deleteApiKey({
				body: {
					keyId: firstApiKey.id,
				},
			});
			result.data = apiKey;
		} catch (error: any) {
			result.error = error;
		}

		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.status).toEqual("UNAUTHORIZED");
	});

	it("should delete an API key by ID with headers", async () => {
		const apiKey = await auth.api.deleteApiKey({
			body: {
				keyId: firstApiKey.id,
			},
			headers,
		});

		expect(apiKey).not.toBeNull();
		expect(apiKey.success).toEqual(true);
	});

	it("should delete an API key by ID with headers using auth-client", async () => {
		const newApiKey = await client.apiKey.create({}, { headers: headers });
		if (!newApiKey.data) return;

		const apiKey = await client.apiKey.delete(
			{
				keyId: newApiKey.data.id,
			},
			{ headers },
		);

		if (!apiKey.data?.success) {
			console.log(apiKey.error);
		}

		expect(apiKey).not.toBeNull();
		expect(apiKey.data?.success).toEqual(true);
	});

	it("should fail to delete an API key by ID that doesn't exist", async () => {
		const result: { data: { success: boolean } | null; error: Err | null } = {
			data: null,
			error: null,
		};
		try {
			const apiKey = await auth.api.deleteApiKey({
				body: {
					keyId: "invalid",
				},
				headers,
			});
			result.data = apiKey;
		} catch (error: any) {
			result.error = error;
		}
		expect(result.data).toBeNull();
		expect(result.error).toBeDefined();
		expect(result.error?.status).toEqual("NOT_FOUND");
		expect(result.error?.body.message).toEqual(
			ERROR_CODES.KEY_NOT_FOUND.message,
		);
	});

	it("should create an API key with permissions", async () => {
		const permissions = {
			files: ["read", "write"],
			users: ["read"],
		};

		const apiKey = await auth.api.createApiKey({
			body: {
				permissions,
				userId: user.id,
			},
		});
		expect(apiKey).not.toBeNull();
		expect(apiKey.permissions).toEqual(permissions);
	});

	it("should have permissions as an object from getApiKey", async () => {
		const permissions = {
			files: ["read", "write"],
			users: ["read"],
		};

		const apiKey = await auth.api.createApiKey({
			body: {
				permissions,
				userId: user.id,
			},
		});

		const apiKeyResults = await auth.api.getApiKey({
			query: {
				id: apiKey.id,
			},
			headers,
		});

		expect(apiKeyResults).not.toBeNull();
		expect(apiKeyResults.permissions).toEqual(permissions);
	});

	it("should have permissions as an object from verifyApiKey", async () => {
		const permissions = {
			files: ["read", "write"],
			users: ["read"],
		};

		const apiKey = await auth.api.createApiKey({
			body: {
				permissions,
				userId: user.id,
			},
		});
		const apiKeyResults = await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
				permissions: {
					files: ["read"],
				},
			},
			headers,
		});

		expect(apiKeyResults).not.toBeNull();
		expect(apiKeyResults.key?.permissions).toEqual(permissions);
	});

	it("should create an API key with default permissions", async () => {
		const apiKey = await auth.api.createApiKey({
			body: {
				userId: user.id,
			},
		});
		expect(apiKey).not.toBeNull();
		expect(apiKey.permissions).toEqual({
			files: ["read"],
		});
	});

	it("should have valid metadata from key verification results", async () => {
		const metadata = {
			test: "hello-world-123",
		};
		const apiKey = await auth.api.createApiKey({
			body: {
				userId: user.id,
				metadata: metadata,
			},
			headers,
		});

		expect(apiKey).not.toBeNull();
		if (apiKey) {
			const result = await auth.api.verifyApiKey({
				body: {
					key: apiKey.key,
				},
				headers,
			});

			expect(result.valid).toBe(true);
			expect(result.error).toBeNull();
			expect(result.key?.metadata).toEqual(metadata);
		}
	});

	it("should verify an API key with matching permissions", async () => {
		const permissions = {
			files: ["read", "write"],
			users: ["read"],
		};

		const apiKey = await auth.api.createApiKey({
			body: {
				permissions,
				userId: user.id,
			},
		});

		const result = await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
				permissions: {
					files: ["read"],
				},
			},
		});

		expect(result.valid).toBe(true);
		expect(result.error).toBeNull();
		expect(result.key?.permissions).toEqual(permissions);
	});

	it("should fail to verify an API key with non-matching permissions", async () => {
		const permissions = {
			files: ["read"],
			users: ["read"],
		};

		const apiKey = await auth.api.createApiKey({
			body: {
				permissions,
				userId: user.id,
			},
		});

		const result = await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
				permissions: {
					files: ["write"],
				},
			},
		});

		expect(result.valid).toBe(false);
		expect(result.error?.code).toBe("KEY_NOT_FOUND");
	});

	it("should fail to verify when required permissions are specified but API key has no permissions", async () => {
		const apiKey = await auth.api.createApiKey({
			body: {
				userId: user.id,
			},
		});

		const result = await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
				permissions: {
					files: ["write"],
				},
			},
		});

		expect(result.valid).toBe(false);
		expect(result.error?.code).toBe("KEY_NOT_FOUND");
	});

	it("should update an API key with permissions", async () => {
		const permissions = {
			files: ["read", "write"],
			users: ["read"],
		};
		const createdApiKey = await auth.api.createApiKey({
			body: {
				userId: user.id,
			},
		});
		expect(createdApiKey.permissions).not.toEqual(permissions);
		const apiKey = await auth.api.updateApiKey({
			body: {
				keyId: createdApiKey.id,
				permissions,
				userId: user.id,
			},
		});
		expect(apiKey).not.toBeNull();
		expect(apiKey.permissions).toEqual(permissions);
	});

	it("should refill API key credits after refill interval (milliseconds)", async () => {
		vi.useRealTimers();

		const refillInterval = 3600000; // 1 hour in milliseconds
		const refillAmount = 5;
		const initialRemaining = 2;

		const apiKey = await auth.api.createApiKey({
			body: {
				userId: user.id,
				remaining: initialRemaining,
				refillInterval: refillInterval,
				refillAmount: refillAmount,
			},
		});

		let result = await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
			},
		});
		expect(result.valid).toBe(true);
		expect(result.key?.remaining).toBe(initialRemaining - 1);

		result = await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
			},
		});
		expect(result.valid).toBe(true);
		expect(result.key?.remaining).toBe(0);

		result = await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
			},
		});
		expect(result.valid).toBe(false);
		expect(result.error?.code).toBe("USAGE_EXCEEDED");

		vi.useFakeTimers();
		await vi.advanceTimersByTimeAsync(refillInterval + 1000);

		result = await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
			},
		});
		expect(result.valid).toBe(true);
		expect(result.key?.remaining).toBe(refillAmount - 1);

		vi.useRealTimers();
	});

	it("should not refill API key credits before refill interval expires", async () => {
		vi.useRealTimers();

		const refillInterval = 86400000; // 24 hours in milliseconds
		const refillAmount = 10;
		const initialRemaining = 1;

		const apiKey = await auth.api.createApiKey({
			body: {
				userId: user.id,
				remaining: initialRemaining,
				refillInterval: refillInterval,
				refillAmount: refillAmount,
			},
		});

		let result = await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
			},
		});
		expect(result.valid).toBe(true);
		expect(result.key?.remaining).toBe(0);

		vi.useFakeTimers();
		await vi.advanceTimersByTimeAsync(refillInterval / 2); // Only advance half the interval

		result = await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
			},
		});
		expect(result.valid).toBe(false);
		expect(result.error?.code).toBe("USAGE_EXCEEDED");

		await vi.advanceTimersByTimeAsync(refillInterval / 2 + 1000);

		result = await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
			},
		});
		expect(result.valid).toBe(true);
		expect(result.key?.remaining).toBe(refillAmount - 1);

		vi.useRealTimers();
	});

	it("should handle multiple refill cycles correctly", async () => {
		vi.useRealTimers();

		const refillInterval = 3600000; // 1 hour in milliseconds
		const refillAmount = 3;

		const apiKey = await auth.api.createApiKey({
			body: {
				userId: user.id,
				remaining: 1,
				refillInterval: refillInterval,
				refillAmount: refillAmount,
			},
		});

		let result = await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
			},
		});
		expect(result.valid).toBe(true);
		expect(result.key?.remaining).toBe(0);

		vi.useFakeTimers();

		await vi.advanceTimersByTimeAsync(refillInterval + 1000);
		result = await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
			},
		});
		expect(result.valid).toBe(true);
		expect(result.key?.remaining).toBe(refillAmount - 1);

		for (let i = 0; i < refillAmount - 1; i++) {
			result = await auth.api.verifyApiKey({
				body: {
					key: apiKey.key,
				},
			});
			expect(result.valid).toBe(true);
		}

		result = await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
			},
		});
		expect(result.valid).toBe(false);
		expect(result.error?.code).toBe("USAGE_EXCEEDED");

		await vi.advanceTimersByTimeAsync(refillInterval + 1000);
		result = await auth.api.verifyApiKey({
			body: {
				key: apiKey.key,
			},
		});
		expect(result.valid).toBe(true);
		expect(result.key?.remaining).toBe(refillAmount - 1);

		vi.useRealTimers();
	});

	describe("secondary storage", async () => {
		const store = new Map<string, string>();
		const expirationMap = new Map<string, number>();

		const { client, auth, signInWithTestUser } = await getTestInstance(
			{
				secondaryStorage: {
					set(key, value, ttl) {
						store.set(key, value);
						if (ttl) expirationMap.set(key, ttl);
					},
					get(key) {
						return store.get(key) || null;
					},
					delete(key) {
						store.delete(key);
						expirationMap.delete(key);
					},
				},
				plugins: [
					apiKey({
						storage: "secondary-storage",
						enableMetadata: true,
					}),
				],
			},
			{
				clientOptions: {
					plugins: [apiKeyClient()],
				},
			},
		);

		beforeEach(() => {
			store.clear();
			expirationMap.clear();
		});

		it("should create API key in secondary storage", async () => {
			const { headers, user } = await signInWithTestUser();
			const { data: apiKey } = await client.apiKey.create(
				{},
				{ headers: headers },
			);

			expect(apiKey).not.toBeNull();
			expect(apiKey?.key).toBeDefined();

			// Check that API key is stored in secondary storage by ID
			expect(store.has(`api-key:by-id:${apiKey?.id}`)).toBe(true);

			// Check that user's API key list is updated
			expect(store.has(`api-key:by-ref:${user.id}`)).toBe(true);

			// Verify the stored data can be retrieved
			const storedData = store.get(`api-key:by-id:${apiKey?.id}`);
			expect(storedData).toBeDefined();
			const parsed = JSON.parse(storedData!);
			expect(parsed.id).toBe(apiKey?.id);
			expect(parsed.referenceId).toBe(user.id);
		});

		it("should get API key from secondary storage", async () => {
			const { headers, user } = await signInWithTestUser();
			const { data: createdKey } = await client.apiKey.create(
				{},
				{ headers: headers },
			);

			expect(createdKey).not.toBeNull();

			const { data: retrievedKey } = await client.apiKey.get(
				{ query: { id: createdKey!.id } },
				{ headers: headers },
			);

			expect(retrievedKey).not.toBeNull();
			expect(retrievedKey?.id).toBe(createdKey?.id);
			expect(retrievedKey?.referenceId).toBe(user.id);
		});

		it("should list API keys from secondary storage", async () => {
			const { headers } = await signInWithTestUser();

			// Create multiple API keys
			const { data: key1 } = await client.apiKey.create(
				{},
				{ headers: headers },
			);
			const { data: key2 } = await client.apiKey.create(
				{},
				{ headers: headers },
			);

			const { data: keys } = await client.apiKey.list({
				fetchOptions: { headers: headers },
			});

			expect(keys).not.toBeNull();
			expect(keys?.apiKeys?.length).toBeGreaterThanOrEqual(2);
			expect(keys?.apiKeys?.some((k) => k.id === key1?.id)).toBe(true);
			expect(keys?.apiKeys?.some((k) => k.id === key2?.id)).toBe(true);
		});

		it("should update API key in secondary storage", async () => {
			const { headers } = await signInWithTestUser();
			const { data: createdKey } = await client.apiKey.create(
				{ name: "Original Name" },
				{ headers: headers },
			);

			expect(createdKey).not.toBeNull();

			const { data: updatedKey } = await client.apiKey.update(
				{
					keyId: createdKey!.id,
					name: "Updated Name",
				},
				{ headers: headers },
			);

			expect(updatedKey).not.toBeNull();
			expect(updatedKey?.name).toBe("Updated Name");
			expect(updatedKey?.id).toBe(createdKey?.id);
		});

		it("should delete API key from secondary storage", async () => {
			const { headers, user } = await signInWithTestUser();
			const { data: createdKey } = await client.apiKey.create(
				{},
				{ headers: headers },
			);

			expect(createdKey).not.toBeNull();

			const { data: deleteResult } = await client.apiKey.delete(
				{ keyId: createdKey!.id },
				{ headers: headers },
			);

			expect(deleteResult?.success).toBe(true);

			// Verify it's deleted from secondary storage
			expect(store.has(`api-key:by-id:${createdKey!.id}`)).toBe(false);

			// Verify it's removed from user's list
			const userListData = store.get(`api-key:by-ref:${user.id}`);
			if (userListData) {
				const userIds = JSON.parse(userListData);
				expect(userIds.includes(createdKey!.id)).toBe(false);
			}
		});

		it("should verify API key from secondary storage", async () => {
			const { headers } = await signInWithTestUser();
			const { data: createdKey } = await client.apiKey.create(
				{},
				{ headers: headers },
			);

			expect(createdKey).not.toBeNull();

			const result = await auth.api.verifyApiKey({
				body: {
					key: createdKey!.key,
				},
			});

			expect(result.valid).toBe(true);
			expect(result.key).not.toBeNull();
			expect(result.key?.id).toBe(createdKey?.id);
		});

		it("should set TTL when API key has expiration", async () => {
			const { headers } = await signInWithTestUser();
			const expiresIn = 60 * 60 * 24; // 1 day in seconds

			const { data: createdKey } = await client.apiKey.create(
				{ expiresIn },
				{ headers: headers },
			);

			expect(createdKey).not.toBeNull();
			expect(createdKey?.expiresAt).not.toBeNull();

			// Check that TTL was set in expiration map (check by ID key)
			const storedKey = `api-key:by-id:${createdKey!.id}`;
			const ttl = expirationMap.get(storedKey);
			expect(ttl).toBeDefined();
			expect(ttl).toBeGreaterThan(0);
			// TTL should be approximately expiresIn seconds (within 5 seconds tolerance)
			expect(Math.abs(ttl! - expiresIn)).toBeLessThan(5);
		});

		it("should handle metadata in secondary storage", async () => {
			const { headers } = await signInWithTestUser();
			const metadata = { plan: "premium", environment: "production" };

			const { data: createdKey } = await client.apiKey.create(
				{ metadata },
				{ headers: headers },
			);

			expect(createdKey).not.toBeNull();
			expect(createdKey?.metadata).toEqual(metadata);

			const { data: retrievedKey } = await client.apiKey.get(
				{ query: { id: createdKey!.id } },
				{ headers: headers },
			);

			expect(retrievedKey?.metadata).toEqual(metadata);
		});

		it("should handle rate limiting with secondary storage", async () => {
			const { user } = await signInWithTestUser();
			const createdKey = await auth.api.createApiKey({
				body: {
					rateLimitEnabled: true,
					rateLimitMax: 2,
					rateLimitTimeWindow: 1000 * 60, // 1 minute
					userId: user.id,
				},
			});
			expect(createdKey).not.toBeNull();

			// First request should succeed
			let result = await auth.api.verifyApiKey({
				body: {
					key: createdKey!.key,
				},
			});
			expect(result.valid).toBe(true);

			// Second request should succeed
			result = await auth.api.verifyApiKey({
				body: {
					key: createdKey!.key,
				},
			});
			expect(result.valid).toBe(true);

			// Third request should fail due to rate limit
			result = await auth.api.verifyApiKey({
				body: {
					key: createdKey!.key,
				},
			});
			expect(result.valid).toBe(false);
			expect(result.error?.code).toBe("RATE_LIMITED");
		});

		it("should handle remaining count with secondary storage", async () => {
			const { user } = await signInWithTestUser();
			const remaining = 5;

			const createdKey = await auth.api.createApiKey({
				body: {
					remaining,
					userId: user.id,
				},
			});

			expect(createdKey).not.toBeNull();
			expect(createdKey?.remaining).toBe(remaining);

			let result = await auth.api.verifyApiKey({
				body: {
					key: createdKey!.key,
				},
			});
			expect(result.valid).toBe(true);
			expect(result.key?.remaining).toBe(remaining - 1);

			result = await auth.api.verifyApiKey({
				body: {
					key: createdKey!.key,
				},
			});
			expect(result.valid).toBe(true);
			expect(result.key?.remaining).toBe(remaining - 2);
		});

		it("should handle expired keys with TTL in secondary storage", async () => {
			vi.useFakeTimers();
			const { headers } = await signInWithTestUser();
			// Use 1 day in seconds (minimum allowed) + 1 second for testing expiration
			const expiresIn = 60 * 60 * 24 + 1; // 86401 seconds = 1 day + 1 second

			const { data: createdKey } = await client.apiKey.create(
				{ expiresIn },
				{ headers: headers },
			);

			expect(createdKey).not.toBeNull();
			expect(createdKey?.expiresAt).not.toBeNull();

			// Advance time past expiration
			await vi.advanceTimersByTimeAsync((expiresIn + 1) * 1000);

			const result = await auth.api.verifyApiKey({
				body: {
					key: createdKey!.key,
				},
			});

			expect(result.valid).toBe(false);
			expect(result.error?.code).toBe("KEY_EXPIRED");

			vi.useRealTimers();
		});

		it("should maintain user's API key list in secondary storage", async () => {
			const { headers } = await signInWithTestUser();

			// Create multiple API keys for the same user
			const { data: key1 } = await client.apiKey.create(
				{},
				{ headers: headers },
			);
			const { data: key2 } = await client.apiKey.create(
				{},
				{ headers: headers },
			);
			const { data: key3 } = await client.apiKey.create(
				{},
				{ headers: headers },
			);

			// List should return all keys
			const { data: keys } = await client.apiKey.list({
				fetchOptions: { headers: headers },
			});

			expect(keys?.apiKeys?.length).toBeGreaterThanOrEqual(3);
			expect(keys?.apiKeys?.some((k) => k.id === key1?.id)).toBe(true);
			expect(keys?.apiKeys?.some((k) => k.id === key2?.id)).toBe(true);
			expect(keys?.apiKeys?.some((k) => k.id === key3?.id)).toBe(true);

			// Delete one key
			await client.apiKey.delete({ keyId: key2!.id }, { headers: headers });

			// List should now have one less key
			const { data: keysAfterDelete } = await client.apiKey.list({
				fetchOptions: { headers: headers },
			});

			expect(keysAfterDelete?.apiKeys?.length).toBe(keys!.apiKeys!.length - 1);
			expect(keysAfterDelete?.apiKeys?.some((k) => k.id === key2?.id)).toBe(
				false,
			);
		});
	});

	describe("secondary-storage-with-fallback", async () => {
		const store = new Map<string, string>();
		const expirationMap = new Map<string, number>();

		const { client, auth, signInWithTestUser } = await getTestInstance(
			{
				secondaryStorage: {
					set(key, value, ttl) {
						store.set(key, value);
						if (ttl) expirationMap.set(key, ttl);
					},
					get(key) {
						return store.get(key) || null;
					},
					delete(key) {
						store.delete(key);
						expirationMap.delete(key);
					},
				},
				plugins: [
					apiKey({
						storage: "secondary-storage",
						fallbackToDatabase: true,
						enableMetadata: true,
					}),
				],
			},
			{
				clientOptions: {
					plugins: [apiKeyClient()],
				},
			},
		);

		beforeEach(() => {
			store.clear();
			expirationMap.clear();
		});

		it("should read from secondary storage first", async () => {
			const { headers } = await signInWithTestUser();
			const { data: createdKey } = await client.apiKey.create(
				{},
				{ headers: headers },
			);

			expect(createdKey).not.toBeNull();
			expect(store.has(`api-key:by-id:${createdKey!.id}`)).toBe(true);

			const { data: retrievedKey } = await client.apiKey.get(
				{ query: { id: createdKey!.id } },
				{ headers: headers },
			);

			expect(retrievedKey).not.toBeNull();
			expect(retrievedKey?.id).toBe(createdKey?.id);
		});

		it("verifyApiKey should persist quota updates to the database when fallbackToDatabase is true", async () => {
			const { headers, user } = await signInWithTestUser();

			const createdKey = await auth.api.createApiKey({
				body: {
					remaining: 1,
					userId: user.id,
				},
			});

			const first = await auth.api.verifyApiKey({
				body: {
					key: createdKey.key,
				},
			});

			expect(first.valid).toBe(true);
			expect(first.key?.remaining).toBe(0);

			// Ensure the canonical DB row was updated (not just the cache).
			const dbAfterFirst = await auth.api.getApiKey({
				query: { id: createdKey.id },
				headers,
			});
			expect(dbAfterFirst.remaining).toBe(0);

			// Simulate cache eviction/deletion and ensure we don't repopulate stale allowances.
			store.clear();

			const second = await auth.api.verifyApiKey({
				body: {
					key: createdKey.key,
				},
			});

			expect(second.valid).toBe(false);
			expect(second.error?.code).toBe("USAGE_EXCEEDED");
		});

		it("should fallback to database when not found in storage and auto-populate storage", async () => {
			const { headers, user } = await signInWithTestUser();

			// Create key directly in database adapter (bypassing storage)
			const context = await auth.$context;
			const hashedKey = "test_hashed_key_123";
			const dbKey = await context.adapter.create<Omit<ApiKey, "id">, ApiKey>({
				model: "apikey",
				data: {
					configId: "default",
					createdAt: new Date(),
					updatedAt: new Date(),
					name: "Test Key",
					prefix: "test",
					start: "test_",
					key: hashedKey,
					enabled: true,
					expiresAt: null,
					referenceId: user.id,
					lastRefillAt: null,
					lastRequest: null,
					metadata: null,
					rateLimitMax: null,
					rateLimitTimeWindow: null,
					remaining: null,
					refillAmount: null,
					refillInterval: null,
					rateLimitEnabled: false,
					requestCount: 0,
					permissions: null,
				},
			});

			expect(dbKey).not.toBeNull();

			// Ensure key is NOT in storage initially
			expect(store.has(`api-key:by-id:${dbKey!.id}`)).toBe(false);
			expect(store.has(`api-key:${hashedKey}`)).toBe(false);

			// Retrieve it via API (should fallback to DB and auto-populate storage)
			const { data: retrievedKey } = await client.apiKey.get(
				{ query: { id: dbKey!.id } },
				{ headers: headers },
			);

			expect(retrievedKey).not.toBeNull();
			expect(retrievedKey?.id).toBe(dbKey?.id);

			// Verify it's now in storage (auto-populated)
			expect(store.has(`api-key:by-id:${dbKey!.id}`)).toBe(true);
			expect(store.has(`api-key:${hashedKey}`)).toBe(true);
		});

		it("should populate storage when listing keys falls back to database", async () => {
			const { headers, user } = await signInWithTestUser();

			// Create keys directly in database adapter (bypassing storage)
			const context = await auth.$context;
			const hashedKey1 = "test_hashed_key_1";
			const hashedKey2 = "test_hashed_key_2";

			const dbKey1 = await context.adapter.create<Omit<ApiKey, "id">, ApiKey>({
				model: "apikey",
				data: {
					configId: "default",
					createdAt: new Date(),
					updatedAt: new Date(),
					name: "Test Key 1",
					prefix: "test",
					start: "test_",
					key: hashedKey1,
					enabled: true,
					expiresAt: null,
					referenceId: user.id,
					lastRefillAt: null,
					lastRequest: null,
					metadata: null,
					rateLimitMax: null,
					rateLimitTimeWindow: null,
					remaining: null,
					refillAmount: null,
					refillInterval: null,
					rateLimitEnabled: false,
					requestCount: 0,
					permissions: null,
				},
			});

			const dbKey2 = await context.adapter.create<Omit<ApiKey, "id">, ApiKey>({
				model: "apikey",
				data: {
					configId: "default",
					createdAt: new Date(),
					updatedAt: new Date(),
					name: "Test Key 2",
					prefix: "test",
					start: "test_",
					key: hashedKey2,
					enabled: true,
					expiresAt: null,
					referenceId: user.id,
					lastRefillAt: null,
					lastRequest: null,
					metadata: null,
					rateLimitMax: null,
					rateLimitTimeWindow: null,
					remaining: null,
					refillAmount: null,
					refillInterval: null,
					rateLimitEnabled: false,
					requestCount: 0,
					permissions: null,
				},
			});

			expect(dbKey1).not.toBeNull();
			expect(dbKey2).not.toBeNull();

			// Ensure keys are NOT in storage initially
			expect(store.has(`api-key:by-id:${dbKey1!.id}`)).toBe(false);
			expect(store.has(`api-key:by-id:${dbKey2!.id}`)).toBe(false);
			expect(store.has(`api-key:by-ref:${user.id}`)).toBe(false);

			// List keys via API (should fallback to DB and auto-populate storage)
			const { data: keys } = await client.apiKey.list({}, { headers: headers });

			expect(keys).not.toBeNull();
			expect(keys?.apiKeys?.length).toBeGreaterThanOrEqual(2);
			expect(keys?.apiKeys?.some((k) => k.id === dbKey1!.id)).toBe(true);
			expect(keys?.apiKeys?.some((k) => k.id === dbKey2!.id)).toBe(true);

			// Verify keys are now in storage (auto-populated)
			expect(store.has(`api-key:by-id:${dbKey1!.id}`)).toBe(true);
			expect(store.has(`api-key:by-id:${dbKey2!.id}`)).toBe(true);
			expect(store.has(`api-key:${hashedKey1}`)).toBe(true);
			expect(store.has(`api-key:${hashedKey2}`)).toBe(true);
			// Verify user's key list is populated
			expect(store.has(`api-key:by-ref:${user.id}`)).toBe(true);
		});

		it("should write to secondary storage only", async () => {
			const { headers } = await signInWithTestUser();
			const { data: createdKey } = await client.apiKey.create(
				{ name: "Test Key" },
				{ headers: headers },
			);

			expect(createdKey).not.toBeNull();
			expect(store.has(`api-key:by-id:${createdKey!.id}`)).toBe(true);
		});

		it("should create in both database and secondary storage when fallbackToDatabase is true", async () => {
			const { headers } = await signInWithTestUser();
			const { data: createdKey } = await client.apiKey.create(
				{ name: "Fallback Test Key" },
				{ headers: headers },
			);

			expect(createdKey).not.toBeNull();

			// Should be in secondary storage
			expect(store.has(`api-key:by-id:${createdKey!.id}`)).toBe(true);

			// Should also be in database (verify by direct DB query)
			const dbKey = await auth.api.getApiKey({
				query: { id: createdKey!.id },
				headers,
			});
			expect(dbKey).not.toBeNull();
			expect(dbKey?.id).toBe(createdKey?.id);
			expect(dbKey?.name).toBe("Fallback Test Key");
		});

		it("should update both database and secondary storage when fallbackToDatabase is true", async () => {
			const { headers } = await signInWithTestUser();
			const { data: createdKey } = await client.apiKey.create(
				{ name: "Original Name" },
				{ headers: headers },
			);

			expect(createdKey).not.toBeNull();

			// Update the key
			const { data: updatedKey } = await client.apiKey.update(
				{
					keyId: createdKey!.id,
					name: "Updated Name",
				},
				{ headers: headers },
			);

			expect(updatedKey).not.toBeNull();
			expect(updatedKey?.name).toBe("Updated Name");

			// Verify secondary storage is updated
			const cachedData = store.get(`api-key:by-id:${createdKey!.id}`);
			expect(cachedData).toBeDefined();
			const parsed = JSON.parse(cachedData!);
			expect(parsed.name).toBe("Updated Name");

			// Verify database is updated
			const dbKey = await auth.api.getApiKey({
				query: { id: createdKey!.id },
				headers,
			});
			expect(dbKey?.name).toBe("Updated Name");
		});

		it("should delete from both database and secondary storage when fallbackToDatabase is true", async () => {
			const { headers } = await signInWithTestUser();
			const { data: createdKey } = await client.apiKey.create(
				{},
				{ headers: headers },
			);

			expect(createdKey).not.toBeNull();
			expect(store.has(`api-key:by-id:${createdKey!.id}`)).toBe(true);

			// Verify it exists in DB
			const dbKeyBefore = await auth.api.getApiKey({
				query: { id: createdKey!.id },
				headers,
			});
			expect(dbKeyBefore).not.toBeNull();

			// Delete the key
			const { data: deleteResult } = await client.apiKey.delete(
				{ keyId: createdKey!.id },
				{ headers: headers },
			);

			expect(deleteResult?.success).toBe(true);

			// Should be deleted from secondary storage
			expect(store.has(`api-key:by-id:${createdKey!.id}`)).toBe(false);

			// Should be deleted from database
			let error: any = null;
			try {
				await auth.api.getApiKey({
					query: { id: createdKey!.id },
					headers,
				});
			} catch (e) {
				error = e;
			}
			expect(error).not.toBeNull();
			expect(error.status).toBe("NOT_FOUND");
		});
	});

	describe("deferUpdates option", () => {
		it("should defer updates when deferUpdates is enabled with global backgroundTasks", async () => {
			const deferredPromises: Array<Promise<unknown>> = [];
			const { auth, signInWithTestUser } = await getTestInstance({
				advanced: {
					backgroundTasks: {
						handler: (p: Promise<unknown>) => {
							deferredPromises.push(p);
						},
					},
				},
				plugins: [
					apiKey({
						deferUpdates: true,
					}),
				],
			});

			const { headers, user } = await signInWithTestUser();

			const key = await auth.api.createApiKey({
				body: { userId: user.id },
				headers,
			});

			const result = await auth.api.verifyApiKey({
				body: { key: key.key },
			});

			expect(result.valid).toBe(true);
			expect(deferredPromises.length).toBeGreaterThan(0);

			await Promise.all(deferredPromises);

			const updatedKey = await auth.api.getApiKey({
				query: { id: key.id },
				headers,
			});
			expect(updatedKey.lastRequest).not.toBeNull();
		});

		it("should still validate rate limits correctly with deferred updates", async () => {
			const deferredPromises: Array<Promise<unknown>> = [];
			const { auth, signInWithTestUser } = await getTestInstance({
				advanced: {
					backgroundTasks: {
						handler: (p: Promise<unknown>) => {
							deferredPromises.push(p);
						},
					},
				},
				plugins: [
					apiKey({
						deferUpdates: true,
						rateLimit: {
							enabled: true,
							maxRequests: 2,
							timeWindow: 60000,
						},
					}),
				],
			});

			const { headers, user } = await signInWithTestUser();

			const key = await auth.api.createApiKey({
				body: { userId: user.id },
				headers,
			});

			const result1 = await auth.api.verifyApiKey({ body: { key: key.key } });
			expect(result1.valid).toBe(true);

			await Promise.all(deferredPromises);
			deferredPromises.length = 0;

			const result2 = await auth.api.verifyApiKey({ body: { key: key.key } });
			expect(result2.valid).toBe(true);

			await Promise.all(deferredPromises);
			deferredPromises.length = 0;

			const result3 = await auth.api.verifyApiKey({ body: { key: key.key } });
			expect(result3.valid).toBe(false);
			expect(result3.error?.code).toBe("RATE_LIMITED");
			expect(result3.error).toHaveProperty("details");
			expect((result3.error as any)?.details).toHaveProperty("tryAgainIn");
		});

		it("should defer remaining count updates", async () => {
			const deferredPromises: Array<Promise<unknown>> = [];
			const { auth, signInWithTestUser } = await getTestInstance({
				advanced: {
					backgroundTasks: {
						handler: (p: Promise<unknown>) => {
							deferredPromises.push(p);
						},
					},
				},
				plugins: [
					apiKey({
						deferUpdates: true,
					}),
				],
			});

			const { headers, user } = await signInWithTestUser();

			const key = await auth.api.createApiKey({
				body: { userId: user.id, remaining: 10 },
			});

			const result = await auth.api.verifyApiKey({
				body: { key: key.key },
			});
			expect(result.valid).toBe(true);
			expect(result.key?.remaining).toBe(9);

			expect(deferredPromises.length).toBeGreaterThan(0);

			await Promise.all(deferredPromises);

			const updatedKey = await auth.api.getApiKey({
				query: { id: key.id },
				headers,
			});
			expect(updatedKey.remaining).toBe(9);
		});

		it("should not defer updates when backgroundTasks handler is not configured", async () => {
			const { auth, signInWithTestUser } = await getTestInstance({
				plugins: [
					apiKey({
						deferUpdates: true,
					}),
				],
			});

			const { headers, user } = await signInWithTestUser();

			const key = await auth.api.createApiKey({
				body: { userId: user.id },
				headers,
			});

			const result = await auth.api.verifyApiKey({
				body: { key: key.key },
			});

			expect(result.valid).toBe(true);

			// Without advanced.backgroundTasks handler, updates should happen synchronously
			const updatedKey = await auth.api.getApiKey({
				query: { id: key.id },
				headers,
			});
			expect(updatedKey.lastRequest).not.toBeNull();
		});
	});

	describe("custom storage methods", async () => {
		const customStore = new Map<string, string>();
		let customGetCalled = false;
		let customSetCalled = false;
		let customDeleteCalled = false;

		const { client, signInWithTestUser } = await getTestInstance(
			{
				// Don't provide global secondaryStorage
				plugins: [
					apiKey({
						storage: "secondary-storage",
						customStorage: {
							set(key, value, ttl) {
								customSetCalled = true;
								customStore.set(key, value);
							},
							get(key) {
								customGetCalled = true;
								return customStore.get(key) || null;
							},
							delete(key) {
								customDeleteCalled = true;
								customStore.delete(key);
							},
						},
						enableMetadata: true,
					}),
				],
			},
			{
				clientOptions: {
					plugins: [apiKeyClient()],
				},
			},
		);

		beforeEach(() => {
			customStore.clear();
			customGetCalled = false;
			customSetCalled = false;
			customDeleteCalled = false;
		});

		it("should use custom storage methods instead of global secondaryStorage", async () => {
			const { headers } = await signInWithTestUser();
			const { data: createdKey } = await client.apiKey.create(
				{},
				{ headers: headers },
			);

			expect(createdKey).not.toBeNull();
			expect(customSetCalled).toBe(true);
			expect(customStore.has(`api-key:by-id:${createdKey!.id}`)).toBe(true);
		});

		it("should use custom get method", async () => {
			const { headers } = await signInWithTestUser();
			const { data: createdKey } = await client.apiKey.create(
				{},
				{ headers: headers },
			);

			expect(createdKey).not.toBeNull();
			customGetCalled = false;

			const { data: retrievedKey } = await client.apiKey.get(
				{ query: { id: createdKey!.id } },
				{ headers: headers },
			);

			expect(retrievedKey).not.toBeNull();
			expect(customGetCalled).toBe(true);
		});

		it("should use custom delete method", async () => {
			const { headers } = await signInWithTestUser();
			const { data: createdKey } = await client.apiKey.create(
				{},
				{ headers: headers },
			);

			expect(createdKey).not.toBeNull();
			customDeleteCalled = false;

			const { data: deleteResult } = await client.apiKey.delete(
				{ keyId: createdKey!.id },
				{ headers: headers },
			);

			expect(deleteResult?.success).toBe(true);
			expect(customDeleteCalled).toBe(true);
			expect(customStore.has(`api-key:by-id:${createdKey!.id}`)).toBe(false);
		});
	});

	// =========================================================================
	// LEGACY DOUBLE-STRINGIFIED METADATA MIGRATION
	// =========================================================================

	describe("legacy double-stringified metadata migration", async () => {
		const { auth, signInWithTestUser, db } = await getTestInstance(
			{
				plugins: [
					apiKey({
						enableMetadata: true,
					}),
				],
			},
			{
				clientOptions: {
					plugins: [apiKeyClient()],
				},
			},
		);

		it("should migrate double-stringified metadata on getApiKey", async () => {
			const { headers } = await signInWithTestUser();

			// Create a key first
			const createdKey = await auth.api.createApiKey({
				body: {
					metadata: { organizationId: "test-org" },
				},
				headers,
			});

			// Pass a single-stringified value - the adapter's transform.input will stringify it again,
			// resulting in double-stringified data in the database (simulating legacy bug)
			const legacyMetadata = JSON.stringify({ organizationId: "legacy-org" });
			await db.update({
				model: "apikey",
				where: [{ field: "id", value: createdKey.id }],
				update: { metadata: legacyMetadata },
			});

			// Verify it's double-stringified in DB (adapter added extra layer of stringification)
			const rawKey = (await db.findOne({
				model: "apikey",
				where: [{ field: "id", value: createdKey.id }],
			})) as { metadata?: string } | null;
			expect(typeof rawKey?.metadata).toBe("string");

			// Read via API - should return properly parsed object
			const result = await auth.api.getApiKey({
				query: { id: createdKey.id },
				headers,
			});

			expect(result).not.toBeNull();
			expect(result.metadata).toEqual({ organizationId: "legacy-org" });
			expect(typeof result.metadata).toBe("object");

			// Verify the database was migrated (no longer double-stringified)
			// After migration, the adapter's transform.output returns the parsed object
			const migratedKey = (await db.findOne({
				model: "apikey",
				where: [{ field: "id", value: createdKey.id }],
			})) as { metadata?: Record<string, any> } | null;

			// After migration, metadata should be a properly formatted object
			expect(migratedKey?.metadata).toEqual({ organizationId: "legacy-org" });
		});

		it("should migrate double-stringified metadata on listApiKeys", async () => {
			const { headers } = await signInWithTestUser();

			// Create first key with double-stringified metadata
			const createdKey1 = await auth.api.createApiKey({
				body: {
					name: "key-1",
					metadata: { plan: "pro" },
				},
				headers,
			});

			// Create second key with double-stringified metadata
			const createdKey2 = await auth.api.createApiKey({
				body: {
					name: "key-2",
					metadata: { plan: "enterprise" },
				},
				headers,
			});

			// Pass single-stringified values - the adapter will double-stringify them
			const legacyMetadata1 = JSON.stringify({ plan: "legacy-1" });
			const legacyMetadata2 = JSON.stringify({ plan: "legacy-2" });

			await db.update({
				model: "apikey",
				where: [{ field: "id", value: createdKey1.id }],
				update: { metadata: legacyMetadata1 },
			});

			await db.update({
				model: "apikey",
				where: [{ field: "id", value: createdKey2.id }],
				update: { metadata: legacyMetadata2 },
			});

			// List via API - both keys should have properly parsed metadata objects
			const results = await auth.api.listApiKeys({ headers });

			const foundKey1 = results.apiKeys.find(
				(k: any) => k.id === createdKey1.id,
			);
			const foundKey2 = results.apiKeys.find(
				(k: any) => k.id === createdKey2.id,
			);

			expect(foundKey1).toBeDefined();
			expect(foundKey1?.metadata).toEqual({ plan: "legacy-1" });
			expect(typeof foundKey1?.metadata).toBe("object");

			expect(foundKey2).toBeDefined();
			expect(foundKey2?.metadata).toEqual({ plan: "legacy-2" });
			expect(typeof foundKey2?.metadata).toBe("object");

			// Verify the database was migrated for both keys
			const migratedKey1 = (await db.findOne({
				model: "apikey",
				where: [{ field: "id", value: createdKey1.id }],
			})) as { metadata?: Record<string, any> } | null;
			expect(migratedKey1?.metadata).toEqual({ plan: "legacy-1" });

			const migratedKey2 = (await db.findOne({
				model: "apikey",
				where: [{ field: "id", value: createdKey2.id }],
			})) as { metadata?: Record<string, any> } | null;
			expect(migratedKey2?.metadata).toEqual({ plan: "legacy-2" });
		});

		it("should migrate double-stringified metadata on updateApiKey", async () => {
			const { headers } = await signInWithTestUser();

			// Create a key first
			const createdKey = await auth.api.createApiKey({
				body: {
					name: "test-key",
					metadata: { tier: "free" },
				},
				headers,
			});

			// Pass a single-stringified value - the adapter will double-stringify it
			const legacyMetadata = JSON.stringify({ tier: "legacy-tier" });
			await db.update({
				model: "apikey",
				where: [{ field: "id", value: createdKey.id }],
				update: { metadata: legacyMetadata },
			});

			// Update via API (changing a different field, not metadata)
			const result = await auth.api.updateApiKey({
				body: {
					keyId: createdKey.id,
					name: "updated-name",
				},
				headers,
			});

			expect(result).not.toBeNull();
			expect(result.name).toBe("updated-name");
			// Metadata should be migrated and returned as object
			expect(result.metadata).toEqual({ tier: "legacy-tier" });
			expect(typeof result.metadata).toBe("object");

			// Verify the database was migrated
			const migratedKey = (await db.findOne({
				model: "apikey",
				where: [{ field: "id", value: createdKey.id }],
			})) as { metadata?: Record<string, any> } | null;
			expect(migratedKey?.metadata).toEqual({ tier: "legacy-tier" });
		});

		it("should migrate double-stringified metadata on verifyApiKey", async () => {
			const { headers } = await signInWithTestUser();

			// Create a key first
			const createdKey = await auth.api.createApiKey({
				body: {
					metadata: { scope: "read" },
				},
				headers,
			});

			// Pass a single-stringified value - the adapter will double-stringify it
			const legacyMetadata = JSON.stringify({ scope: "legacy-scope" });
			await db.update({
				model: "apikey",
				where: [{ field: "id", value: createdKey.id }],
				update: { metadata: legacyMetadata },
			});

			// Verify via API - should return properly parsed object
			const result = await auth.api.verifyApiKey({
				body: { key: createdKey.key },
			});

			expect(result.valid).toBe(true);
			expect(result.key).not.toBeNull();
			expect(result.key?.metadata).toEqual({ scope: "legacy-scope" });
			expect(typeof result.key?.metadata).toBe("object");

			// Verify the database was migrated
			const migratedKey = (await db.findOne({
				model: "apikey",
				where: [{ field: "id", value: createdKey.id }],
			})) as { metadata?: Record<string, any> } | null;
			expect(migratedKey?.metadata).toEqual({ scope: "legacy-scope" });
		});

		it("should handle already properly formatted metadata (no migration needed)", async () => {
			const { headers } = await signInWithTestUser();

			const metadata = { alreadyCorrect: true, value: 123 };

			// Create a key with proper metadata
			const createdKey = await auth.api.createApiKey({
				body: { metadata },
				headers,
			});

			// Read via API - should return the same object
			const result = await auth.api.getApiKey({
				query: { id: createdKey.id },
				headers,
			});

			expect(result.metadata).toEqual(metadata);
			expect(typeof result.metadata).toBe("object");
		});

		it("should handle null metadata gracefully", async () => {
			const { headers } = await signInWithTestUser();

			// Create a key without metadata
			const createdKey = await auth.api.createApiKey({
				body: {},
				headers,
			});

			// Read via API - should return null
			const result = await auth.api.getApiKey({
				query: { id: createdKey.id },
				headers,
			});

			expect(result.metadata).toBeNull();
		});
	});

	// =========================================================================
	// MULTIPLE CONFIGURATIONS
	// =========================================================================

	describe("multiple configurations", async () => {
		const { auth, signInWithTestUser, client } = await getTestInstance(
			{
				plugins: [
					apiKey([
						{
							configId: "public-api",
							defaultPrefix: "pub_",
							rateLimit: {
								enabled: true,
								maxRequests: 100,
								timeWindow: 60000,
							},
						},
						{
							configId: "internal-api",
							defaultPrefix: "int_",
							rateLimit: {
								enabled: true,
								maxRequests: 1000,
								timeWindow: 60000,
							},
						},
						{
							configId: "default",
							defaultPrefix: "def_",
						},
					]),
				],
			},
			{
				clientOptions: {
					plugins: [apiKeyClient()],
				},
			},
		);

		it("should create API key with specific configId", async () => {
			const { user } = await signInWithTestUser();

			const publicKey = await auth.api.createApiKey({
				body: {
					configId: "public-api",
					userId: user.id,
				},
			});

			expect(publicKey).not.toBeNull();
			expect(publicKey.configId).toBe("public-api");
			expect(publicKey.prefix).toBe("pub_");
			expect(publicKey.rateLimitMax).toBe(100);

			const internalKey = await auth.api.createApiKey({
				body: {
					configId: "internal-api",
					userId: user.id,
				},
			});

			expect(internalKey).not.toBeNull();
			expect(internalKey.configId).toBe("internal-api");
			expect(internalKey.prefix).toBe("int_");
			expect(internalKey.rateLimitMax).toBe(1000);
		});

		it("should use default config when no configId is provided", async () => {
			const { user } = await signInWithTestUser();

			const defaultKey = await auth.api.createApiKey({
				body: {
					userId: user.id,
				},
			});

			expect(defaultKey).not.toBeNull();
			expect(defaultKey.configId).toBe("default");
			expect(defaultKey.prefix).toBe("def_");
		});

		it("should list keys filtered by configId", async () => {
			const { headers, user } = await signInWithTestUser();

			// Create keys with different configs
			await auth.api.createApiKey({
				body: { configId: "public-api", userId: user.id },
			});
			await auth.api.createApiKey({
				body: { configId: "internal-api", userId: user.id },
			});
			await auth.api.createApiKey({
				body: { configId: "default", userId: user.id },
			});

			// List all keys
			const allKeys = await auth.api.listApiKeys({ headers });
			expect(allKeys.apiKeys.length).toBeGreaterThanOrEqual(3);

			// List only public-api keys
			const publicKeys = await client.apiKey.list(
				{ query: { configId: "public-api" } },
				{ headers },
			);
			expect(
				publicKeys.data?.apiKeys.every((k) => k.configId === "public-api"),
			).toBe(true);

			// List only internal-api keys
			const internalKeys = await client.apiKey.list(
				{ query: { configId: "internal-api" } },
				{ headers },
			);
			expect(
				internalKeys.data?.apiKeys.every((k) => k.configId === "internal-api"),
			).toBe(true);
		});

		it("should verify key and apply correct config rate limits", async () => {
			const { user } = await signInWithTestUser();

			const publicKey = await auth.api.createApiKey({
				body: {
					configId: "public-api",
					userId: user.id,
				},
			});

			const result = await auth.api.verifyApiKey({
				body: { key: publicKey.key },
			});

			expect(result.valid).toBe(true);
			expect(result.key?.configId).toBe("public-api");
			expect(result.key?.rateLimitMax).toBe(100);
		});

		it("should get key and resolve correct config", async () => {
			const { headers, user } = await signInWithTestUser();

			const internalKey = await auth.api.createApiKey({
				body: {
					configId: "internal-api",
					userId: user.id,
				},
			});

			const retrievedKey = await auth.api.getApiKey({
				query: { id: internalKey.id, configId: "internal-api" },
				headers,
			});

			expect(retrievedKey.configId).toBe("internal-api");
			expect(retrievedKey.prefix).toBe("int_");
		});

		it("should update key while preserving configId", async () => {
			const { headers, user } = await signInWithTestUser();

			const key = await auth.api.createApiKey({
				body: {
					configId: "public-api",
					name: "original-name",
					userId: user.id,
				},
			});

			const updatedKey = await auth.api.updateApiKey({
				body: {
					keyId: key.id,
					name: "updated-name",
					configId: "public-api",
				},
				headers,
			});

			expect(updatedKey.configId).toBe("public-api");
			expect(updatedKey.name).toBe("updated-name");
		});

		it("should delete key from specific config", async () => {
			const { headers, user } = await signInWithTestUser();

			const key = await auth.api.createApiKey({
				body: {
					configId: "internal-api",
					userId: user.id,
				},
			});

			const deleteResult = await auth.api.deleteApiKey({
				body: { keyId: key.id, configId: "internal-api" },
				headers,
			});

			expect(deleteResult.success).toBe(true);

			// Verify key is deleted
			try {
				await auth.api.getApiKey({
					query: { id: key.id, configId: "internal-api" },
					headers,
				});
				expect.fail("Should have thrown an error");
			} catch (error: any) {
				expect(isAPIError(error)).toBe(true);
			}
		});

		it("should throw error when configId array has non-unique configIds", async () => {
			expect(() =>
				apiKey([{ configId: "duplicate" }, { configId: "duplicate" }]),
			).toThrow("configId must be unique");
		});

		it("should throw error when configId is missing in array config", async () => {
			expect(() =>
				apiKey([
					{ configId: "valid" },
					{}, // Missing configId
				]),
			).toThrow("configId is required");
		});
	});

	// =========================================================================
	// ORGANIZATION-OWNED API KEYS
	// =========================================================================

	describe("organization-owned API keys", async () => {
		const { organization } = await import(
			"../../better-auth/src/plugins/organization"
		);
		const { organizationClient } = await import(
			"../../better-auth/src/plugins/organization/client"
		);

		const { auth, signInWithTestUser, client } = await getTestInstance(
			{
				plugins: [
					organization({
						async sendInvitationEmail() {},
					}),
					apiKey([
						{
							configId: "user-keys",
							defaultPrefix: "usr_",
							references: "user",
						},
						{
							configId: "org-keys",
							defaultPrefix: "org_",
							references: "organization",
						},
					]),
				],
			},
			{
				clientOptions: {
					plugins: [apiKeyClient(), organizationClient()],
				},
			},
		);

		it("should create organization-owned API key", async () => {
			const { headers } = await signInWithTestUser();

			// Create an organization
			const org = await auth.api.createOrganization({
				body: { name: "Test Org", slug: "test-org" },
				headers,
			});

			// Create org-owned API key
			const orgKey = await auth.api.createApiKey({
				body: {
					configId: "org-keys",
					organizationId: org.id,
				},
				headers,
			});

			expect(orgKey).not.toBeNull();
			expect(orgKey.configId).toBe("org-keys");
			expect(orgKey.referenceId).toBe(org.id);
			expect(orgKey.prefix).toBe("org_");
		});

		it("should create user-owned API key", async () => {
			const { user } = await signInWithTestUser();

			const userKey = await auth.api.createApiKey({
				body: {
					configId: "user-keys",
					userId: user.id,
				},
			});

			expect(userKey).not.toBeNull();
			expect(userKey.configId).toBe("user-keys");
			expect(userKey.referenceId).toBe(user.id);
			expect(userKey.prefix).toBe("usr_");
		});

		it("should fail to create org key without organizationId", async () => {
			const { user } = await signInWithTestUser();

			try {
				await auth.api.createApiKey({
					body: {
						configId: "org-keys",
						userId: user.id, // Wrong - should be organizationId
					},
				});
				expect.fail("Should have thrown an error");
			} catch (error: any) {
				expect(isAPIError(error)).toBe(true);
				expect(error.body?.code).toBe(
					ERROR_CODES.ORGANIZATION_ID_REQUIRED.code,
				);
			}
		});

		it("should verify organization-owned API key", async () => {
			const { headers } = await signInWithTestUser();

			const org = await auth.api.createOrganization({
				body: { name: "Verify Org", slug: "verify-org" },
				headers,
			});

			const orgKey = await auth.api.createApiKey({
				body: {
					configId: "org-keys",
					organizationId: org.id,
				},
				headers,
			});

			const result = await auth.api.verifyApiKey({
				body: { key: orgKey.key, configId: "org-keys" },
			});

			expect(result.valid).toBe(true);
			expect(result.key?.configId).toBe("org-keys");
			expect(result.key?.referenceId).toBe(org.id);
		});

		it("should list only user-owned keys when no organizationId provided", async () => {
			const { headers, user } = await signInWithTestUser();

			// Create an organization
			const org = await auth.api.createOrganization({
				body: { name: "List Org", slug: "list-org" },
				headers,
			});

			// Create user-owned key
			await auth.api.createApiKey({
				body: { configId: "user-keys", userId: user.id },
			});

			// Create org-owned key
			await auth.api.createApiKey({
				body: { configId: "org-keys", organizationId: org.id },
				headers,
			});

			// List from client without organizationId should only return user-owned keys
			const result = await client.apiKey.list({}, { headers });

			expect(result.data?.apiKeys).toBeDefined();
			// All returned keys should be user-owned (from user-keys config)
			result.data?.apiKeys.forEach((key) => {
				expect(key.configId).toBe("user-keys");
				expect(key.referenceId).toBe(user.id);
			});
		});

		it("should list organization-owned keys when organizationId is provided", async () => {
			const { headers, user } = await signInWithTestUser();

			// Create an organization
			const org = await auth.api.createOrganization({
				body: { name: "List Org Keys", slug: "list-org-keys" },
				headers,
			});

			// Create user-owned key
			const userKey = await auth.api.createApiKey({
				body: { configId: "user-keys", userId: user.id },
			});

			// Create org-owned keys
			const _orgKey1 = await auth.api.createApiKey({
				body: {
					configId: "org-keys",
					organizationId: org.id,
					name: "org-key-1",
				},
				headers,
			});
			const _orgKey2 = await auth.api.createApiKey({
				body: {
					configId: "org-keys",
					organizationId: org.id,
					name: "org-key-2",
				},
				headers,
			});

			// List with organizationId should only return org-owned keys
			// Note: Must use server-side API for organization-owned keys (client requests are blocked for security)
			const result = await auth.api.listApiKeys({
				query: { organizationId: org.id },
				headers,
			});

			expect(result.apiKeys).toBeDefined();
			expect(result.apiKeys.length).toBe(2);
			// All returned keys should be org-owned
			result.apiKeys.forEach((key) => {
				expect(key.configId).toBe("org-keys");
				expect(key.referenceId).toBe(org.id);
			});

			// Verify user key is not in the list
			const userKeyInList = result.apiKeys.find((k) => k.id === userKey.id);
			expect(userKeyInList).toBeUndefined();
		});

		it("should filter organization keys by configId", async () => {
			const { auth: authMultiOrg, signInWithTestUser: signIn } =
				await getTestInstance(
					{
						plugins: [
							organization({
								async sendInvitationEmail() {},
							}),
							apiKey([
								{
									configId: "org-public",
									defaultPrefix: "pub_",
									references: "organization",
								},
								{
									configId: "org-internal",
									defaultPrefix: "int_",
									references: "organization",
								},
							]),
						],
					},
					{
						clientOptions: {
							plugins: [apiKeyClient(), organizationClient()],
						},
					},
				);

			const { headers } = await signIn();

			const org = await authMultiOrg.api.createOrganization({
				body: { name: "Filter Org", slug: "filter-org" },
				headers,
			});

			// Create keys with different configs
			await authMultiOrg.api.createApiKey({
				body: { configId: "org-public", organizationId: org.id },
				headers,
			});
			await authMultiOrg.api.createApiKey({
				body: { configId: "org-internal", organizationId: org.id },
				headers,
			});

			// List only org-public keys
			// Note: Must use server-side API for organization-owned keys (client requests are blocked for security)
			const result = await authMultiOrg.api.listApiKeys({
				query: { organizationId: org.id, configId: "org-public" },
				headers,
			});

			expect(result.apiKeys).toBeDefined();
			expect(result.apiKeys.length).toBe(1);
			expect(result.apiKeys[0]?.configId).toBe("org-public");
		});

		it("should allow organization owners to manage API keys", async () => {
			const { headers } = await signInWithTestUser();

			const org = await auth.api.createOrganization({
				body: { name: "Owner Access Org", slug: "owner-access-org" },
				headers,
			});

			// Organization owners have full access to API keys
			const result = await client.apiKey.list(
				{ query: { organizationId: org.id } },
				{ headers },
			);

			// Owner should be able to list org API keys
			expect(result.error).toBeFalsy();
			expect(result.data?.apiKeys).toBeDefined();
		});

		it("should deny non-members from accessing organization API keys", async () => {
			// Create owner user and their organization
			const { headers: ownerHeaders } = await signInWithTestUser();
			const org = await auth.api.createOrganization({
				body: { name: "Non Member Org", slug: "non-member-org" },
				headers: ownerHeaders,
			});

			// Create a different user who is not a member
			const nonMemberEmail = `non-member-${Date.now()}@test.com`;
			await auth.api.signUpEmail({
				body: {
					email: nonMemberEmail,
					password: "password123",
					name: "Non Member User",
				},
			});
			const nonMemberSession = await auth.api.signInEmail({
				body: { email: nonMemberEmail, password: "password123" },
			});
			const nonMemberHeaders = {
				cookie: `better-auth.session_token=${nonMemberSession.token}`,
			};

			// Non-member should not be able to list org API keys
			const result = await client.apiKey.list(
				{ query: { organizationId: org.id } },
				{ headers: nonMemberHeaders },
			);

			// Should fail - either 403 (FORBIDDEN) for non-members or 401 (UNAUTHORIZED) for session issues
			expect(result.error).toBeDefined();
			// Non-members get FORBIDDEN, invalid session gets UNAUTHORIZED
			expect([401, 403]).toContain(result.error?.status);
		});

		it("should not allow session mocking for org-owned keys", async () => {
			const { auth: authWithSessionMocking, signInWithTestUser: signIn } =
				await getTestInstance(
					{
						plugins: [
							organization({
								async sendInvitationEmail() {},
							}),
							apiKey([
								{
									configId: "org-keys",
									defaultPrefix: "org_",
									references: "organization",
									enableSessionForAPIKeys: true, // Enable session mocking
								},
							]),
						],
					},
					{
						clientOptions: {
							plugins: [apiKeyClient(), organizationClient()],
						},
					},
				);

			const { headers } = await signIn();

			const org = await authWithSessionMocking.api.createOrganization({
				body: { name: "Session Org", slug: "session-org" },
				headers,
			});

			const orgKey = await authWithSessionMocking.api.createApiKey({
				body: {
					configId: "org-keys",
					organizationId: org.id,
				},
				headers,
			});

			// Try to use org key for session mocking - should fail
			try {
				await authWithSessionMocking.api.getSession({
					headers: {
						"x-api-key": orgKey.key,
					},
				});
				expect.fail("Should have thrown an error");
			} catch (error: any) {
				expect(isAPIError(error)).toBe(true);
				expect(error.body?.code).toBe(
					ERROR_CODES.INVALID_REFERENCE_ID_FROM_API_KEY.code,
				);
			}
		});

		it("should allow session mocking for user-owned keys only", async () => {
			const { auth: authWithSessionMocking, signInWithTestUser: signIn } =
				await getTestInstance(
					{
						plugins: [
							organization({
								async sendInvitationEmail() {},
							}),
							apiKey([
								{
									configId: "user-keys",
									defaultPrefix: "usr_",
									references: "user",
									enableSessionForAPIKeys: true, // Enable session mocking
								},
							]),
						],
					},
					{
						clientOptions: {
							plugins: [apiKeyClient(), organizationClient()],
						},
					},
				);

			const { user } = await signIn();

			const userKey = await authWithSessionMocking.api.createApiKey({
				body: {
					configId: "user-keys",
					userId: user.id,
				},
			});

			// Use user key for session mocking - should work
			const session = await authWithSessionMocking.api.getSession({
				headers: {
					"x-api-key": userKey.key,
				},
			});

			expect(session).not.toBeNull();
			expect(session?.user.id).toBe(user.id);
		});

		it("should handle mixed user and org keys in same instance", async () => {
			const { headers, user } = await signInWithTestUser();

			// Create organization
			const org = await auth.api.createOrganization({
				body: { name: "Mixed Org", slug: "mixed-org" },
				headers,
			});

			// Create both types of keys
			const userKey = await auth.api.createApiKey({
				body: { configId: "user-keys", userId: user.id },
			});

			const orgKey = await auth.api.createApiKey({
				body: { configId: "org-keys", organizationId: org.id },
				headers,
			});

			// Verify both keys work
			const userResult = await auth.api.verifyApiKey({
				body: { key: userKey.key, configId: "user-keys" },
			});
			expect(userResult.valid).toBe(true);
			expect(userResult.key?.referenceId).toBe(user.id);

			const orgResult = await auth.api.verifyApiKey({
				body: { key: orgKey.key, configId: "org-keys" },
			});
			expect(orgResult.valid).toBe(true);
			expect(orgResult.key?.referenceId).toBe(org.id);
		});

		it("should get org-owned key by id from server", async () => {
			const { headers } = await signInWithTestUser();

			const org = await auth.api.createOrganization({
				body: { name: "Get Org", slug: "get-org" },
				headers,
			});

			const orgKey = await auth.api.createApiKey({
				body: {
					configId: "org-keys",
					organizationId: org.id,
					name: "my-org-key",
				},
				headers,
			});

			// Get key by ID (server-side)
			const retrievedKey = await auth.api.getApiKey({
				query: { id: orgKey.id, configId: "org-keys" },
				headers,
			});

			expect(retrievedKey).not.toBeNull();
			expect(retrievedKey.id).toBe(orgKey.id);
			expect(retrievedKey.configId).toBe("org-keys");
			expect(retrievedKey.referenceId).toBe(org.id);
			expect(retrievedKey.name).toBe("my-org-key");
		});

		it("should delete org-owned key", async () => {
			const { headers } = await signInWithTestUser();

			const org = await auth.api.createOrganization({
				body: { name: "Delete Org", slug: "delete-org" },
				headers,
			});

			const orgKey = await auth.api.createApiKey({
				body: {
					configId: "org-keys",
					organizationId: org.id,
				},
				headers,
			});

			// Delete the key
			const deleteResult = await auth.api.deleteApiKey({
				body: { keyId: orgKey.id, configId: "org-keys" },
				headers,
			});

			expect(deleteResult.success).toBe(true);

			// Verify key is deleted
			const verifyResult = await auth.api.verifyApiKey({
				body: { key: orgKey.key, configId: "org-keys" },
			});

			expect(verifyResult.valid).toBe(false);
		});

		it("should update org-owned key", async () => {
			const { headers } = await signInWithTestUser();

			const org = await auth.api.createOrganization({
				body: { name: "Update Org", slug: "update-org" },
				headers,
			});

			const orgKey = await auth.api.createApiKey({
				body: {
					configId: "org-keys",
					organizationId: org.id,
					name: "original-name",
				},
				headers,
			});

			// Update the key
			const updatedKey = await auth.api.updateApiKey({
				body: {
					keyId: orgKey.id,
					name: "updated-name",
					enabled: false,
					configId: "org-keys",
				},
				headers,
			});

			expect(updatedKey.name).toBe("updated-name");
			expect(updatedKey.enabled).toBe(false);
			expect(updatedKey.configId).toBe("org-keys");
			expect(updatedKey.referenceId).toBe(org.id);
		});
	});
});
