/**
 * @fileoverview Hono handler factory for `GET /` — serves the HTML landing
 * page. Cache and body shape depend on `manifest.landing.requireAuth` and,
 * when an auth strategy is provided, whether the caller presents a valid
 * bearer token.
 *
 * @module src/mcp-server/transports/http/landing-page/handler
 */
import { logger } from '../../../../utils/internal/logger.js';
import { requestContextService } from '../../../../utils/internal/requestContext.js';
import { renderLandingPage } from './render.js';
/**
 * Content-Security-Policy for the landing page. The page is fully
 * self-contained: a single inlined `<style>` block, a single inlined
 * copy-to-clipboard `<script>`, and a JSON-LD structured data block. Logos
 * are the only element with a variable source and are always either a
 * data URI or an `https://` URL. Nothing else is fetched, framed, or
 * submitted — the policy reflects that reality.
 */
const CONTENT_SECURITY_POLICY = [
    "default-src 'none'",
    "style-src 'unsafe-inline'",
    "script-src 'unsafe-inline'",
    "img-src 'self' data: https:",
    "form-action 'none'",
    "frame-ancestors 'none'",
    "base-uri 'none'",
].join('; ');
/**
 * Factory for the `GET /` route handler.
 *
 * Cache behavior and body shape depend on `manifest.landing.requireAuth`:
 *
 * | `requireAuth` | Authenticated | Unauthenticated |
 * |:---|:---|:---|
 * | `false` (default) | full page · `Cache-Control: public, max-age=60` | full page · same |
 * | `true` | full page · `Cache-Control: private, max-age=60` · `Vary: Authorization` | reduced hero-only page · same cache headers |
 *
 * When `requireAuth` is `true` the handler validates the bearer token via the
 * provided {@link AuthStrategy} (the same strategy the MCP endpoint uses). A
 * missing, malformed, or invalid token produces the degraded page. When no
 * auth strategy is configured (e.g. `MCP_AUTH_MODE=none`) while `requireAuth`
 * is on, there is no way to authenticate — every caller gets the degraded
 * page. This keeps the inventory hidden by default rather than falling back
 * to a header-presence heuristic that any caller can bypass.
 *
 * When `manifest.transport.publicUrl` is set, the base URL is deterministic,
 * so both page variants (full and degraded) are rendered once at factory
 * time and served from memory on each request — the render pipeline then
 * only runs when the origin must be derived per-request from
 * `new URL(c.req.url).origin`.
 */
export function createLandingPageHandler(manifest, authStrategy) {
    const { publicUrl } = manifest.transport;
    const precomputed = publicUrl
        ? {
            full: renderLandingPage(manifest, publicUrl, false),
            degraded: renderLandingPage(manifest, publicUrl, true),
        }
        : null;
    const requireAuth = manifest.landing.requireAuth;
    // When requireAuth is on without a strategy, `degraded` stays true on every
    // request and nobody unlocks the full inventory. Narrow the strategy once
    // so the per-request closure doesn't need a non-null assertion.
    const verifier = requireAuth && authStrategy ? authStrategy : null;
    return async (c) => {
        const context = requestContextService.createRequestContext({
            operation: 'landingPageHandler',
        });
        const baseUrl = publicUrl ?? new URL(c.req.url).origin;
        let isAuthenticated = false;
        if (verifier) {
            // Matches authMiddleware's extraction: case-sensitive 'Bearer ' prefix,
            // token is everything after. Keeps both surfaces consistent on what
            // counts as a well-formed Authorization header.
            const authHeader = c.req.header('authorization');
            const token = authHeader?.startsWith('Bearer ') ? authHeader.substring(7) : undefined;
            if (token) {
                try {
                    await verifier.verify(token);
                    isAuthenticated = true;
                }
                catch (err) {
                    logger.debug('Landing page bearer validation failed.', {
                        ...context,
                        error: err instanceof Error ? err.message : String(err),
                    });
                }
            }
        }
        const degraded = requireAuth && !isAuthenticated;
        const html = precomputed
            ? degraded
                ? precomputed.degraded
                : precomputed.full
            : renderLandingPage(manifest, baseUrl, degraded);
        logger.debug('Serving landing page.', {
            ...context,
            accept: c.req.header('accept'),
            bytes: html.length,
            requireAuth,
            degraded,
            cached: precomputed !== null,
        });
        c.header('Content-Type', 'text/html; charset=utf-8');
        c.header('X-Content-Type-Options', 'nosniff');
        c.header('Content-Security-Policy', CONTENT_SECURITY_POLICY);
        if (requireAuth) {
            c.header('Cache-Control', 'private, max-age=60');
            c.header('Vary', 'Authorization');
        }
        else {
            c.header('Cache-Control', 'public, max-age=60');
        }
        return c.body(html);
    };
}
//# sourceMappingURL=handler.js.map