/*
 * Copyright 2026, Jaime Terrats.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
import { createServer } from 'node:net';
import { existsSync, readFileSync } from 'node:fs';
import { join, extname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { serve } from '@hono/node-server';
import { touchWorkspace } from '../services/workspace-registry.js';
import { workspaceMiddleware } from './middleware/workspace.js';
import { timingMiddleware } from './middleware/timing.js';
import { pingRoute } from './routes/ping.js';
import { detectRoute } from './routes/detect.js';
import { profilesRoute } from './routes/profiles.js';
import { toolsRoute } from './routes/tools.js';
import { configRoutes } from './routes/config.js';
import { tasksRoutes } from './routes/tasks.js';
import { workflowsRoutes } from './routes/workflows.js';
import { recordsRoute } from './routes/records.js';
import { initRoute } from './routes/init.js';
import { statusRoute } from './routes/status.js';
import { delegationRoute } from './routes/delegation.js';
import { eventsRoute } from './routes/events.js';
import { rolesRoute } from './routes/roles.js';
import { spawnRoute } from './routes/spawn.js';
import { workspacesRoute } from './routes/workspaces.js';
import { artifactsRoute } from './routes/artifacts.js';
import { chatRoute } from './routes/chat.js';
import { gitRoute } from './routes/git.js';
import { mcpRoute } from './routes/mcp.js';
import { filesRoute } from './routes/files.js';
import { deployRoute } from './routes/deploy.js';
import { insightsRoute } from './routes/insights.js';
import { contextRoute } from './routes/context.js';
import { lspRoute } from './routes/lsp.js';
import { exportRoute } from './routes/export.js';
export function createBridgeApp(cwd) {
    const app = new Hono();
    app.use('*', cors({ origin: '*', allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'] }));
    app.use('/api/*', timingMiddleware());
    const api = new Hono();
    // Apply workspace middleware to all API routes — resolves active workspace from
    // query param, header, or defaults to the server startup cwd.
    api.use('*', workspaceMiddleware(cwd));
    api.route('/ping', pingRoute(cwd));
    api.route('/detect', detectRoute(cwd));
    api.route('/profiles', profilesRoute(cwd));
    api.route('/tools', toolsRoute(cwd));
    api.route('/config', configRoutes(cwd));
    api.route('/init', initRoute(cwd));
    api.route('/tasks', tasksRoutes(cwd));
    api.route('/workflows', workflowsRoutes(cwd));
    api.route('/records', recordsRoute(cwd));
    api.route('/status', statusRoute(cwd));
    api.route('/roles', rolesRoute(cwd));
    api.route('/delegation', delegationRoute(cwd));
    api.route('/spawn', spawnRoute(cwd));
    api.route('/events', eventsRoute(cwd));
    api.route('/workspaces', workspacesRoute());
    api.route('/artifacts', artifactsRoute(cwd));
    api.route('/chat', chatRoute(cwd));
    api.route('/git', gitRoute(cwd));
    api.route('/mcp', mcpRoute(cwd));
    api.route('/files', filesRoute(cwd));
    api.route('/deploy', deployRoute(cwd));
    api.route('/insights', insightsRoute(cwd));
    api.route('/context', contextRoute(cwd));
    api.route('/lsp', lspRoute(cwd));
    api.route('/export', exportRoute());
    app.route('/api/v1', api);
    return app;
}
const MIME_TYPES = {
    '.html': 'text/html',
    '.js': 'application/javascript',
    '.css': 'text/css',
    '.json': 'application/json',
    '.svg': 'image/svg+xml',
    '.png': 'image/png',
    '.ico': 'image/x-icon',
};
function resolveAppDist() {
    const thisFile = fileURLToPath(import.meta.url);
    const candidate = join(thisFile, '..', '..', '..', 'app', 'dist');
    return existsSync(candidate) ? candidate : null;
}
function serveStatic(app, staticDir) {
    app.get('*', (c) => {
        const path = c.req.path === '/' ? '/index.html' : c.req.path;
        const filePath = join(staticDir, path);
        if (existsSync(filePath)) {
            const content = readFileSync(filePath);
            const mime = MIME_TYPES[extname(filePath)] ?? 'application/octet-stream';
            const isHashed = /\.[a-zA-Z0-9]{8,}\./.test(path);
            const cacheControl = isHashed ? 'public, max-age=31536000, immutable' : 'public, max-age=60';
            return c.body(content, { headers: { 'Content-Type': mime, 'Cache-Control': cacheControl } });
        }
        // SPA fallback — serve index.html for non-file routes
        const indexPath = join(staticDir, 'index.html');
        if (existsSync(indexPath)) {
            const content = readFileSync(indexPath);
            return c.body(content, { headers: { 'Content-Type': 'text/html' } });
        }
        return c.text('Not Found', 404);
    });
}
const MAX_PORT_ATTEMPTS = 10;
function isPortFree(port, hostname) {
    return new Promise((resolve) => {
        const tester = createServer();
        tester.once('error', () => resolve(false));
        tester.listen(port, hostname, () => {
            tester.close(() => resolve(true));
        });
    });
}
export function findAvailablePort(startPort, hostname) {
    const tryPort = (index) => {
        if (index >= MAX_PORT_ATTEMPTS) {
            return Promise.reject(new Error(`No available port found in range ${startPort}-${startPort + MAX_PORT_ATTEMPTS - 1}`));
        }
        return isPortFree(startPort + index, hostname).then((free) => (free ? startPort + index : tryPort(index + 1)));
    };
    return tryPort(0);
}
export async function startBridgeServer(opts) {
    const app = createBridgeApp(opts.cwd);
    const staticDir = opts.staticDir ?? resolveAppDist();
    if (staticDir) {
        serveStatic(app, staticDir);
    }
    try {
        touchWorkspace(opts.cwd);
    }
    catch {
        /* non-critical */
    }
    const hostname = opts.hostname ?? '127.0.0.1';
    const port = await findAvailablePort(opts.port, hostname);
    const server = serve({ fetch: app.fetch, port, hostname });
    server.on('error', (err) => {
        if (err.code === 'EADDRINUSE') {
            const msg = `\nError: port ${port} is already in use.\n`;
            process.stderr.write(msg);
            process.exit(1);
        }
        throw err;
    });
    return { close: () => server.close(), port };
}
//# sourceMappingURL=index.js.map