import express from 'express';
import { join } from 'path';
import { readFileSync } from 'fs';
import { sessions } from './session-manager.js';
import { httpProxyHandler } from './tunnel.js';
// Entra ID config — read from environment variables at startup (optional)
const entraClientId = process.env.ENTRA_CLIENT_ID;
const entraTenantId = process.env.ENTRA_TENANT_ID;
const entraConfigured = !!(entraClientId && entraTenantId);
// Base64-encode the config once at startup (empty string if not configured)
const entraConfigB64 = entraConfigured
    ? Buffer.from(JSON.stringify({
        clientId: entraClientId,
        tenantId: entraTenantId,
    })).toString('base64')
    : '';
export function createApp(webDir, pkg, startedAt) {
    const app = express();
    const filesDir = process.env.AGENTLINK_FILES_DIR;
    // Landing page at root
    app.get('/', (_req, res) => {
        res.sendFile(join(webDir, 'landing.html'));
    });
    // Chinese landing page
    app.get('/zh', (_req, res) => {
        res.sendFile(join(webDir, 'landing.zh.html'));
    });
    // Serve index.html with no-store (Vite hashed filenames handle cache-busting)
    const sendIndexHtml = (_req, res) => {
        res.setHeader('Cache-Control', 'no-store');
        res.sendFile(join(webDir, 'index.html'));
    };
    // Serve index.html with Entra config injected via <meta> tag
    // Cache the injected HTML at startup — it's static content, no need to re-read on every request
    let cachedEntraHtml = null;
    const sendIndexHtmlWithEntra = (_req, res) => {
        try {
            if (!cachedEntraHtml) {
                const html = readFileSync(join(webDir, 'index.html'), 'utf-8');
                cachedEntraHtml = html.replace('<head>', `<head>\n    <meta name="entra-config" content="${entraConfigB64}">`);
            }
            res.setHeader('Cache-Control', 'no-store');
            res.setHeader('Content-Type', 'text/html');
            res.send(cachedEntraHtml);
        }
        catch {
            res.status(500).send('Internal server error');
        }
    };
    // Intercept /index.html before express.static to ensure no-store is applied
    app.get('/index.html', sendIndexHtml);
    // Optional public file downloads from a deployment-managed directory.
    // These files are intentionally not tied to the built web UI output.
    if (filesDir) {
        app.use('/files', express.static(filesDir, {
            dotfiles: 'deny',
            fallthrough: false,
            index: false,
            redirect: false,
            setHeaders(res) {
                res.setHeader('Cache-Control', 'no-cache');
                res.setHeader('X-Content-Type-Options', 'nosniff');
            },
        }));
    }
    // Serve static assets from web/ — hashed assets are immutable, others use no-cache
    app.use(express.static(webDir, {
        setHeaders(res, filePath) {
            if (filePath.includes('assets')) {
                // Vite-hashed files (e.g. index-CqWVRN_z.js) — immutable
                res.setHeader('Cache-Control', 'public, max-age=31536000, immutable');
            }
            else if (filePath.endsWith('.html')) {
                res.setHeader('Cache-Control', 'no-store');
            }
            else {
                res.setHeader('Cache-Control', 'no-cache');
            }
        },
    }));
    // ── Port proxy HTTP handler ────────────────────────────────────────────
    app.all('/s/:sessionId/proxy/:port/*', httpProxyHandler);
    app.all('/s/:sessionId/proxy/:port', httpProxyHandler);
    // Also register under /ms/ path for Entra-protected sessions
    app.all('/ms/:sessionId/proxy/:port/*', httpProxyHandler);
    app.all('/ms/:sessionId/proxy/:port', httpProxyHandler);
    // MSAL SPA callback — serve SPA with Entra config for MSAL.js to handle the auth code
    app.get('/auth/callback', (req, res) => {
        if (!entraConfigured) {
            res.status(500).send('Entra ID not configured.');
            return;
        }
        sendIndexHtmlWithEntra(req, res);
    });
    // SPA fallback: /s/:sessionId → serve versioned index.html (no login)
    // Block /s/ access for Entra-protected sessions — must use /ms/ path
    app.get('/s/:sessionId', (req, res) => {
        const agent = sessions.getAgentBySession(req.params.sessionId);
        if (agent?.entra) {
            res.redirect(302, `/ms/${req.params.sessionId}`);
            return;
        }
        sendIndexHtml(req, res);
    });
    // Protected route: /ms/:sessionId → serve index.html with Entra config (login required)
    app.get('/ms/:sessionId', (req, res) => {
        if (!entraConfigured) {
            res.status(500).send('Entra ID not configured. Set ENTRA_CLIENT_ID and ENTRA_TENANT_ID environment variables.');
            return;
        }
        sendIndexHtmlWithEntra(req, res);
    });
    // Health check
    app.get('/api/health', (_req, res) => {
        res.json({
            status: 'ok',
            agents: sessions.agents.size,
            webClients: sessions.webClients.size,
            timestamp: new Date().toISOString(),
        });
    });
    // Server status (aggregate stats, no agent details exposed)
    app.get('/api/status', (_req, res) => {
        const mem = process.memoryUsage();
        res.json({
            server: {
                version: pkg.version,
                startedAt: startedAt.toISOString(),
                uptimeSeconds: Math.floor((Date.now() - startedAt.getTime()) / 1000),
                memoryMB: Math.round(mem.rss / 1024 / 1024),
            },
            agents: {
                connected: sessions.agents.size,
                versions: (() => {
                    const counts = {};
                    for (const agent of sessions.agents.values()) {
                        const key = agent.version || 'unknown';
                        counts[key] = (counts[key] || 0) + 1;
                    }
                    return counts;
                })(),
            },
            webClients: {
                connected: sessions.webClients.size,
            },
        });
    });
    return app;
}
//# sourceMappingURL=http.js.map