import fs from "node:fs/promises";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, test, vi } from "vitest";
import { getConfig } from "../src/utils/get-config";

interface TmpDirFixture {
	tmpdir: string;
}

async function createTempDir() {
	const tmpdir = path.join(process.cwd(), "test", "getConfig_test-");
	return await fs.mkdtemp(tmpdir);
}

export const tmpdirTest = test.extend<TmpDirFixture>({
	tmpdir: async ({}, use) => {
		const directory = await createTempDir();

		await use(directory);

		await fs.rm(directory, { recursive: true });
	},
});

let tmpDir = ".";

describe("getConfig", async () => {
	beforeEach(async () => {
		const tmp = path.join(process.cwd(), "getConfig_test-");
		tmpDir = await fs.mkdtemp(tmp);
	});

	afterEach(async () => {
		await fs.rm(tmpDir, { recursive: true });
	});

	it("should resolve resolver type alias", async () => {
		const authPath = path.join(tmpDir, "server", "auth");
		const dbPath = path.join(tmpDir, "server", "db");
		await fs.mkdir(authPath, { recursive: true });
		await fs.mkdir(dbPath, { recursive: true });

		//create dummy tsconfig.json
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.json"),
			`{
              "compilerOptions": {
                /* Path Aliases */
                "paths": {
                  "@server/*": ["./server/*"]
                }
              }
					}`,
		);

		//create dummy auth.ts
		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import {betterAuth} from "better-auth";
			 import {prismaAdapter} from "better-auth/adapters/prisma";			
			 import {db} from "@server/db/db";

			 export const auth = betterAuth({
					database: prismaAdapter(db, {
							provider: 'sqlite'
					}),
					emailAndPassword: {
						enabled: true,
					}
			 })`,
		);

		//create dummy db.ts
		await fs.writeFile(
			path.join(dbPath, "db.ts"),
			`class PrismaClient {
				constructor() {}
			}
			
			export const db = new PrismaClient()`,
		);

		const config = await getConfig({
			cwd: tmpDir,
			configPath: "server/auth/auth.ts",
		});

		expect(config).not.toBe(null);
	});

	it("should resolve direct alias", async () => {
		const authPath = path.join(tmpDir, "server", "auth");
		const dbPath = path.join(tmpDir, "server", "db");
		await fs.mkdir(authPath, { recursive: true });
		await fs.mkdir(dbPath, { recursive: true });

		//create dummy tsconfig.json
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.json"),
			`{
              "compilerOptions": {
                /* Path Aliases */
                "paths": {
                  "prismaDbClient": ["./server/db/db"]
                }
              }
					}`,
		);

		//create dummy auth.ts
		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import {betterAuth} from "better-auth";
			 import {prismaAdapter} from "better-auth/adapters/prisma";			
			 import {db} from "prismaDbClient";

			 export const auth = betterAuth({
					database: prismaAdapter(db, {
							provider: 'sqlite'
					}),
					emailAndPassword: {
						enabled: true,
					}
			 })`,
		);

		//create dummy db.ts
		await fs.writeFile(
			path.join(dbPath, "db.ts"),
			`class PrismaClient {
				constructor() {}
			}
			
			export const db = new PrismaClient()`,
		);

		const config = await getConfig({
			cwd: tmpDir,
			configPath: "server/auth/auth.ts",
		});

		expect(config).not.toBe(null);
	});

	it("should resolve resolver type alias with relative path", async () => {
		const authPath = path.join(tmpDir, "test", "server", "auth");
		const dbPath = path.join(tmpDir, "test", "server", "db");
		await fs.mkdir(authPath, { recursive: true });
		await fs.mkdir(dbPath, { recursive: true });

		//create dummy tsconfig.json
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.json"),
			`{
              "compilerOptions": {
                /* Path Aliases */
                "baseUrl": "./test",
                "paths": {
                  "@server/*": ["./server/*"]
                }
              }
					}`,
		);

		//create dummy auth.ts
		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import {betterAuth} from "better-auth";
			 import {prismaAdapter} from "better-auth/adapters/prisma";			
			 import {db} from "@server/db/db";

			 export const auth = betterAuth({
					database: prismaAdapter(db, {
							provider: 'sqlite'
					}),
					emailAndPassword: {
						enabled: true,
					}
			 })`,
		);

		//create dummy db.ts
		await fs.writeFile(
			path.join(dbPath, "db.ts"),
			`class PrismaClient {
				constructor() {}
			}
			
			export const db = new PrismaClient()`,
		);

		const config = await getConfig({
			cwd: tmpDir,
			configPath: "test/server/auth/auth.ts",
		});

		expect(config).not.toBe(null);
	});

	it("should resolve direct alias with relative path", async () => {
		const authPath = path.join(tmpDir, "test", "server", "auth");
		const dbPath = path.join(tmpDir, "test", "server", "db");
		await fs.mkdir(authPath, { recursive: true });
		await fs.mkdir(dbPath, { recursive: true });

		//create dummy tsconfig.json
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.json"),
			`{
              "compilerOptions": {
                /* Path Aliases */
                "baseUrl": "./test",
                "paths": {
                  "prismaDbClient": ["./server/db/db"]
                }
              }
					}`,
		);

		//create dummy auth.ts
		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import {betterAuth} from "better-auth";
			 import {prismaAdapter} from "better-auth/adapters/prisma";			
			 import {db} from "prismaDbClient";

			 export const auth = betterAuth({
					database: prismaAdapter(db, {
							provider: 'sqlite'
					}),
					emailAndPassword: {
						enabled: true,
					}
			 })`,
		);

		//create dummy db.ts
		await fs.writeFile(
			path.join(dbPath, "db.ts"),
			`class PrismaClient {
				constructor() {}
			}
			
			export const db = new PrismaClient()`,
		);

		const config = await getConfig({
			cwd: tmpDir,
			configPath: "test/server/auth/auth.ts",
		});

		expect(config).not.toBe(null);
	});

	it("should resolve with relative import", async () => {
		const authPath = path.join(tmpDir, "test", "server", "auth");
		const dbPath = path.join(tmpDir, "test", "server", "db");
		await fs.mkdir(authPath, { recursive: true });
		await fs.mkdir(dbPath, { recursive: true });

		//create dummy tsconfig.json
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.json"),
			`{
              "compilerOptions": {
                /* Path Aliases */
                "baseUrl": "./test",
                "paths": {
                  "prismaDbClient": ["./server/db/db"]
                }
              }
					}`,
		);

		//create dummy auth.ts
		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import {betterAuth} from "better-auth";
			 import {prismaAdapter} from "better-auth/adapters/prisma";			
			 import {db} from "../db/db";

			 export const auth = betterAuth({
					database: prismaAdapter(db, {
							provider: 'sqlite'
					}),
					emailAndPassword: {
						enabled: true,
					}
			 })`,
		);

		//create dummy db.ts
		await fs.writeFile(
			path.join(dbPath, "db.ts"),
			`class PrismaClient {
				constructor() {}
			}
			
			export const db = new PrismaClient()`,
		);

		const config = await getConfig({
			cwd: tmpDir,
			configPath: "test/server/auth/auth.ts",
		});

		expect(config).not.toBe(null);
	});

	it("should error with invalid alias", async () => {
		const authPath = path.join(tmpDir, "server", "auth");
		const dbPath = path.join(tmpDir, "server", "db");
		await fs.mkdir(authPath, { recursive: true });
		await fs.mkdir(dbPath, { recursive: true });

		//create dummy tsconfig.json
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.json"),
			`{
              "compilerOptions": {
                /* Path Aliases */
                "paths": {
                  "@server/*": ["./PathIsInvalid/*"]
                }
              }
					}`,
		);

		//create dummy auth.ts
		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import {betterAuth} from "better-auth";
			 import {prismaAdapter} from "better-auth/adapters/prisma";			
			 import {db} from "@server/db/db";

			 export const auth = betterAuth({
					database: prismaAdapter(db, {
							provider: 'sqlite'
					}),
					emailAndPassword: {
						enabled: true,
					}
			 })`,
		);

		//create dummy db.ts
		await fs.writeFile(
			path.join(dbPath, "db.ts"),
			`class PrismaClient {
				constructor() {}
			}
			
			export const db = new PrismaClient()`,
		);

		const consoleErrorSpy = vi
			.spyOn(console, "error")
			.mockImplementation(() => {});

		await expect(() =>
			getConfig({ cwd: tmpDir, configPath: "server/auth/auth.ts" }),
		).rejects.toThrowError();

		expect(consoleErrorSpy).toHaveBeenCalledWith(
			expect.stringContaining("Couldn't read your auth config."),
			expect.objectContaining({
				code: "MODULE_NOT_FOUND",
			}),
		);
	});

	it("should resolve js config", async () => {
		const authPath = path.join(tmpDir, "server", "auth");
		const dbPath = path.join(tmpDir, "server", "db");
		await fs.mkdir(authPath, { recursive: true });
		await fs.mkdir(dbPath, { recursive: true });

		//create dummy auth.ts
		await fs.writeFile(
			path.join(authPath, "auth.js"),
			`import  { betterAuth } from "better-auth";

			 export const auth = betterAuth({
					emailAndPassword: {
						enabled: true,
					}
			 })`,
		);
		const config = await getConfig({
			cwd: tmpDir,
			configPath: "server/auth/auth.js",
		});
		expect(config).toMatchObject({
			emailAndPassword: { enabled: true },
		});
	});

	it("should resolve path aliases from referenced tsconfig files", async () => {
		const authPath = path.join(tmpDir, "apps", "web", "server", "auth");
		const dbPath = path.join(tmpDir, "packages", "shared", "db");
		await fs.mkdir(authPath, { recursive: true });
		await fs.mkdir(dbPath, { recursive: true });

		// Create root tsconfig.json with references
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.json"),
			`{
				"references": [
					{ "path": "./apps/web" },
					{ "path": "./packages/shared" }
				]
			}`,
		);

		// Create web app tsconfig.json with aliases
		await fs.writeFile(
			path.join(tmpDir, "apps", "web", "tsconfig.json"),
			`{
				"compilerOptions": {
					"paths": {
						"@web/*": ["./server/*"]
					}
				}
			}`,
		);

		// Create shared package tsconfig.json with aliases
		await fs.writeFile(
			path.join(tmpDir, "packages", "shared", "tsconfig.json"),
			`{
				"compilerOptions": {
					"paths": {
						"@shared/*": ["./db/*"]
					}
				}
			}`,
		);

		// Create dummy auth.ts using both aliases
		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import {betterAuth} from "better-auth";
			 import {prismaAdapter} from "better-auth/adapters/prisma";			
			 import {db} from "@shared/db";

			 export const auth = betterAuth({
					database: prismaAdapter(db, {
							provider: 'sqlite'
					}),
					emailAndPassword: {
						enabled: true,
					}
			 })`,
		);

		// Create dummy db.ts
		await fs.writeFile(
			path.join(dbPath, "db.ts"),
			`class PrismaClient {
				constructor() {}
			}
			
			export const db = new PrismaClient()`,
		);

		const config = await getConfig({
			cwd: tmpDir,
			configPath: "apps/web/server/auth/auth.ts",
		});

		expect(config).toMatchObject({
			emailAndPassword: { enabled: true },
			database: expect.objectContaining({
				// This proves the @shared/db alias was resolved correctly
			}),
		});
	});

	it("should handle missing referenced tsconfig files gracefully", async () => {
		const authPath = path.join(tmpDir, "server", "auth");
		await fs.mkdir(authPath, { recursive: true });

		// Create root tsconfig.json with reference to non-existent file
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.json"),
			`{
				"compilerOptions": {
					"paths": {
						"@server/*": ["./server/*"]
					}
				},
				"references": [
					{ "path": "./non-existent" }
				]
			}`,
		);

		// Create dummy auth.ts
		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import {betterAuth} from "better-auth";

			 export const auth = betterAuth({
					emailAndPassword: {
						enabled: true,
					}
			 })`,
		);

		const config = await getConfig({
			cwd: tmpDir,
			configPath: "server/auth/auth.ts",
		});

		expect(config).not.toBe(null);
	});

	it("should handle circular references in tsconfig files", async () => {
		const authPath = path.join(tmpDir, "server", "auth");
		const appPath = path.join(tmpDir, "app");
		await fs.mkdir(authPath, { recursive: true });
		await fs.mkdir(appPath, { recursive: true });

		// Create root tsconfig.json that references app tsconfig
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.json"),
			`{
				"compilerOptions": {
					"paths": {
						"@root/*": ["./server/*"]
					}
				},
				"references": [
					{ "path": "./app" }
				]
			}`,
		);

		// Create app tsconfig.json that references back to root
		await fs.writeFile(
			path.join(tmpDir, "app", "tsconfig.json"),
			`{
				"compilerOptions": {
					"paths": {
						"@app/*": ["./src/*"]
					}
				},
				"references": [
					{ "path": ".." }
				]
			}`,
		);

		// Create dummy auth.ts
		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import {betterAuth} from "better-auth";

			 export const auth = betterAuth({
					emailAndPassword: {
						enabled: true,
					}
			 })`,
		);

		const config = await getConfig({
			cwd: tmpDir,
			configPath: "server/auth/auth.ts",
		});

		expect(config).not.toBe(null);
	});

	it("should resolve direct tsconfig file references", async () => {
		const authPath = path.join(tmpDir, "server", "auth");
		const sharedPath = path.join(tmpDir, "shared", "db");
		await fs.mkdir(authPath, { recursive: true });
		await fs.mkdir(sharedPath, { recursive: true });

		// Create root tsconfig.json with direct file references
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.json"),
			`{
				"references": [
					{ "path": "./tsconfig.app.json" },
					{ "path": "./tsconfig.shared.json" }
				]
			}`,
		);

		// Create tsconfig.app.json with app-specific aliases
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.app.json"),
			`{
				"compilerOptions": {
					"paths": {
						"@app/*": ["./server/*"]
					}
				}
			}`,
		);

		// Create tsconfig.shared.json with shared aliases
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.shared.json"),
			`{
				"compilerOptions": {
					"paths": {
						"@shared/*": ["./shared/*"]
					}
				}
			}`,
		);

		// Create dummy auth.ts using both aliases
		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import {betterAuth} from "better-auth";
			 import {prismaAdapter} from "better-auth/adapters/prisma";			
			 import {db} from "@shared/db/db";

			 export const auth = betterAuth({
					database: prismaAdapter(db, {
							provider: 'sqlite'
					}),
					emailAndPassword: {
						enabled: true,
					}
			 })`,
		);

		// Create dummy db.ts
		await fs.writeFile(
			path.join(sharedPath, "db.ts"),
			`class PrismaClient {
				constructor() {}
			}
			
			export const db = new PrismaClient()`,
		);

		const config = await getConfig({
			cwd: tmpDir,
			configPath: "server/auth/auth.ts",
		});

		expect(config).not.toBe(null);
	});

	it("should handle mixed directory and file references", async () => {
		const authPath = path.join(tmpDir, "apps", "web", "server", "auth");
		const utilsPath = path.join(tmpDir, "packages", "utils");
		await fs.mkdir(authPath, { recursive: true });
		await fs.mkdir(utilsPath, { recursive: true });

		// Create root tsconfig.json with mixed references
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.json"),
			`{
				"references": [
					{ "path": "./apps/web" },
					{ "path": "./tsconfig.utils.json" }
				]
			}`,
		);

		// Create web app directory-based tsconfig
		await fs.writeFile(
			path.join(tmpDir, "apps", "web", "tsconfig.json"),
			`{
				"compilerOptions": {
					"paths": {
						"@web/*": ["./server/*"]
					}
				}
			}`,
		);

		// Create utils file-based tsconfig
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.utils.json"),
			`{
				"compilerOptions": {
					"paths": {
						"@utils/*": ["./packages/utils/*"]
					}
				}
			}`,
		);

		// Create dummy auth.ts
		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import {betterAuth} from "better-auth";

			 export const auth = betterAuth({
					emailAndPassword: {
						enabled: true,
					}
			 })`,
		);

		// Create dummy utils file
		await fs.writeFile(
			path.join(utilsPath, "index.ts"),
			`export const utils = {}`,
		);

		const config = await getConfig({
			cwd: tmpDir,
			configPath: "apps/web/server/auth/auth.ts",
		});

		expect(config).not.toBe(null);
	});
	it("should resolve SvelteKit $lib/server imports correctly", async () => {
		const authPath = path.join(tmpDir, "src");
		const libServerPath = path.join(tmpDir, "src", "lib", "server");
		await fs.mkdir(authPath, { recursive: true });
		await fs.mkdir(libServerPath, { recursive: true });

		await fs.writeFile(
			path.join(tmpDir, "package.json"),
			JSON.stringify({
				name: "test-sveltekit",
				devDependencies: {
					"@sveltejs/kit": "^2.0.0",
				},
			}),
		);

		await fs.writeFile(
			path.join(libServerPath, "database.ts"),
			`export const db = {
				// Mock database client
				connect: () => console.log('Connected to database')
			};`,
		);

		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import { betterAuth } from "better-auth";
			 import { db } from "$lib/server/database";

			 export const auth = betterAuth({
					emailAndPassword: {
						enabled: true,
					}
			 })`,
		);

		const config = await getConfig({
			cwd: tmpDir,
			configPath: "src/auth.ts",
		});

		expect(config).not.toBe(null);
		expect(config).toMatchObject({
			emailAndPassword: { enabled: true },
		});
	});

	it("should resolve export default auth", async () => {
		const authPath = path.join(tmpDir, "server", "auth");
		await fs.mkdir(authPath, { recursive: true });

		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import { betterAuth } from "better-auth";

			 const auth = betterAuth({
					emailAndPassword: {
						enabled: true,
					},
					socialProviders: {
						github: {
							clientId: "test-id",
							clientSecret: "test-secret"
						}
					}
			 });
			 
			 export default auth;`,
		);

		const config = await getConfig({
			cwd: tmpDir,
			configPath: "server/auth/auth.ts",
		});

		expect(config).not.toBe(null);
		expect(config).toMatchObject({
			emailAndPassword: { enabled: true },
			socialProviders: {
				github: {
					clientId: "test-id",
					clientSecret: "test-secret",
				},
			},
		});
	});

	it("should resolve export default auth with named export", async () => {
		const authPath = path.join(tmpDir, "server", "auth");
		await fs.mkdir(authPath, { recursive: true });

		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import { betterAuth } from "better-auth";

			 const auth = betterAuth({
					emailAndPassword: {
						enabled: true,
					},
					socialProviders: {
						github: {
							clientId: "test-id",
							clientSecret: "test-secret"
						}
					}
			 });
			 
			 export { auth };

			 export default auth;`,
		);

		const config = await getConfig({
			cwd: tmpDir,
			configPath: "server/auth/auth.ts",
		});

		expect(config).not.toBe(null);
		expect(config).toMatchObject({
			emailAndPassword: { enabled: true },
			socialProviders: {
				github: {
					clientId: "test-id",
					clientSecret: "test-secret",
				},
			},
		});
	});

	it("should resolve export default with inline betterAuth call", async () => {
		const authPath = path.join(tmpDir, "server", "auth");
		await fs.mkdir(authPath, { recursive: true });

		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import { betterAuth } from "better-auth";

			 export default betterAuth({
					emailAndPassword: {
						enabled: true,
					},
					trustedOrigins: ["http://localhost:3000"]
			 });`,
		);

		const config = await getConfig({
			cwd: tmpDir,
			configPath: "server/auth/auth.ts",
		});

		expect(config).not.toBe(null);
		expect(config).toMatchObject({
			emailAndPassword: { enabled: true },
			trustedOrigins: ["http://localhost:3000"],
		});
	});
	it("should load configs importing cloudflare workers", async () => {
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.json"),
			`{
				"compilerOptions": {}
			}`,
		);

		await fs.writeFile(
			path.join(tmpDir, "auth.ts"),
			`import { betterAuth } from "better-auth";
			 import { env } from "cloudflare:workers";

			 export const auth = betterAuth({
					emailAndPassword: {
						enabled: !!env.FLAG,
					},
			 });`,
		);

		const config = await getConfig({
			cwd: tmpDir,
			configPath: "auth.ts",
		});

		expect(config).not.toBe(null);
		expect(config?.emailAndPassword?.enabled).toBe(true);
	});

	/**
	 * @see https://github.com/better-auth/better-auth/issues/6373
	 */
	it("should resolve path aliases from extended tsconfig", async () => {
		const authPath = path.join(tmpDir, "src", "auth");
		const dbPath = path.join(tmpDir, "src", "db");
		await fs.mkdir(authPath, { recursive: true });
		await fs.mkdir(dbPath, { recursive: true });

		// Create base tsconfig with path aliases
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.base.json"),
			`{
				"compilerOptions": {
					"paths": {
						"@src/*": ["./src/*"]
					}
				}
			}`,
		);

		// Create tsconfig.json that extends base
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.json"),
			`{
				"extends": "./tsconfig.base.json"
			}`,
		);

		// Create dummy db.ts
		await fs.writeFile(
			path.join(dbPath, "db.ts"),
			`class PrismaClient {
				constructor() {}
			}
			export const db = new PrismaClient()`,
		);

		// Create auth.ts using the alias from extended config
		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import {betterAuth} from "better-auth";
			 import {prismaAdapter} from "better-auth/adapters/prisma";
			 import {db} from "@src/db/db";

			 export const auth = betterAuth({
					database: prismaAdapter(db, {
							provider: 'sqlite'
					}),
					emailAndPassword: {
						enabled: true,
					}
			 })`,
		);

		const config = await getConfig({
			cwd: tmpDir,
			configPath: "src/auth/auth.ts",
		});

		expect(config).not.toBe(null);
		expect(config).toMatchObject({
			emailAndPassword: { enabled: true },
		});
	});

	/**
	 * @see https://github.com/better-auth/better-auth/issues/6373
	 */
	it("should resolve path aliases from chained extends", async () => {
		const authPath = path.join(tmpDir, "src", "auth");
		const dbPath = path.join(tmpDir, "src", "db");
		await fs.mkdir(authPath, { recursive: true });
		await fs.mkdir(dbPath, { recursive: true });

		// Create grandparent tsconfig with path aliases
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.root.json"),
			`{
				"compilerOptions": {
					"paths": {
						"@server/*": ["./src/*"]
					}
				}
			}`,
		);

		// Create parent tsconfig that extends grandparent
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.base.json"),
			`{
				"extends": "./tsconfig.root.json"
			}`,
		);

		// Create tsconfig.json that extends parent
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.json"),
			`{
				"extends": "./tsconfig.base.json"
			}`,
		);

		// Create dummy db.ts
		await fs.writeFile(
			path.join(dbPath, "db.ts"),
			`class PrismaClient {
				constructor() {}
			}
			export const db = new PrismaClient()`,
		);

		// Create auth.ts using the alias from grandparent
		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import {betterAuth} from "better-auth";
			 import {prismaAdapter} from "better-auth/adapters/prisma";
			 import {db} from "@server/db/db";

			 export const auth = betterAuth({
					database: prismaAdapter(db, {
							provider: 'sqlite'
					}),
					emailAndPassword: {
						enabled: true,
					}
			 })`,
		);

		const config = await getConfig({
			cwd: tmpDir,
			configPath: "src/auth/auth.ts",
		});

		expect(config).not.toBe(null);
		expect(config).toMatchObject({
			emailAndPassword: { enabled: true },
		});
	});

	/**
	 * @see https://github.com/better-auth/better-auth/issues/8933
	 */
	it("should resolve extended tsconfig paths relative to the extended file's directory", async () => {
		// Simulates SvelteKit structure: root tsconfig.json extends
		// .svelte-kit/tsconfig.json whose paths use "../src/lib" (relative
		// to the .svelte-kit directory, not the project root).
		//
		// With the bug, `$lib` is resolved relative to the root tsconfig's
		// directory (projectRoot), producing `<parent>/src/lib`. To catch the
		// regression reliably, place a poisoned module at that wrong path
		// that throws on import — the test only passes if the CORRECT path
		// (`projectRoot/src/lib`) is used.
		const projectRoot = path.join(tmpDir, "app");
		const authPath = path.join(projectRoot, "src");
		const correctLibServer = path.join(projectRoot, "src", "lib", "server");
		const wrongLibServer = path.join(tmpDir, "src", "lib", "server");
		const svelteKitDir = path.join(projectRoot, ".svelte-kit");
		await fs.mkdir(authPath, { recursive: true });
		await fs.mkdir(correctLibServer, { recursive: true });
		await fs.mkdir(wrongLibServer, { recursive: true });
		await fs.mkdir(svelteKitDir, { recursive: true });

		await fs.writeFile(
			path.join(svelteKitDir, "tsconfig.json"),
			`{
				"compilerOptions": {
					"paths": {
						"$lib": ["../src/lib"],
						"$lib/*": ["../src/lib/*"]
					}
				}
			}`,
		);

		await fs.writeFile(
			path.join(projectRoot, "tsconfig.json"),
			`{
				"extends": "./.svelte-kit/tsconfig.json"
			}`,
		);

		await fs.writeFile(
			path.join(correctLibServer, "database.ts"),
			`export const db = "correct-db";`,
		);

		// Poisoned module at the wrong location: throws on import.
		await fs.writeFile(
			path.join(wrongLibServer, "database.ts"),
			`throw new Error("wrong path resolved: $lib should not point here");`,
		);

		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import { betterAuth } from "better-auth";
			 import { db } from "$lib/server/database";

			 export const auth = betterAuth({
					emailAndPassword: {
						enabled: true,
					},
					appName: db,
			 })`,
		);

		const config = await getConfig({
			cwd: projectRoot,
			configPath: "src/auth.ts",
			shouldThrowOnError: true,
		});

		expect(config).not.toBe(null);
		expect(config).toMatchObject({
			emailAndPassword: { enabled: true },
			appName: "correct-db",
		});
	});

	/**
	 * @see https://github.com/better-auth/better-auth/issues/6373
	 */
	it("should let child paths override parent paths from extends", async () => {
		const authPath = path.join(tmpDir, "server", "auth");
		const dbPath = path.join(tmpDir, "server", "db");
		const oldDbPath = path.join(tmpDir, "old", "db");
		await fs.mkdir(authPath, { recursive: true });
		await fs.mkdir(dbPath, { recursive: true });
		await fs.mkdir(oldDbPath, { recursive: true });

		// Create parent tsconfig with path aliases pointing to old location
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.base.json"),
			`{
				"compilerOptions": {
					"paths": {
						"@server/*": ["./old/*"]
					}
				}
			}`,
		);

		// Create child tsconfig that overrides the alias
		await fs.writeFile(
			path.join(tmpDir, "tsconfig.json"),
			`{
				"extends": "./tsconfig.base.json",
				"compilerOptions": {
					"paths": {
						"@server/*": ["./server/*"]
					}
				}
			}`,
		);

		// Create dummy db.ts in the correct (overridden) location
		await fs.writeFile(
			path.join(dbPath, "db.ts"),
			`class PrismaClient {
				constructor() {}
			}
			export const db = new PrismaClient()`,
		);

		// Create auth.ts
		await fs.writeFile(
			path.join(authPath, "auth.ts"),
			`import {betterAuth} from "better-auth";
			 import {prismaAdapter} from "better-auth/adapters/prisma";
			 import {db} from "@server/db/db";

			 export const auth = betterAuth({
					database: prismaAdapter(db, {
							provider: 'sqlite'
					}),
					emailAndPassword: {
						enabled: true,
					}
			 })`,
		);

		const config = await getConfig({
			cwd: tmpDir,
			configPath: "server/auth/auth.ts",
		});

		expect(config).not.toBe(null);
		expect(config).toMatchObject({
			emailAndPassword: { enabled: true },
		});
	});
});

