import { dirname, join } from "node:path";
import { mkdir, writeFile } from "node:fs/promises";
import { randomBytes } from "node:crypto";

//#region src/secrets/generator.ts
/**
* Generate a secure random password using URL-safe base64 characters.
* @param length Password length (default: 32)
*/
function generateSecurePassword(length = 32) {
	return randomBytes(Math.ceil(length * 3 / 4)).toString("base64url").slice(0, length);
}
/** Default service configurations (localhost for local dev via Docker port mapping) */
const SERVICE_DEFAULTS = {
	postgres: {
		host: "localhost",
		port: 5432,
		username: "app",
		database: "app"
	},
	redis: {
		host: "localhost",
		port: 6379,
		username: "default"
	},
	rabbitmq: {
		host: "localhost",
		port: 5672,
		username: "app",
		vhost: "/"
	},
	minio: {
		host: "localhost",
		port: 9e3,
		username: "app",
		bucket: "app"
	},
	mailpit: {
		host: "localhost",
		port: 1025,
		username: "app"
	},
	localstack: {
		host: "localhost",
		port: 4566,
		username: "localstack",
		region: "us-east-1"
	}
};
/** Default credentials for pgboss (not a Docker service, reuses postgres) */
const PGBOSS_DEFAULTS = {
	host: "localhost",
	port: 5432,
	username: "pgboss",
	database: "app"
};
/**
* Generate credentials for a specific service.
*/
function generateServiceCredentials(service) {
	const defaults = SERVICE_DEFAULTS[service];
	return {
		...defaults,
		password: generateSecurePassword()
	};
}
/**
* Generate credentials for multiple services.
*/
function generateServicesCredentials(services) {
	const result = {};
	for (const service of services) result[service] = generateServiceCredentials(service);
	return result;
}
/**
* Generate connection URL for PostgreSQL.
*/
function generatePostgresUrl(creds) {
	const { username, password, host, port, database } = creds;
	return `postgresql://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}`;
}
/**
* Generate connection URL for Redis.
*/
function generateRedisUrl(creds) {
	const { password, host, port } = creds;
	return `redis://:${encodeURIComponent(password)}@${host}:${port}`;
}
/**
* Generate connection URL for RabbitMQ.
*/
function generateRabbitmqUrl(creds) {
	const { username, password, host, port, vhost } = creds;
	const encodedVhost = encodeURIComponent(vhost ?? "/");
	return `amqp://${username}:${encodeURIComponent(password)}@${host}:${port}/${encodedVhost}`;
}
/**
* Generate endpoint URL for MinIO (S3-compatible).
*/
function generateMinioEndpoint(creds) {
	const { host, port } = creds;
	return `http://${host}:${port}`;
}
/**
* Generate a LocalStack-compatible access key ID.
* Must start with 'LSIA' prefix and be at least 20 characters.
* @see https://docs.localstack.cloud/aws/capabilities/config/credentials/
*/
function generateLocalStackAccessKeyId() {
	const suffix = randomBytes(12).toString("base64url").slice(0, 16);
	return `LSIA${suffix}`;
}
/**
* Generate connection URL for pg-boss (uses PostgreSQL protocol).
* Format: pgboss://user:pass@host:port/db?schema=pgboss
*/
function generatePgBossUrl(creds) {
	const { username, password, host, port, database } = creds;
	return `pgboss://${username}:${encodeURIComponent(password)}@${host}:${port}/${database}?schema=pgboss`;
}
/**
* Generate event connection strings based on the events backend.
*/
function generateEventConnectionStrings(eventsBackend, services) {
	switch (eventsBackend) {
		case "pgboss": {
			const creds = services.pgboss;
			if (!creds) throw new Error("pgboss credentials required for pgboss events");
			const url = generatePgBossUrl(creds);
			return {
				publisher: url,
				subscriber: url
			};
		}
		case "sns": {
			const creds = services.localstack;
			if (!creds) throw new Error("localstack credentials required for sns events");
			const endpoint = `http://${creds.host}:${creds.port}`;
			const region = creds.region ?? "us-east-1";
			const accessKeyId = creds.accessKeyId ?? creds.username;
			const secretKey = encodeURIComponent(creds.password);
			return {
				publisher: `sns://${accessKeyId}:${secretKey}@${creds.host}:${creds.port}?region=${region}&endpoint=${encodeURIComponent(endpoint)}`,
				subscriber: `sqs://${accessKeyId}:${secretKey}@${creds.host}:${creds.port}?region=${region}&endpoint=${encodeURIComponent(endpoint)}`
			};
		}
		case "rabbitmq": {
			const creds = services.rabbitmq;
			if (!creds) throw new Error("rabbitmq credentials required for rabbitmq events");
			const url = generateRabbitmqUrl(creds);
			return {
				publisher: url,
				subscriber: url
			};
		}
	}
}
/**
* Generate connection URLs from service credentials.
*/
function generateConnectionUrls(services, eventsBackend) {
	const urls = {};
	if (services.postgres) urls.DATABASE_URL = generatePostgresUrl(services.postgres);
	if (services.redis) urls.REDIS_URL = generateRedisUrl(services.redis);
	if (services.rabbitmq) urls.RABBITMQ_URL = generateRabbitmqUrl(services.rabbitmq);
	if (services.minio) urls.STORAGE_ENDPOINT = generateMinioEndpoint(services.minio);
	if (eventsBackend) {
		const eventUrls = generateEventConnectionStrings(eventsBackend, services);
		urls.EVENT_PUBLISHER_CONNECTION_STRING = eventUrls.publisher;
		urls.EVENT_SUBSCRIBER_CONNECTION_STRING = eventUrls.subscriber;
	}
	if (services.mailpit) {
		urls.SMTP_HOST = services.mailpit.host;
		urls.SMTP_PORT = String(services.mailpit.port);
	}
	return urls;
}
/**
* Generate LocalStack service credentials with LSIA-prefixed access key.
*/
function generateLocalStackCredentials() {
	const defaults = SERVICE_DEFAULTS.localstack;
	return {
		...defaults,
		password: generateSecurePassword(),
		accessKeyId: generateLocalStackAccessKeyId()
	};
}
/**
* Create a new StageSecrets object with generated credentials.
* @param stage - The deployment stage (e.g., 'development', 'production')
* @param services - List of services to generate credentials for
* @param options - Optional configuration
* @param options.projectName - Project name used to derive the database name (e.g., 'myapp' → 'myapp_dev')
* @param options.eventsBackend - Event backend type (pgboss, sns, rabbitmq)
*/
function createStageSecrets(stage, services, options) {
	const now = (/* @__PURE__ */ new Date()).toISOString();
	const serviceCredentials = generateServicesCredentials(services);
	if (options?.projectName) {
		if (serviceCredentials.postgres) serviceCredentials.postgres.database = `${options.projectName.replace(/-/g, "_")}_dev`;
		if (serviceCredentials.minio) {
			serviceCredentials.minio.bucket = options.projectName;
			serviceCredentials.minio.username = options.projectName;
		}
	}
	const eventsBackend = options?.eventsBackend;
	if (eventsBackend === "pgboss" && serviceCredentials.postgres) serviceCredentials.pgboss = {
		...PGBOSS_DEFAULTS,
		password: generateSecurePassword(),
		host: serviceCredentials.postgres.host,
		port: serviceCredentials.postgres.port,
		database: serviceCredentials.postgres.database
	};
	if (eventsBackend === "sns") serviceCredentials.localstack = generateLocalStackCredentials();
	const urls = generateConnectionUrls(serviceCredentials, eventsBackend);
	return {
		stage,
		createdAt: now,
		updatedAt: now,
		eventsBackend,
		services: serviceCredentials,
		urls,
		custom: {}
	};
}
/**
* Rotate password for a specific service.
*/
function rotateServicePassword(secrets, service) {
	const currentCreds = secrets.services[service];
	if (!currentCreds) throw new Error(`Service "${service}" not configured in secrets`);
	const newCreds = {
		...currentCreds,
		password: generateSecurePassword()
	};
	const newServices = {
		...secrets.services,
		[service]: newCreds
	};
	return {
		...secrets,
		updatedAt: (/* @__PURE__ */ new Date()).toISOString(),
		services: newServices,
		urls: generateConnectionUrls(newServices, secrets.eventsBackend)
	};
}

