import { OrgRoles } from "./authz/types.js";
import { loadEnvironmentFiles } from "../env.js";
import { syncOrgMemberToSpiceDb } from "./authz/sync.js";
import "./authz/index.js";
import { createAgentsRunDatabaseClient } from "../db/runtime/runtime-client.js";
import { createApp, getAppById } from "../data-access/runtime/apps.js";
import { addUserToOrganization, upsertOrganization } from "../data-access/runtime/organizations.js";
import { getUserByEmail } from "../data-access/runtime/users.js";
import { validatePasswordPolicy } from "./password-policy.js";
import { createAuth } from "./auth.js";
import { writeSpiceDbSchema } from "./spicedb-schema.js";
import { hashPassword } from "better-auth/crypto";

//#region src/auth/init.ts
/**
* Standalone initialization script for creating the default organization and admin user.
* This script is designed to run during setup/migration, NOT at server startup.
*
* Uses upsertOrganization to create org with TENANT_ID as the actual organization ID,
* and Better Auth's API for user creation.
*
* Usage: pnpm db:auth:init
*
* Required environment variables:
* - INKEEP_AGENTS_RUN_DATABASE_URL: PostgreSQL connection string
* - TENANT_ID: Organization/tenant ID (defaults to 'default') - this becomes the org ID
* - INKEEP_AGENTS_MANAGE_UI_USERNAME: Admin email address
* - INKEEP_AGENTS_MANAGE_UI_PASSWORD: Admin password (min 15 chars, see password policy)
* - BETTER_AUTH_SECRET: Secret for Better Auth
*
* Optional environment variables:
* - INKEEP_AGENTS_API_URL: API URL for Better Auth (defaults to http://localhost:3002)
* - INKEEP_AUTH_INIT_FORCE_PASSWORD_RESET: When 'true' and the admin user
*   already exists, re-sync the user's credential-account password from
*   INKEEP_AGENTS_MANAGE_UI_PASSWORD. Intended for ephemeral CI envs where
*   the secret may have rotated. Default-off so prod re-runs are safe.
*/
loadEnvironmentFiles();
const TENANT_ID = process.env.TENANT_ID || "default";
async function init() {
	console.log("🚀 Initializing database with default organization and user...\n");
	console.log("📜 Writing SpiceDB schema...");
	try {
		await writeSpiceDbSchema();
		console.log("   ✅ SpiceDB schema applied");
	} catch (error) {
		console.error("   ❌ Failed to write SpiceDB schema:", error);
		console.error("   Make sure SpiceDB is running (docker-compose.dbs.yml)");
		process.exit(1);
	}
	const dbClient = createAgentsRunDatabaseClient();
	const username = process.env.INKEEP_AGENTS_MANAGE_UI_USERNAME;
	const password = process.env.INKEEP_AGENTS_MANAGE_UI_PASSWORD;
	const authSecret = process.env.BETTER_AUTH_SECRET;
	if (!username || !password) {
		console.error("❌ INKEEP_AGENTS_MANAGE_UI_USERNAME and INKEEP_AGENTS_MANAGE_UI_PASSWORD are required");
		console.error("   These credentials are used to create the initial admin user.");
		process.exit(1);
	}
	if (!authSecret) {
		console.error("❌ BETTER_AUTH_SECRET is required");
		console.error("   This secret is used to sign authentication tokens.");
		process.exit(1);
	}
	const passwordViolations = validatePasswordPolicy(password, { userEmail: username });
	if (passwordViolations.length > 0) {
		console.error("❌ INKEEP_AGENTS_MANAGE_UI_PASSWORD does not meet the password policy:");
		for (const v of passwordViolations) console.error(`   - ${v.message}`);
		process.exit(1);
	}
	const auth = createAuth({
		baseURL: process.env.INKEEP_AGENTS_API_URL || "http://localhost:3002",
		secret: authSecret,
		dbClient
	});
	console.log(`📦 Checking/creating organization: ${TENANT_ID}`);
	const { created: orgCreated } = await upsertOrganization(dbClient)({
		organizationId: TENANT_ID,
		name: TENANT_ID,
		slug: TENANT_ID,
		logo: null,
		metadata: null
	});
	if (orgCreated) console.log(`   ✅ Organization created with ID: ${TENANT_ID}`);
	else console.log(`   ℹ️  Organization already exists: ${TENANT_ID}`);
	console.log(`\n👤 Creating admin user: ${username}`);
	let user = await getUserByEmail(dbClient)(username);
	if (user) if (process.env.INKEEP_AUTH_INIT_FORCE_PASSWORD_RESET === "true") try {
		const ctx = await auth.$context;
		const hashedPassword = await hashPassword(password);
		await ctx.internalAdapter.updatePassword(user.id, hashedPassword);
		console.log(`   ✅ User exists; password re-synced from env`);
	} catch (error) {
		console.error(`   ❌ Failed to re-sync password for user ${user.id}:`, error);
		throw error;
	}
	else console.log(`   ℹ️  User already exists: ${username} — skipping creation and password update`);
	else {
		console.log("   Creating user with Better Auth...");
		const result = await auth.api.signUpEmail({ body: {
			email: username,
			password,
			name: username.split("@")[0]
		} });
		if (!result.user) {
			console.error("   ❌ Failed to create user: signUpEmail returned no user");
			process.exit(1);
		}
		user = await getUserByEmail(dbClient)(username);
		if (!user) {
			console.error("   ❌ User was created but could not be retrieved from database");
			process.exit(1);
		}
		if (result.token) await auth.api.signOut({ headers: new Headers({ authorization: `Bearer ${result.token}` }) });
		console.log(`   ✅ User created: ${user.email}`);
	}
	console.log(`\n🔗 Adding user to organization...`);
	await addUserToOrganization(dbClient)({
		userId: user.id,
		organizationId: TENANT_ID,
		role: OrgRoles.ADMIN,
		isServiceAccount: true
	});
	console.log(`   ✅ User ${user.email} added as ${OrgRoles.ADMIN}`);
	try {
		await syncOrgMemberToSpiceDb({
			tenantId: TENANT_ID,
			userId: user.id,
			role: OrgRoles.ADMIN,
			action: "add"
		});
		console.log("   ✅ Synced to SpiceDB");
	} catch (error) {
		console.error("❌ SpiceDB sync failed:", error);
		throw error;
	}
	const playgroundAppId = process.env.INKEEP_PLAYGROUND_APP_ID || "app_playground";
	const tempJwtPublicKeyB64 = process.env.INKEEP_AGENTS_TEMP_JWT_PUBLIC_KEY;
	console.log(`\n🎮 Checking/creating playground app: ${playgroundAppId}`);
	let allowedDomains = ["localhost", "127.0.0.1"];
	const manageUiUrl = process.env.INKEEP_AGENTS_MANAGE_UI_URL;
	if (manageUiUrl) try {
		const url = new URL(manageUiUrl);
		if (url.hostname === "localhost" || url.hostname === "127.0.0.1") allowedDomains = ["localhost", "127.0.0.1"];
		else allowedDomains = [url.hostname];
	} catch {
		console.log(`   ⚠️  Invalid INKEEP_AGENTS_MANAGE_UI_URL: ${manageUiUrl}, using localhost defaults`);
	}
	console.log(`   📋 Playground allowed domains: ${JSON.stringify(allowedDomains)}`);
	if (await getAppById(dbClient)(playgroundAppId)) console.log(`   ℹ️  Playground app already exists: ${playgroundAppId}`);
	else {
		const publicKeys = [];
		if (tempJwtPublicKeyB64) {
			const { deriveKidFromPublicKey } = await import("../utils/jwt-helpers.js");
			const publicKeyPem = Buffer.from(tempJwtPublicKeyB64, "base64").toString("utf-8");
			const kid = await deriveKidFromPublicKey(publicKeyPem);
			publicKeys.push({
				kid,
				publicKey: publicKeyPem,
				algorithm: "RS256",
				addedAt: (/* @__PURE__ */ new Date()).toISOString()
			});
		} else console.log("   ⚠️  INKEEP_AGENTS_TEMP_JWT_PUBLIC_KEY not set — playground app created without auth keys");
		const config = {
			type: "web_client",
			webClient: {
				allowedDomains,
				publicKeys,
				allowAnonymous: false
			}
		};
		await createApp(dbClient)({
			id: playgroundAppId,
			tenantId: null,
			projectId: null,
			name: "Playground",
			description: "Global playground app for manage-ui",
			type: "web_client",
			defaultAgentId: null,
			defaultProjectId: null,
			enabled: true,
			config
		});
		console.log(`   ✅ Playground app created: ${playgroundAppId} (domains: ${JSON.stringify(allowedDomains)})`);
		if (publicKeys.length > 0) console.log(`   ✅ RSA public key configured (kid: playground-rsa)`);
	}
	console.log("\n================================================");
	console.log("✅ Initialization complete!");
	console.log("================================================");
	console.log(`\nOrganization: ${TENANT_ID}`);
	console.log(`Admin user:   ${username}`);
	console.log(`Playground:   ${playgroundAppId}`);
	console.log("\nYou can now log in with these credentials.\n");
	process.exit(0);
}
init().catch((error) => {
	console.error("\n❌ Initialization failed:", error);
	process.exit(1);
});

//#endregion
export {  };