/**
 * Regression suite for user-reported `compilerOptions.paths` scenarios. Tests
 * use the `tmpdirTest` fixture for isolation and `shouldThrowOnError: true`
 * so resolution failures surface as real assertion failures. Poisoned modules
 * placed at wrong paths catch silently-incorrect rewrites.
 */
describe("tsconfig paths resolution (user-reported scenarios)", () => {
	async function writeTree(
		root: string,
		files: Record<string, string>,
	): Promise<void> {
		for (const [relPath, content] of Object.entries(files)) {
			const abs = path.join(root, relPath);
			await fs.mkdir(path.dirname(abs), { recursive: true });
			await fs.writeFile(abs, content);
		}
	}

	/**
	 * SvelteKit extending `.svelte-kit/tsconfig.json`, with both default and
	 * namespace imports against `$lib`.
	 * @see https://github.com/better-auth/better-auth/issues/8933
	 */
	tmpdirTest(
		"resolves $lib imports in an extends setup with default and namespace forms",
		async ({ tmpdir, expect }) => {
			const projectRoot = path.join(tmpdir, "my-app");
			await writeTree(tmpdir, {
				// Poisoned modules one level above the project root.
				"src/lib/server/db.ts": `throw new Error("wrong path: $lib resolved to tmpdir parent");`,
				"src/lib/server/db/schema.ts": `throw new Error("wrong path: $lib/server/db/schema resolved to tmpdir parent");`,

				"my-app/.svelte-kit/tsconfig.json": `{
					"compilerOptions": {
						"paths": {
							"$lib": ["../src/lib"],
							"$lib/*": ["../src/lib/*"]
						}
					}
				}`,
				"my-app/tsconfig.json": `{
					"extends": "./.svelte-kit/tsconfig.json"
				}`,
				"my-app/src/lib/server/db.ts": `export const db = "my-app-db";`,
				"my-app/src/lib/server/db/schema.ts": `export const usersTable = "my-app-users";`,
				"my-app/src/auth.ts": `
					import { betterAuth } from "better-auth";
					import { db } from "$lib/server/db";
					import * as schema from "$lib/server/db/schema";
					const [version] = Object.values(schema);
					export const auth = betterAuth({
						appName: db + ":" + version,
						emailAndPassword: { enabled: true },
					});
				`,
			});

			const config = await getConfig({
				cwd: projectRoot,
				configPath: "src/auth.ts",
				shouldThrowOnError: true,
			});

			expect(config).toMatchObject({
				appName: "my-app-db:my-app-users",
				emailAndPassword: { enabled: true },
			});
		},
	);

	/**
	 * SvelteKit nested in a monorepo subdirectory with no tsconfig at the
	 * repo root.
	 * @see https://github.com/better-auth/better-auth/issues/8933#issuecomment-4189080622
	 */
	tmpdirTest(
		"resolves $lib imports when SvelteKit lives in a monorepo subdirectory",
		async ({ tmpdir, expect }) => {
			const websiteRoot = path.join(tmpdir, "mc-id", "website");
			await writeTree(tmpdir, {
				"mc-id/src/lib/server/email-service.ts": `throw new Error("wrong path: $lib resolved to monorepo root");`,

				"mc-id/website/.svelte-kit/tsconfig.json": `{
					"compilerOptions": {
						"paths": {
							"$lib": ["../src/lib"],
							"$lib/*": ["../src/lib/*"]
						}
					}
				}`,
				"mc-id/website/tsconfig.json": `{
					"extends": "./.svelte-kit/tsconfig.json"
				}`,
				"mc-id/website/src/lib/server/email-service.ts": `export const service = "website-email-service";`,
				"mc-id/website/src/lib/server/auth.ts": `
					import { betterAuth } from "better-auth";
					import { service } from "$lib/server/email-service";
					export const auth = betterAuth({
						appName: service,
						emailAndPassword: { enabled: true },
					});
				`,
			});

			const config = await getConfig({
				cwd: websiteRoot,
				configPath: "src/lib/server/auth.ts",
				shouldThrowOnError: true,
			});

			expect(config).toMatchObject({
				appName: "website-email-service",
				emailAndPassword: { enabled: true },
			});
		},
	);

	/**
	 * Alias key with a trailing `*` mapped to a substitution template with a
	 * mid-path `*`, resolving across multiple sibling packages.
	 * @see https://github.com/better-auth/better-auth/pull/9020
	 */
	tmpdirTest(
		"resolves mid-path wildcard substitutions across multiple sibling packages",
		async ({ tmpdir, expect }) => {
			await writeTree(tmpdir, {
				"tsconfig.json": `{
					"compilerOptions": {
						"paths": {
							"@web/*": ["./libs/web/*/src/index.ts"]
						}
					}
				}`,
				"libs/web/ui-kit/src/index.ts": `export const name = "ui-kit";`,
				"libs/web/data-sdk/src/index.ts": `export const name = "data-sdk";`,
				"libs/web/auth-sdk/src/index.ts": `export const name = "auth-sdk";`,
				// Sibling without src/index.ts — must not resolve to a candidate.
				"libs/web/legacy/README.md": `not a package`,
				"apps/web/src/auth.ts": `
					import { betterAuth } from "better-auth";
					import { name as uiKit } from "@web/ui-kit";
					import { name as dataSdk } from "@web/data-sdk";
					import { name as authSdk } from "@web/auth-sdk";
					export const auth = betterAuth({
						appName: [uiKit, dataSdk, authSdk].join(","),
						emailAndPassword: { enabled: true },
					});
				`,
			});

			const config = await getConfig({
				cwd: tmpdir,
				configPath: "apps/web/src/auth.ts",
				shouldThrowOnError: true,
			});

			expect(config).toMatchObject({
				appName: "ui-kit,data-sdk,auth-sdk",
				emailAndPassword: { enabled: true },
			});
		},
	);

	/**
	 * `*` must capture strings containing `/` (TypeScript canonical behavior).
	 * @see https://github.com/microsoft/TypeScript/blob/main/src/compiler/moduleNameResolver.ts
	 */
	tmpdirTest(
		"captures multi-segment star when the import specifier spans `/`",
		async ({ tmpdir, expect }) => {
			await writeTree(tmpdir, {
				"tsconfig.json": `{
					"compilerOptions": {
						"paths": {
							"@scope/*": ["./packages/*/src/index.ts"]
						}
					}
				}`,
				"packages/feature/nested/src/index.ts": `export const name = "nested-feature";`,
				"src/auth.ts": `
					import { betterAuth } from "better-auth";
					import { name } from "@scope/feature/nested";
					export const auth = betterAuth({
						appName: name,
						emailAndPassword: { enabled: true },
					});
				`,
			});

			const config = await getConfig({
				cwd: tmpdir,
				configPath: "src/auth.ts",
				shouldThrowOnError: true,
			});

			expect(config).toMatchObject({
				appName: "nested-feature",
				emailAndPassword: { enabled: true },
			});
		},
	);

	/**
	 * When multiple substitutions exist and all resolve, the first declared
	 * one wins. Both candidate files exist so ordering is the only signal.
	 */
	tmpdirTest(
		"picks the first substitution candidate in declaration order when multiple exist",
		async ({ tmpdir, expect }) => {
			await writeTree(tmpdir, {
				"tsconfig.json": `{
					"compilerOptions": {
						"paths": {
							"@db": [
								"./primary/db.ts",
								"./secondary/db.ts"
							]
						}
					}
				}`,
				"primary/db.ts": `export const db = "primary";`,
				"secondary/db.ts": `export const db = "secondary";`,
				"src/auth.ts": `
					import { betterAuth } from "better-auth";
					import { db } from "@db";
					export const auth = betterAuth({
						appName: db,
						emailAndPassword: { enabled: true },
					});
				`,
			});

			const config = await getConfig({
				cwd: tmpdir,
				configPath: "src/auth.ts",
				shouldThrowOnError: true,
			});

			expect(config).toMatchObject({
				appName: "primary",
				emailAndPassword: { enabled: true },
			});
		},
	);

	/** Falls through to the next substitution when the first does not exist. */
	tmpdirTest(
		"falls through to the next substitution when the first does not exist on disk",
		async ({ tmpdir, expect }) => {
			await writeTree(tmpdir, {
				"tsconfig.json": `{
					"compilerOptions": {
						"paths": {
							"@db": [
								"./missing/primary/db.ts",
								"./fallback/secondary/db.ts"
							]
						}
					}
				}`,
				"fallback/secondary/db.ts": `export const db = "secondary-fallback";`,
				"src/auth.ts": `
					import { betterAuth } from "better-auth";
					import { db } from "@db";
					export const auth = betterAuth({
						appName: db,
						emailAndPassword: { enabled: true },
					});
				`,
			});

			const config = await getConfig({
				cwd: tmpdir,
				configPath: "src/auth.ts",
				shouldThrowOnError: true,
			});

			expect(config).toMatchObject({
				appName: "secondary-fallback",
				emailAndPassword: { enabled: true },
			});
		},
	);

	/**
	 * Covers static `import`, `export * from`, and dynamic `import()` across
	 * the same mid-path wildcard alias.
	 */
	tmpdirTest(
		"rewrites aliased specifiers across import, re-export, and dynamic import AST nodes",
		async ({ tmpdir, expect }) => {
			await writeTree(tmpdir, {
				"tsconfig.json": `{
					"compilerOptions": {
						"paths": {
							"@pkg/*": ["./packages/*/src/index.ts"]
						}
					}
				}`,
				"packages/primary/src/index.ts": `export const primary = "primary-value";`,
				"packages/secondary/src/index.ts": `export const secondary = "secondary-value";`,
				"packages/barrel/src/index.ts": `export * from "@pkg/primary";`,
				"src/auth.ts": `
					import { betterAuth } from "better-auth";
					import { primary } from "@pkg/barrel";
					const mod = await import("@pkg/secondary");
					export const auth = betterAuth({
						appName: primary + "/" + mod.secondary,
						emailAndPassword: { enabled: true },
					});
				`,
			});

			const config = await getConfig({
				cwd: tmpdir,
				configPath: "src/auth.ts",
				shouldThrowOnError: true,
			});

			expect(config).toMatchObject({
				appName: "primary-value/secondary-value",
				emailAndPassword: { enabled: true },
			});
		},
	);

	/** Source files with modern ESM/CJS/TS extensions must all be resolvable. */
	tmpdirTest.for([
		{ ext: ".ts", body: `export const value = "ts";` },
		{ ext: ".tsx", body: `export const value = "tsx";` },
		{ ext: ".mts", body: `export const value = "mts";` },
		{ ext: ".cts", body: `export const value = "cts";` },
		{ ext: ".js", body: `export const value = "js";` },
		{ ext: ".jsx", body: `export const value = "jsx";` },
		{ ext: ".mjs", body: `export const value = "mjs";` },
		{ ext: ".cjs", body: `module.exports = { value: "cjs" };` },
	])(
		"resolves aliased files ending in $ext",
		async ({ ext, body }, { tmpdir, expect }) => {
			await writeTree(tmpdir, {
				"tsconfig.json": `{
					"compilerOptions": {
						"paths": {
							"@lib": ["./src/lib"]
						}
					}
				}`,
				[`src/lib${ext}`]: body,
				"src/auth.ts": `
					import { betterAuth } from "better-auth";
					import { value } from "@lib";
					export const auth = betterAuth({
						appName: value,
						emailAndPassword: { enabled: true },
					});
				`,
			});

			const config = await getConfig({
				cwd: tmpdir,
				configPath: "src/auth.ts",
				shouldThrowOnError: true,
			});

			expect(config).toMatchObject({
				appName: ext.slice(1),
				emailAndPassword: { enabled: true },
			});
		},
	);
});
