"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.HttpServerCore = void 0;
exports.createHttpServer = createHttpServer;
exports.parseJsonBody = parseJsonBody;
exports.sendJson = sendJson;
exports.sendText = sendText;
/**
 * Module: server/server-core
 *
 * Core HTTP server functionality shared between production and development servers
 */
const http = __importStar(require("http"));
const https = __importStar(require("node:https"));
const path = __importStar(require("path"));
const log_1 = require("../log");
const api_routes_1 = require("./api-routes");
const auth_1 = require("./auth");
const static_server_1 = require("./static-server");
const log = (0, log_1.createLogger)('server-core');
function toParsedUrlQuery(searchParams) {
    const query = {};
    for (const [key, value] of searchParams.entries()) {
        const existing = query[key];
        if (existing === undefined) {
            query[key] = value;
            continue;
        }
        if (typeof existing === 'string') {
            query[key] = [existing, value];
            continue;
        }
        existing.push(value);
    }
    return query;
}
/**
 * Core HTTP server class
 */
class HttpServerCore {
    server;
    config;
    customHandlers = [];
    started = false;
    stopping = null;
    constructor(config) {
        this.config = config;
        const requestHandler = this.handleRequest.bind(this);
        this.server =
            config.tls === undefined
                ? http.createServer(requestHandler)
                : https.createServer({ cert: config.tls.cert, key: config.tls.key }, requestHandler);
    }
    /**
     * Add custom request handler
     */
    addHandler(handler) {
        this.customHandlers.push(handler);
    }
    /**
     * Main request handler
     */
    async handleRequest(req, res) {
        try {
            const requestUrl = new URL(req.url ?? '/', 'http://127.0.0.1');
            const pathname = requestUrl.pathname || '/';
            const query = toParsedUrlQuery(requestUrl.searchParams);
            // Set CORS headers for development
            if (this.config.mode === 'development') {
                this.setCorsHeaders(res);
            }
            // Handle preflight requests
            if (req.method === 'OPTIONS') {
                res.writeHead(200);
                res.end();
                return;
            }
            // Try custom handlers first
            for (const handler of this.customHandlers) {
                if (await handler(req, res, pathname, query)) {
                    return;
                }
            }
            if (pathname === '/f' ||
                pathname === '/f/' ||
                pathname.startsWith('/f/') ||
                pathname === '/workspace' ||
                pathname === '/workspace/' ||
                pathname.startsWith('/workspace/') ||
                pathname === '/rtws' ||
                pathname === '/rtws/' ||
                pathname.startsWith('/rtws/')) {
                const authCheck = (0, auth_1.getHttpAuthCheckAllowUrlParam)(req, this.config.auth ?? { kind: 'disabled' }, requestUrl);
                if (authCheck.kind !== 'ok') {
                    this.sendUnauthorized(res);
                    return;
                }
            }
            // Backend-direct workspace file preview page.
            if (await (0, api_routes_1.handleWorkspaceFilePreviewPage)(req, res, pathname)) {
                return;
            }
            // Handle API routes
            if (pathname.startsWith('/api/')) {
                const authCheck = (0, auth_1.getHttpAuthCheck)(req, this.config.auth ?? { kind: 'disabled' });
                if (authCheck.kind !== 'ok') {
                    this.sendUnauthorized(res);
                    return;
                }
                const apiContext = {
                    clients: this.config.clients,
                    mode: this.config.mode,
                };
                if (await (0, api_routes_1.handleApiRoute)(req, res, pathname, apiContext)) {
                    return;
                }
                this.sendApiNotFound(res);
                return;
            }
            // Handle static files
            const staticHandled = await (0, static_server_1.serveStatic)(pathname, res, {
                mode: this.config.mode,
                domindsInstallRoot: this.resolveDomindsInstallRoot(),
                staticDir: this.config.staticRoot || 'dist',
            });
            // If no handler processed the request, send 404
            if (!staticHandled) {
                this.sendError(res, 404, 'Not Found');
            }
        }
        catch (error) {
            log.error('Error handling request:', error);
            this.sendError(res, 500, 'Internal Server Error');
        }
    }
    /**
     * Set CORS headers for development
     */
    setCorsHeaders(res) {
        res.setHeader('Access-Control-Allow-Origin', '*');
        res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
        res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
    }
    /**
     * Send error response
     */
    sendError(res, statusCode, message) {
        if (!res.headersSent) {
            res.writeHead(statusCode, { 'Content-Type': 'text/plain' });
            res.end(message);
        }
    }
    sendUnauthorized(res) {
        if (res.headersSent)
            return;
        res.writeHead(401, {
            'Content-Type': 'application/json',
            'WWW-Authenticate': 'Bearer',
        });
        res.end(JSON.stringify({ error: 'unauthorized' }));
    }
    sendApiNotFound(res) {
        if (res.headersSent)
            return;
        res.writeHead(404, {
            'Content-Type': 'application/json',
            'Cache-Control': 'no-store',
        });
        res.end(JSON.stringify({ success: false, error: 'Not Found' }));
    }
    /**
     * Start the server on the configured port.
     */
    start() {
        return new Promise((resolve, reject) => {
            const cleanup = () => {
                this.server.off('error', onError);
                this.server.off('listening', onListening);
            };
            const onError = (error) => {
                cleanup();
                reject(error);
            };
            const onListening = () => {
                cleanup();
                const address = this.server.address();
                const actualPort = typeof address === 'object' && address !== null
                    ? address.port
                    : this.config.port;
                this.config = { ...this.config, port: actualPort };
                this.started = true;
                log.debug(`Server listening on ${this.getScheme()}://${this.config.host}:${actualPort}`);
                resolve(actualPort);
            };
            this.server.once('error', onError);
            this.server.once('listening', onListening);
            this.server.listen(this.config.port, this.config.host);
        });
    }
    /**
     * Stop the server
     */
    stop() {
        if (this.stopping !== null)
            return this.stopping;
        if (!this.started)
            return Promise.resolve();
        this.stopping = new Promise((resolve, reject) => {
            this.server.close((error) => {
                this.started = false;
                this.stopping = null;
                if (error !== undefined) {
                    reject(error);
                    return;
                }
                log.info('Server stopped');
                resolve();
            });
        });
        return this.stopping;
    }
    /**
     * Get the underlying HTTP server
     */
    getHttpServer() {
        return this.server;
    }
    getScheme() {
        return this.config.tls === undefined ? 'http' : 'https';
    }
    /**
     * Resolve dominds installation root
     */
    resolveDomindsInstallRoot() {
        // __dirname is either main/server (source) or dist/server (compiled)
        // Go up two levels to reach the project root
        return path.resolve(__dirname, '..', '..');
    }
}
exports.HttpServerCore = HttpServerCore;
/**
 * Create and configure HTTP server
 */
function createHttpServer(config) {
    return new HttpServerCore(config);
}
/**
 * Utility function to parse request body as JSON
 */
function parseJsonBody(req) {
    return new Promise((resolve, reject) => {
        let body = '';
        req.on('data', (chunk) => {
            body += chunk.toString();
        });
        req.on('end', () => {
            try {
                const parsed = body ? JSON.parse(body) : {};
                resolve(parsed);
            }
            catch (error) {
                reject(new Error('Invalid JSON'));
            }
        });
        req.on('error', reject);
    });
}
/**
 * Utility function to send JSON response
 */
function sendJson(res, statusCode, data) {
    if (!res.headersSent) {
        res.writeHead(statusCode, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify(data));
    }
}
/**
 * Utility function to send text response
 */
function sendText(res, statusCode, text) {
    if (!res.headersSent) {
        res.writeHead(statusCode, { 'Content-Type': 'text/plain' });
        res.end(text);
    }
}
