import manageDbPool_default from "../../../data/db/manageDbPool.js";
import { getLogger as getLogger$1 } from "../../../logger.js";
import manageDbClient_default from "../../../data/db/manageDbClient.js";
import { oauthService, retrievePKCEVerifier } from "../../../utils/oauthService.js";
import { OpenAPIHono, z } from "@hono/zod-openapi";
import { CredentialReferenceApiSelectSchema, CredentialStoreType, OAuthCallbackQuerySchema, generateId, getCredentialReferenceWithResources, getProjectMainResolvedRef, getToolById, updateTool, upsertCredentialReference, withRef } from "@inkeep/agents-core";
import { createProtectedRoute, noAuth } from "@inkeep/agents-core/middleware";

//#region src/domains/manage/routes/oauth.ts
/**
* OAuth Callback Handler
*
* Handles OAuth 2.1 authorization code flows for MCP tools:
* - Processes authorization codes from OAuth providers
* - Exchanges codes for access tokens using PKCE
* - Stores credentials in Keychain
* - Updates MCP tool status
* - Redirects users back to frontend
*/
/**
* Find existing credential or create/update one (idempotent operation).
* Uses upsert to handle reconnection for user-scoped credentials
* where the (toolId, userId) unique constraint may already be satisfied.
*/
async function findOrCreateCredential(db, tenantId, projectId, credentialData) {
	const existingCredential = await getCredentialReferenceWithResources(db)({
		scopes: {
			tenantId,
			projectId
		},
		id: credentialData.id
	});
	if (existingCredential) return CredentialReferenceApiSelectSchema.parse(existingCredential);
	try {
		const credential = await upsertCredentialReference(db)({ data: {
			...credentialData,
			tenantId,
			projectId
		} });
		return CredentialReferenceApiSelectSchema.parse(credential);
	} catch (error) {
		console.error("Failed to save credential to database:", error);
		throw new Error(`Failed to save credential '${credentialData.id}' to database`);
	}
}
const app = new OpenAPIHono();
const logger = getLogger$1("oauth-callback");
/**
* Extract base URL from request context
*/
function getBaseUrlFromRequest(c) {
	const url = new URL(c.req.url);
	return `${url.protocol}//${url.host}`;
}
/**
* Generate OAuth callback HTML page
*/
function generateOAuthCallbackPage(params) {
	const { title, message, isSuccess } = params;
	return `
    <!DOCTYPE html>
    <html>
    <head>
      <title>${title}</title>
      <meta charset="utf-8">
      <style>
        body {
          background-color: #000;
          color: #fff;
          font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
          display: flex;
          align-items: center;
          justify-content: center;
          height: 100vh;
          margin: 0;
          text-align: center;
        }
        .container {
          max-width: 400px;
          padding: 2rem;
        }
        .title {
          font-size: 1.2rem;
          margin-bottom: 1rem;
        }
        .message {
          color: #ccc;
          line-height: 1.5;
        }
        .countdown {
          margin-top: 1rem;
          font-size: 0.9rem;
        }
      </style>
    </head>
    <body>
      <div class="container">
        <div class="title">${title}</div>
        <div class="message">${message}</div>
        <div class="message countdown">
          ${isSuccess ? "Closing automatically..." : ""}
        </div>
      </div>
      <script>
        ${isSuccess && `
        // Success: Send PostMessage then auto-close
        if (window.opener) {
          try {
            window.opener.postMessage({
              type: 'oauth-success',
              timestamp: Date.now()
            }, '*');
          } catch (error) {
            console.error('PostMessage failed:', error);
          }
        }
        
        // Auto-close after brief delay
        setTimeout(() => {
          window.close();
        }, 1000);
          `}
      <\/script>
    </body>
    </html>
  `;
}
app.openapi(createProtectedRoute({
	method: "get",
	path: "/callback",
	summary: "MCP OAuth authorization callback",
	description: "Handles OAuth authorization codes for MCP tools and completes the authentication flow. Secured via PKCE state parameter (not HTTP authentication) since this is an external redirect from an OAuth provider.",
	operationId: "mcp-oauth-callback",
	tags: ["OAuth"],
	permission: noAuth(),
	request: { query: OAuthCallbackQuerySchema },
	responses: {
		302: { description: "Redirect to frontend after successful OAuth" },
		400: {
			description: "OAuth error or invalid request",
			content: { "text/html": { schema: z.string() } }
		},
		500: {
			description: "Internal server error",
			content: { "text/html": { schema: z.string() } }
		}
	}
}), async (c) => {
	try {
		const { code, state, error, error_description } = c.req.valid("query");
		logger.info({
			state,
			hasCode: !!code
		}, "OAuth callback received");
		if (error) {
			logger.error({
				error,
				error_description
			}, "OAuth authorization failed");
			const errorPage = generateOAuthCallbackPage({
				title: "Authentication Failed",
				message: error_description || error || "OAuth Authorization Failed. Please try again.",
				isSuccess: false
			});
			return c.html(errorPage);
		}
		const pkceData = retrievePKCEVerifier(state);
		if (!pkceData) {
			logger.error({ state }, "Invalid or expired OAuth state");
			const expiredPage = generateOAuthCallbackPage({
				title: "Session Expired",
				message: "OAuth Session Expired: The OAuth session has expired or is invalid. Please try again.",
				isSuccess: false
			});
			return c.html(expiredPage);
		}
		const { codeVerifier, toolId, tenantId, projectId, userId, clientInformation, metadata, resourceUrl } = pkceData;
		const resolvedRef = await getProjectMainResolvedRef(manageDbClient_default)(tenantId, projectId);
		const tool = await withRef(manageDbPool_default, resolvedRef, (db) => getToolById(db)({
			scopes: {
				tenantId,
				projectId
			},
			toolId
		}));
		if (!tool) throw new Error(`Tool ${toolId} not found`);
		logger.info({
			toolId,
			tenantId,
			projectId
		}, "Processing OAuth callback");
		logger.info({ toolId }, "Exchanging authorization code for access token");
		const credentialStores = c.get("credentialStores");
		const baseUrl = getBaseUrlFromRequest(c);
		const { tokens } = await oauthService.exchangeCodeForTokens({
			code,
			codeVerifier,
			clientInformation,
			metadata,
			resourceUrl,
			mcpServerUrl: tool.config.mcp.server.url,
			baseUrl
		});
		logger.info({
			toolId,
			tokenType: tokens.token_type,
			hasRefresh: !!tokens.refresh_token
		}, "Token exchange successful");
		const credentialTokenKey = tool.credentialScope === "user" && userId ? `oauth_token_${toolId}_${encodeURIComponent(userId)}` : `oauth_token_${toolId}`;
		let newCredentialData;
		const keychainStore = credentialStores.get("keychain-default");
		if (keychainStore) try {
			await keychainStore.set(credentialTokenKey, JSON.stringify(tokens));
			newCredentialData = {
				id: generateId(),
				toolId,
				userId,
				name: tool.name,
				type: CredentialStoreType.keychain,
				credentialStoreId: "keychain-default",
				retrievalParams: { key: credentialTokenKey }
			};
		} catch (error$1) {
			logger.info({ error: error$1 instanceof Error ? error$1.message : error$1 }, "Keychain store not available.");
		}
		if (!newCredentialData) throw new Error("No credential store found");
		const newCredential = await withRef(manageDbPool_default, resolvedRef, async (db) => {
			const credential = await findOrCreateCredential(db, tenantId, projectId, newCredentialData);
			await updateTool(db)({
				scopes: {
					tenantId,
					projectId
				},
				toolId,
				data: { credentialReferenceId: credential.id }
			});
			return credential;
		}, {
			commit: true,
			commitMessage: `OAuth: Link credential to tool ${toolId}`,
			author: {
				name: "oauth-callback",
				email: "api@inkeep.com"
			}
		});
		logger.info({
			toolId,
			credentialId: newCredential.id
		}, "OAuth flow completed successfully");
		const successPage = generateOAuthCallbackPage({
			title: "Authentication Complete",
			message: "You have been successfully authenticated.",
			isSuccess: true
		});
		return c.html(successPage);
	} catch (error) {
		logger.error({ error }, "OAuth callback processing failed");
		const errorPage = generateOAuthCallbackPage({
			title: "Processing Failed",
			message: "OAuth Processing Failed. Please try again.",
			isSuccess: false
		});
		return c.html(errorPage);
	}
});
var oauth_default = app;

//#endregion
export { oauth_default as default };