//#endregion
//#region src/setup/fullstack-secrets.ts
/**
* Generate a secure random password for database users.
* Uses a combination of timestamp and random bytes for uniqueness.
*/
function generateDbPassword() {
	return `${Date.now().toString(36)}${Math.random().toString(36).slice(2)}${Math.random().toString(36).slice(2)}`;
}
/**
* Generate database URL for an app.
* All apps connect to the same database, but use different users/schemas.
*/
function generateDbUrl(appName, password, projectName, host = "localhost", port = 5432) {
	const userName = appName.replace(/-/g, "_");
	const dbName = `${projectName.replace(/-/g, "_")}_dev`;
	return `postgresql://${userName}:${password}@${host}:${port}/${dbName}`;
}
/**
* Generate fullstack-aware custom secrets for a workspace.
*
* Generates:
* - Common secrets: NODE_ENV, PORT, LOG_LEVEL, JWT_SECRET
* - Per-app database passwords and URLs for backend apps with db service
* - Better-auth secrets for apps using the better-auth framework
*/
function generateFullstackCustomSecrets(workspace) {
	const hasDb = !!workspace.services.db;
	const customs = {
		NODE_ENV: "development",
		PORT: "3000",
		LOG_LEVEL: "debug",
		JWT_SECRET: `dev-${Date.now()}-${Math.random().toString(36).slice(2)}`
	};
	if (!hasDb) return customs;
	const frontendPorts = [];
	for (const [appName, appConfig] of Object.entries(workspace.apps)) {
		if (appConfig.type === "frontend") {
			frontendPorts.push(appConfig.port);
			const upperName$1 = appName.toUpperCase();
			customs[`${upperName$1}_URL`] = `http://localhost:${appConfig.port}`;
			continue;
		}
		const password = generateDbPassword();
		const upperName = appName.toUpperCase();
		customs[`${upperName}_DATABASE_URL`] = generateDbUrl(appName, password, workspace.name);
		customs[`${upperName}_DB_PASSWORD`] = password;
		if (appConfig.framework === "better-auth") {
			customs.AUTH_PORT = String(appConfig.port);
			customs.AUTH_URL = `http://localhost:${appConfig.port}`;
			customs.BETTER_AUTH_SECRET = `better-auth-${Date.now()}-${generateSecurePassword(16)}`;
			customs.BETTER_AUTH_URL = `http://localhost:${appConfig.port}`;
		}
	}
	if (customs.BETTER_AUTH_SECRET) {
		const allPorts = Object.values(workspace.apps).map((a) => a.port);
		customs.BETTER_AUTH_TRUSTED_ORIGINS = allPorts.map((p) => `http://localhost:${p}`).join(",");
	}
	return customs;
}
/**
* Extract *_DB_PASSWORD keys from secrets and write docker/.env.
*
* The docker/.env file contains database passwords that the PostgreSQL
* init script reads to create per-app database users.
*/
async function writeDockerEnvFromSecrets(secrets, workspaceRoot) {
	const dbPasswordEntries = Object.entries(secrets.custom).filter(([key]) => key.endsWith("_DB_PASSWORD"));
	if (dbPasswordEntries.length === 0) return;
	const envContent = `# Auto-generated docker environment file
# Contains database passwords for docker-compose postgres init
# This file is gitignored - do not commit to version control
${dbPasswordEntries.map(([key, value]) => `${key}=${value}`).join("\n")}
`;
	const envPath = join(workspaceRoot, "docker", ".env");
	await mkdir(dirname(envPath), { recursive: true });
	await writeFile(envPath, envContent);
}

//#endregion
export { createStageSecrets, generateConnectionUrls, generateDbPassword, generateDbUrl, generateFullstackCustomSecrets, generateLocalStackCredentials, generateSecurePassword, generateServiceCredentials, rotateServicePassword, writeDockerEnvFromSecrets };
//# sourceMappingURL=fullstack-secrets-x2Kffx7-.mjs.map