import { env } from "../env.js";
import { parseAllowedAuthMethods } from "./auth-types.js";
import { getInitialOrganization as getInitialOrganization$1, queryHasCredentialAccount, queryMemberExists, queryOrgAllowedAuthMethods, queryPendingInvitationExists, querySsoProviderIssuers } from "../data-access/runtime/auth.js";

//#region src/auth/auth-config-utils.ts
async function getInitialOrganization(dbClient, userId) {
	return getInitialOrganization$1(dbClient)(userId);
}
/**
* Build the list of trusted origins for Better Auth.
* Includes static origins from env, SSO provider issuers from the DB,
* and (for /sso/register POST requests) the issuer from the request body
* so OIDC discovery is trusted before the provider is persisted.
*/
async function getTrustedOrigins(dbClient, request) {
	const staticOrigins = [
		"http://localhost:3000",
		"http://localhost:3002",
		env.INKEEP_AGENTS_MANAGE_UI_URL,
		env.INKEEP_AGENTS_API_URL,
		env.TRUSTED_ORIGIN
	].filter((origin) => typeof origin === "string" && origin.length > 0);
	const dynamicOrigins = [];
	if ((request?.url?.includes("/sso/register") || request?.url?.includes("/sso-provider/create")) && request?.method === "POST") try {
		const body = await request.clone().json();
		const rawUrl = body.issuer || body.oidcConfig?.discoveryUrl || body.oidcConfig?.issuer;
		if (rawUrl) {
			const issuerOrigin = new URL(rawUrl).origin;
			dynamicOrigins.push(issuerOrigin);
			const discoveryOrigins = await fetchOidcDiscoveryOrigins(rawUrl);
			dynamicOrigins.push(...discoveryOrigins);
		}
	} catch {}
	try {
		const providers = await querySsoProviderIssuers(dbClient)();
		const issuerOrigins = providers.map((p) => {
			try {
				return new URL(p.issuer).origin;
			} catch {
				return null;
			}
		}).filter((origin) => origin !== null);
		const discoveryResults = await Promise.all(providers.map((p) => fetchOidcDiscoveryOrigins(p.issuer)));
		return [
			...staticOrigins,
			...dynamicOrigins,
			...issuerOrigins,
			...discoveryResults.flat()
		];
	} catch {
		return [...staticOrigins, ...dynamicOrigins];
	}
}
async function fetchOidcDiscoveryOrigins(issuer) {
	try {
		const discoveryUrl = issuer.endsWith("/") ? `${issuer}.well-known/openid-configuration` : `${issuer}/.well-known/openid-configuration`;
		const res = await fetch(discoveryUrl, { signal: AbortSignal.timeout(5e3) });
		if (!res.ok) return [];
		const doc = await res.json();
		const endpointKeys = [
			"authorization_endpoint",
			"token_endpoint",
			"userinfo_endpoint",
			"jwks_uri",
			"revocation_endpoint",
			"introspection_endpoint"
		];
		const origins = [];
		for (const key of endpointKeys) if (typeof doc[key] === "string") try {
			origins.push(new URL(doc[key]).origin);
		} catch {}
		return [...new Set(origins)];
	} catch {
		return [];
	}
}
/**
* Extracts the root domain from a URL for cross-subdomain cookie sharing.
*
* When the API and UI share a common 3-part parent (e.g., api.pilot.inkeep.com
* and pilot.inkeep.com both share .pilot.inkeep.com), the function auto-computes
* the shared parent. When domains don't share a 3-part parent (e.g.,
* api.agents.inkeep.com and app.inkeep.com), set AUTH_COOKIE_DOMAIN explicitly.
*
* Examples (auto-computed from baseURL):
* - https://api.pilot.inkeep.com -> .pilot.inkeep.com
* - https://pilot.inkeep.com -> .pilot.inkeep.com
* - http://localhost:3002 -> undefined (no domain for localhost)
*
* With AUTH_COOKIE_DOMAIN=.inkeep.com:
* - Any *.inkeep.com URL -> .inkeep.com
*/
function extractCookieDomain(baseURL, explicitDomain) {
	if (explicitDomain) return explicitDomain.startsWith(".") ? explicitDomain : `.${explicitDomain}`;
	try {
		const hostname = new URL(baseURL).hostname;
		if (hostname === "localhost" || hostname.match(/^\d+\.\d+\.\d+\.\d+$/)) return;
		const parts = hostname.split(".");
		if (parts.length < 2) return;
		let domainParts;
		if (parts.length === 3) domainParts = parts;
		else if (parts.length > 3) domainParts = parts.slice(1);
		else domainParts = parts;
		return `.${domainParts.join(".")}`;
	} catch {
		return;
	}
}
async function hasCredentialAccount(dbClient, userId) {
	return queryHasCredentialAccount(dbClient)(userId);
}
/**
* Checks whether an SSO user should be auto-provisioned into an organization.
* Reads the per-provider `autoProvision` flag from `allowedAuthMethods` JSON.
* Returns false if:
* - The provider has no organizationId or providerId
* - The organization doesn't exist
* - The SSO provider entry has autoProvision disabled (or is missing)
* - The user is already a member
*/
async function shouldAutoProvision(dbClient, user, provider) {
	if (!provider.organizationId || !provider.providerId) return false;
	const org = await queryOrgAllowedAuthMethods(dbClient)(provider.organizationId);
	if (!org) return false;
	const ssoEntry = parseAllowedAuthMethods(org.allowedAuthMethods).find((m) => m.method === "sso" && m.providerId === provider.providerId);
	if (!ssoEntry || !ssoEntry.enabled || !ssoEntry.autoProvision) return false;
	if (await queryMemberExists(dbClient)(user.id, provider.organizationId)) return false;
	if (await queryPendingInvitationExists(dbClient)(user.email, provider.organizationId)) return false;
	return true;
}

//#endregion
export { extractCookieDomain, getInitialOrganization, getTrustedOrigins, hasCredentialAccount, shouldAutoProvision };