import { env } from "../../../env.js";
import runDbClient_default from "../../../data/db/runDbClient.js";
import { getLogger as getLogger$1 } from "../../../logger.js";
import { OpenAPIHono, z } from "@hono/zod-openapi";
import { AnonymousSessionResponseSchema, commonGetErrorResponses, createApiError, getAppById, getPoWErrorMessage, validateOrigin, verifyPoW } from "@inkeep/agents-core";
import { createProtectedRoute, noAuth } from "@inkeep/agents-core/middleware";
import { SignJWT, errors, jwtVerify } from "jose";
import { createChallenge } from "altcha-lib";

//#region src/domains/run/routes/auth.ts
const logger = getLogger$1("run-auth");
const DEV_ANON_SECRET = crypto.randomUUID() + crypto.randomUUID();
function getAnonJwtSecret() {
	const secret = env.INKEEP_ANON_JWT_SECRET;
	if (!secret) {
		if (env.ENVIRONMENT !== "development" && env.ENVIRONMENT !== "test") throw new Error("INKEEP_ANON_JWT_SECRET environment variable is required");
		logger.warn({}, "Using random ephemeral secret for anonymous JWTs — set INKEEP_ANON_JWT_SECRET");
		return new TextEncoder().encode(DEV_ANON_SECRET);
	}
	return new TextEncoder().encode(secret);
}
const PowChallengeResponseSchema = z.object({
	algorithm: z.string().openapi({ example: "SHA-256" }),
	challenge: z.string(),
	maxnumber: z.number().optional(),
	salt: z.string(),
	signature: z.string()
}).openapi("PowChallengeResponse");
const PowDisabledErrorSchema = z.object({ error: z.object({
	code: z.literal("not_found"),
	message: z.string()
}) }).openapi("PowDisabledError");
const app = new OpenAPIHono();
app.openapi(createProtectedRoute({
	method: "get",
	path: "/pow/challenge",
	summary: "Get PoW Challenge",
	description: "Fetch an ALTCHA Proof-of-Work challenge. Returns 404 when PoW is not enabled on the server.",
	operationId: "get-pow-challenge",
	tags: ["Auth"],
	permission: noAuth(),
	security: [],
	responses: {
		200: {
			description: "PoW challenge generated successfully",
			content: { "application/json": { schema: PowChallengeResponseSchema } }
		},
		404: {
			description: "PoW is not enabled",
			content: { "application/json": { schema: PowDisabledErrorSchema } }
		}
	}
}), async (c) => {
	const hmacSecret = env.INKEEP_POW_HMAC_SECRET;
	if (!hmacSecret) return c.json({ error: {
		code: "not_found",
		message: "PoW is not enabled"
	} }, 404);
	const challenge = await createChallenge({
		hmacKey: hmacSecret,
		algorithm: "SHA-256",
		maxnumber: env.INKEEP_POW_DIFFICULTY,
		expires: new Date(Date.now() + env.INKEEP_POW_CHALLENGE_TTL_SECONDS * 1e3)
	});
	return c.json({
		algorithm: challenge.algorithm,
		challenge: challenge.challenge,
		maxnumber: challenge.maxnumber,
		salt: challenge.salt,
		signature: challenge.signature
	}, 200);
});
app.openapi(createProtectedRoute({
	method: "post",
	path: "/apps/{appId}/anonymous-session",
	summary: "Create Anonymous Session",
	description: "Issue an anonymous session JWT for a web_client app. If a valid Bearer token for the same app is provided, the existing anonymous identity is preserved with a fresh expiry (rolling refresh). Otherwise a new identity is created.",
	operationId: "create-anonymous-session",
	tags: ["Auth"],
	permission: noAuth(),
	security: [],
	request: { params: z.object({ appId: z.string().describe("App ID (e.g., app_a1b2c3d4e5f6)") }) },
	responses: {
		200: {
			description: "Anonymous session created successfully",
			content: { "application/json": { schema: AnonymousSessionResponseSchema } }
		},
		...commonGetErrorResponses
	}
}), async (c) => {
	const { appId } = c.req.valid("param");
	const appRecord = await getAppById(runDbClient_default)(appId);
	if (!appRecord || !appRecord.enabled) throw createApiError({
		code: "not_found",
		message: "App not found or disabled"
	});
	if (appRecord.type !== "web_client") throw createApiError({
		code: "bad_request",
		message: "Anonymous sessions are only available for web_client apps"
	});
	const config = appRecord.config;
	if (config.webClient.allowAnonymous === false) throw createApiError({
		code: "unauthorized",
		message: "Anonymous sessions are disabled for this app. Authentication is required."
	});
	const origin = c.req.header("Origin");
	if (!validateOrigin(origin, config.webClient.allowedDomains)) {
		logger.warn({
			origin,
			allowedDomains: config.webClient.allowedDomains,
			appId: appRecord.id
		}, "Anonymous session: origin not allowed");
		throw createApiError({
			code: "forbidden",
			message: "Origin not allowed"
		});
	}
	const pow = await verifyPoW(c.req.raw, env.INKEEP_POW_HMAC_SECRET);
	if (!pow.ok) throw createApiError({
		code: "bad_request",
		message: getPoWErrorMessage(pow.error)
	});
	const secret = getAnonJwtSecret();
	let anonUserId;
	let refreshTenantId;
	let refreshProjectId;
	const authHeader = c.req.header("Authorization");
	if (authHeader?.startsWith("Bearer ")) {
		const bearerToken = authHeader.slice(7);
		try {
			const { payload } = await jwtVerify(bearerToken, secret, {
				issuer: "inkeep",
				algorithms: ["HS256"]
			});
			if (payload.app === appId && payload.type === "anonymous" && typeof payload.sub === "string" && payload.sub.startsWith("anon_")) {
				anonUserId = payload.sub;
				refreshTenantId = typeof payload.tid === "string" ? payload.tid : null;
				refreshProjectId = typeof payload.pid === "string" ? payload.pid : null;
			} else logger.debug({
				appId,
				tokenApp: payload.app,
				tokenType: payload.type
			}, "Anonymous session refresh: token claims mismatch, creating new identity");
		} catch (err) {
			if (err instanceof errors.JWTExpired) logger.debug({
				appId,
				error: err.message
			}, "Anonymous session refresh: token expired, creating new identity");
			else logger.debug({
				appId,
				error: err instanceof Error ? err.message : String(err)
			}, "Anonymous session refresh: invalid token, creating new identity");
		}
	}
	const isRefresh = !!anonUserId;
	if (!anonUserId) anonUserId = `anon_${crypto.randomUUID()}`;
	const tenantId = isRefresh ? refreshTenantId ?? appRecord.tenantId : appRecord.tenantId;
	const projectId = isRefresh ? refreshProjectId ?? appRecord.projectId : appRecord.projectId;
	const lifetimeSeconds = env.INKEEP_ANON_SESSION_LIFETIME_SECONDS;
	const now = Math.floor(Date.now() / 1e3);
	const exp = now + lifetimeSeconds;
	const expiresAt = (/* @__PURE__ */ new Date(exp * 1e3)).toISOString();
	const token = await new SignJWT({
		tid: tenantId,
		pid: projectId,
		app: appId,
		type: "anonymous"
	}).setProtectedHeader({
		alg: "HS256",
		typ: "JWT"
	}).setSubject(anonUserId).setIssuer("inkeep").setIssuedAt(now).setExpirationTime(exp).sign(secret);
	logger.info({
		appId: appRecord.id,
		appType: appRecord.type,
		origin,
		anonUserId,
		isRefresh
	}, isRefresh ? "Anonymous session refreshed" : "Anonymous session created");
	return c.json({
		token,
		expiresAt
	});
});
var auth_default = app;

//#endregion
export { auth_default as default, getAnonJwtSecret };