/**
 * Centralized Prisma Client Singleton
 * Prevents multiple Prisma clients from creating connection leaks
 */

const { PrismaClient } = require("@prisma/client");

// Parse DATABASE_URL and add connection pooling parameters
function getOptimizedDatabaseUrl() {
	const originalUrl = process.env.DATABASE_URL;

	if (!originalUrl) {
		throw new Error("DATABASE_URL environment variable is required");
	}

	// Parse the URL
	const url = new URL(originalUrl);

	// Add connection pooling parameters - configurable via environment variables
	const connectionLimit = process.env.DB_CONNECTION_LIMIT || "30";
	const poolTimeout = process.env.DB_POOL_TIMEOUT || "20";
	const connectTimeout = process.env.DB_CONNECT_TIMEOUT || "10";
	const idleTimeout = process.env.DB_IDLE_TIMEOUT || "300";
	const maxLifetime = process.env.DB_MAX_LIFETIME || "1800";

	url.searchParams.set("connection_limit", connectionLimit);
	url.searchParams.set("pool_timeout", poolTimeout);
	url.searchParams.set("connect_timeout", connectTimeout);
	url.searchParams.set("idle_timeout", idleTimeout);
	url.searchParams.set("max_lifetime", maxLifetime);

	// Log connection pool settings in development/debug mode
	if (
		process.env.ENABLE_LOGGING === "true" ||
		process.env.LOG_LEVEL === "debug"
	) {
		console.log(
			`[Database Pool] connection_limit=${connectionLimit}, pool_timeout=${poolTimeout}s, connect_timeout=${connectTimeout}s`,
		);
	}

	return url.toString();
}

// Singleton Prisma client instance
let prismaInstance = null;

function getPrismaClient() {
	if (!prismaInstance) {
		const optimizedUrl = getOptimizedDatabaseUrl();

		prismaInstance = new PrismaClient({
			datasources: {
				db: {
					url: optimizedUrl,
				},
			},
			log:
				process.env.PRISMA_LOG_QUERIES === "true"
					? ["query", "info", "warn", "error"]
					: ["warn", "error"],
			errorFormat: "pretty",
		});

		// Handle graceful shutdown
		process.on("beforeExit", async () => {
			await prismaInstance.$disconnect();
		});

		process.on("SIGINT", async () => {
			await prismaInstance.$disconnect();
			process.exit(0);
		});

		process.on("SIGTERM", async () => {
			await prismaInstance.$disconnect();
			process.exit(0);
		});
	}

	return prismaInstance;
}

// Connection health check
async function checkDatabaseConnection(prisma) {
	try {
		await prisma.$queryRaw`SELECT 1`;
		return true;
	} catch (error) {
		console.error("Database connection check failed:", error.message);
		return false;
	}
}

// Wait for database to be available with retry logic
async function waitForDatabase(prisma, options = {}) {
	const maxAttempts =
		options.maxAttempts ||
		parseInt(process.env.PM_DB_CONN_MAX_ATTEMPTS, 10) ||
		30;
	const waitInterval =
		options.waitInterval ||
		parseInt(process.env.PM_DB_CONN_WAIT_INTERVAL, 10) ||
		2;

	if (process.env.ENABLE_LOGGING === "true") {
		console.log(
			`Waiting for database connection (max ${maxAttempts} attempts, ${waitInterval}s interval)...`,
		);
	}

	for (let attempt = 1; attempt <= maxAttempts; attempt++) {
		try {
			const isConnected = await checkDatabaseConnection(prisma);
			if (isConnected) {
				if (process.env.ENABLE_LOGGING === "true") {
					console.log(
						`Database connected successfully after ${attempt} attempt(s)`,
					);
				}
				return true;
			}
		} catch {
			// checkDatabaseConnection already logs the error
		}

		if (attempt < maxAttempts) {
			if (process.env.ENABLE_LOGGING === "true") {
				console.log(
					`⏳ Database not ready (attempt ${attempt}/${maxAttempts}), retrying in ${waitInterval}s...`,
				);
			}
			await new Promise((resolve) => setTimeout(resolve, waitInterval * 1000));
		}
	}

	throw new Error(
		`❌ Database failed to become available after ${maxAttempts} attempts`,
	);
}

// Graceful disconnect with retry
async function disconnectPrisma(prisma, maxRetries = 3) {
	for (let i = 0; i < maxRetries; i++) {
		try {
			await prisma.$disconnect();
			console.log("Database disconnected successfully");
			return;
		} catch (error) {
			console.error(`Disconnect attempt ${i + 1} failed:`, error.message);
			if (i === maxRetries - 1) {
				console.error("Failed to disconnect from database after all retries");
			} else {
				await new Promise((resolve) => setTimeout(resolve, 1000)); // Wait 1 second
			}
		}
	}
}

module.exports = {
	getPrismaClient,
	checkDatabaseConnection,
	waitForDatabase,
	disconnectPrisma,
};
