import { env } from "../env.js";
import runDbClient_default from "../data/db/runDbClient.js";
import { sessionAuth } from "./sessionAuth.js";
import { getLogger, isInternalServiceToken, isSlackUserToken, validateAndGetApiKey, verifyInternalServiceAuthHeader, verifySlackUserToken } from "@inkeep/agents-core";
import { registerAuthzMeta } from "@inkeep/agents-core/middleware";
import { createMiddleware } from "hono/factory";
import { HTTPException } from "hono/http-exception";

//#region src/middleware/manageAuth.ts
/**
* Legacy exception: allow database API keys on GET /manage/tenants/:t/projects/:p/conversations/:id
* A customer uses the deprecated API key and needs read access to this single endpoint.
* The caller must already know the conversationId. Only GET with a specific conversation ID is allowed —
* list, bounds, media, and all other manage endpoints remain session-only.
*/
const LEGACY_API_KEY_CONVERSATION_ROUTE = /^\/manage\/tenants\/[^/]+\/projects\/[^/]+\/conversations\/[^/]+\/?$/;
function isLegacyApiKeyAllowedRoute(method, path) {
	return method === "GET" && LEGACY_API_KEY_CONVERSATION_ROUTE.test(path);
}
function extractProjectIdFromPath(path) {
	return path.match(/\/projects\/([^/]+)\//)?.[1];
}
const logger = getLogger("env-key-auth");
/**
* Middleware to authenticate API requests using Bearer token authentication
* Authentication priority:
* 1. Bypass secret (INKEEP_AGENTS_MANAGE_API_BYPASS_SECRET)
* 2. Better-auth session token (from device authorization flow)
* 3. Slack user JWT token (for Slack work app delegation)
* 4. Internal service token
*
* NOTE: Database API keys are intentionally NOT accepted on manage endpoints,
* EXCEPT for the legacy exception on GET /manage/tenants/:t/projects/:p/conversations/:id
* (see isLegacyApiKeyAllowedRoute). API keys are otherwise restricted to the run domain only.
*/
const manageBearerAuth = () => createMiddleware(async (c, next) => {
	const authHeader = c.req.header("Authorization");
	if (!authHeader || !authHeader.startsWith("Bearer ")) throw new HTTPException(401, { message: "Missing or invalid authorization header. Expected: Bearer <api_key>" });
	const token = authHeader.substring(7);
	if (env.INKEEP_AGENTS_MANAGE_API_BYPASS_SECRET && token === env.INKEEP_AGENTS_MANAGE_API_BYPASS_SECRET) {
		logger.info({}, "Bypass secret authenticated successfully");
		c.set("userId", "system");
		c.set("userEmail", "system@internal");
		await next();
		return;
	}
	const auth = c.get("auth");
	try {
		const headers$1 = new Headers();
		headers$1.set("Authorization", authHeader);
		const forwardedCookie = c.req.header("x-forwarded-cookie");
		const cookie = c.req.header("cookie");
		if (forwardedCookie) {
			headers$1.set("cookie", forwardedCookie);
			logger.debug({ source: "x-forwarded-cookie" }, "Using x-forwarded-cookie for session validation");
		} else if (cookie) {
			headers$1.set("cookie", cookie);
			logger.debug({ source: "cookie" }, "Using cookie for session validation");
		}
		const session = await auth.api.getSession({ headers: headers$1 });
		if (session?.user) {
			logger.info({ userId: session.user.id }, "Better-auth session authenticated successfully");
			c.set("userId", session.user.id);
			c.set("userEmail", session.user.email);
			await next();
			return;
		}
	} catch (error) {
		logger.debug({ error }, "Better-auth session validation failed, trying other auth methods");
	}
	if (isSlackUserToken(token)) {
		const result = await verifySlackUserToken(token);
		if (!result.valid || !result.payload) throw new HTTPException(401, { message: result.error || "Invalid Slack user token" });
		logger.info({
			inkeepUserId: result.payload.sub,
			tenantId: result.payload.tenantId,
			slackTeamId: result.payload.slack.teamId,
			slackUserId: result.payload.slack.userId
		}, "Slack user JWT authenticated successfully");
		c.set("userId", result.payload.sub);
		if (result.payload.slack.email) c.set("userEmail", result.payload.slack.email);
		c.set("tenantId", result.payload.tenantId);
		await next();
		return;
	}
	if (isInternalServiceToken(token)) {
		const result = await verifyInternalServiceAuthHeader(authHeader);
		if (!result.valid || !result.payload) throw new HTTPException(401, { message: result.error || "Invalid internal service token" });
		logger.info({
			serviceId: result.payload.sub,
			tenantId: result.payload.tenantId,
			projectId: result.payload.projectId,
			userId: result.payload.userId
		}, "Internal service authenticated");
		c.set("userId", result.payload.userId || `system`);
		c.set("userEmail", `${result.payload.sub}@internal.inkeep`);
		if (result.payload.tenantId) c.set("tenantId", result.payload.tenantId);
		await next();
		return;
	}
	if (isLegacyApiKeyAllowedRoute(c.req.method, c.req.path)) try {
		const apiKeyRecord = await validateAndGetApiKey(token, runDbClient_default);
		if (apiKeyRecord) {
			const routeProjectId = extractProjectIdFromPath(c.req.path);
			if (routeProjectId && apiKeyRecord.projectId !== routeProjectId) {
				logger.warn({
					apiKeyId: apiKeyRecord.id,
					apiKeyProjectId: apiKeyRecord.projectId,
					routeProjectId
				}, "Legacy API key project mismatch");
				throw new HTTPException(403, { message: "API key does not have access to this project" });
			}
			logger.info({
				apiKeyId: apiKeyRecord.id,
				tenantId: apiKeyRecord.tenantId
			}, "Legacy API key authenticated for manage conversation endpoint");
			c.set("userId", `apikey:${apiKeyRecord.id}`);
			c.set("tenantId", apiKeyRecord.tenantId);
			await next();
			return;
		}
	} catch (error) {
		if (error instanceof HTTPException) throw error;
		logger.error({ error }, "Legacy API key validation failed");
	}
	throw new HTTPException(401, { message: "Invalid Token" });
});
/**
* Middleware that gates a route with manage-domain authentication.
* Uses Bearer token → manage bearer auth (bypass secret, session, Slack JWT, internal service),
* otherwise falls back to session auth.
*/
const manageBearerOrSessionAuth = () => {
	const mw = createMiddleware(async (c, next) => {
		if (env.ENVIRONMENT === "test") {
			await next();
			return;
		}
		if (c.req.header("Authorization")?.startsWith("Bearer ")) return manageBearerAuth()(c, next);
		return sessionAuth()(c, next);
	});
	registerAuthzMeta(mw, {
		resource: "organization",
		permission: "member",
		description: "Requires session cookie authentication"
	});
	return mw;
};

//#endregion
export { manageBearerAuth, manageBearerOrSessionAuth };