"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.HdoAdminServer = void 0;
const node_http_1 = require("node:http");
const node_fs_1 = require("node:fs");
const node_module_1 = require("node:module");
const node_path_1 = require("node:path");
const node_url_1 = require("node:url");
const admin_ui_1 = require("./admin-ui");
const nodeRequire = (0, node_module_1.createRequire)(__filename);
const THREE_MODULE_PATH = (0, node_path_1.join)((0, node_path_1.dirname)(nodeRequire.resolve('three')), 'three.module.js');
class HdoAdminServer {
    controller;
    options;
    server = null;
    constructor(controller, options) {
        this.controller = controller;
        this.options = options;
    }
    start() {
        if (this.server)
            return;
        this.server = (0, node_http_1.createServer)(async (req, res) => {
            try {
                await this.handle(req, res);
            }
            catch (err) {
                sendJson(res, 500, { error: err instanceof Error ? err.message : String(err) });
            }
        });
        this.server.listen(this.options.port, '127.0.0.1');
    }
    async stop() {
        if (!this.server)
            return;
        const server = this.server;
        this.server = null;
        server.closeAllConnections?.();
        await new Promise((resolve, reject) => {
            server.close((err) => (err ? reject(err) : resolve()));
        });
    }
    async handle(req, res) {
        const method = req.method ?? 'GET';
        const pathname = (0, node_url_1.parse)(req.url ?? '/', true).pathname ?? '/';
        if (method === 'GET' && pathname === '/') {
            sendText(res, 200, (0, admin_ui_1.adminHtml)(), 'text/html; charset=utf-8');
            return;
        }
        if (method === 'GET' && pathname === '/vendor/three.module.js') {
            sendFile(res, THREE_MODULE_PATH, 'application/javascript; charset=utf-8');
            return;
        }
        if (method === 'GET' && pathname === '/api/events') {
            this.streamEvents(req, res);
            return;
        }
        const route = this.route(method, pathname);
        if (!route) {
            sendJson(res, 404, { error: 'not found' });
            return;
        }
        const body = method === 'GET' ? {} : await readBody(req);
        await route(req, res, body);
    }
    streamEvents(req, res) {
        res.writeHead(200, {
            'content-type': 'text/event-stream; charset=utf-8',
            'cache-control': 'no-store',
            'connection': 'keep-alive',
            'access-control-allow-origin': '*'
        });
        const write = (event) => {
            res.write(`event: hdo\n`);
            res.write(`data: ${JSON.stringify(event)}\n\n`);
        };
        const unsubscribe = this.controller.onEvent(write);
        req.on('close', unsubscribe);
    }
    route(method, pathname) {
        if (method === 'GET' && pathname === '/api/snapshot') {
            return async (_req, res) => sendJson(res, 200, await this.controller.snapshot());
        }
        if (method === 'GET' && pathname === '/api/install-commands') {
            return (_req, res) => sendJson(res, 200, this.controller.installCommands());
        }
        if (method === 'POST' && pathname === '/api/settings') {
            return (_req, res, body) => {
                sendJson(res, 200, this.controller.updateSettings(body));
            };
        }
        if (method === 'POST' && pathname === '/api/client/register') {
            return async (_req, res, body) => {
                sendJson(res, 200, await this.controller.registerDevice(body));
            };
        }
        if (method === 'POST' && pathname === '/api/client/manifest') {
            return async (_req, res, body) => {
                const { deviceId } = body;
                sendJson(res, 200, await this.controller.refreshManifest(deviceId));
            };
        }
        if (method === 'POST' && pathname === '/api/client/subscription') {
            return async (_req, res, body) => {
                const { deviceId } = body;
                sendText(res, 200, await this.controller.refreshSubscription(deviceId), 'text/yaml; charset=utf-8');
            };
        }
        if (method === 'POST' && pathname === '/api/client/plugin-states') {
            return async (_req, res, body) => {
                const { deviceId } = body;
                sendJson(res, 200, await this.controller.reportPluginStates(deviceId));
            };
        }
        if (method === 'POST' && pathname === '/api/client/wireguard/prepare') {
            return async (_req, res, body) => {
                sendJson(res, 200, await this.controller.prepareWireGuardPeer(body));
            };
        }
        if (method === 'POST' && pathname === '/api/client/wireguard/open') {
            return async (_req, res) => {
                sendJson(res, 200, await this.controller.openWireGuardProfile());
            };
        }
        if (method === 'POST' && pathname === '/api/client/wireguard/connect') {
            return async (_req, res, body) => {
                sendJson(res, 200, await this.controller.connectWireGuardPeer(body));
            };
        }
        if (method === 'POST' && pathname === '/api/client/wireguard/recover') {
            return async (_req, res, body) => {
                const { reason, allowPrivileged } = body;
                sendJson(res, 200, await this.controller.recoverWireGuardPeer({
                    reason: reason || 'api',
                    allowPrivileged: allowPrivileged !== false
                }));
            };
        }
        if (method === 'GET' && pathname === '/api/client/wireguard/status') {
            return (_req, res) => {
                sendJson(res, 200, this.controller.wireGuardStatus());
            };
        }
        if (method === 'POST' && pathname === '/api/client/wireguard/repair-routes') {
            return async (_req, res) => {
                sendJson(res, 200, await this.controller.repairWireGuardRoutes());
            };
        }
        if (method === 'GET' && pathname === '/api/client/wireguard/daemon/status') {
            return (_req, res) => {
                sendJson(res, 200, this.controller.wireGuardLaunchDaemonStatus());
            };
        }
        if (method === 'POST' && pathname === '/api/client/wireguard/daemon/install') {
            return async (_req, res) => {
                sendJson(res, 200, await this.controller.installWireGuardLaunchDaemon());
            };
        }
        if (method === 'POST' && pathname === '/api/client/wireguard/daemon/uninstall') {
            return async (_req, res, body) => {
                sendJson(res, 200, await this.controller.uninstallWireGuardLaunchDaemon(body));
            };
        }
        if (method === 'POST' && pathname === '/api/client/tasks/run') {
            return async (_req, res) => {
                sendJson(res, 200, await this.controller.executePendingTasks());
            };
        }
        if (method === 'POST' && pathname === '/api/client/services/publish') {
            return async (_req, res, body) => {
                sendJson(res, 200, await this.controller.publishService(body));
            };
        }
        if (method === 'POST' && pathname === '/api/admin/nodes') {
            return async (_req, res, body) => {
                sendJson(res, 200, await this.controller.upsertNode(body));
            };
        }
        const heartbeatMatch = pathname.match(/^\/api\/admin\/nodes\/([^/]+)\/heartbeat$/);
        if (method === 'POST' && heartbeatMatch) {
            return async (_req, res) => {
                sendJson(res, 200, await this.controller.heartbeatNode(decodeURIComponent(heartbeatMatch[1])));
            };
        }
        if (method === 'POST' && pathname === '/api/admin/services') {
            return async (_req, res, body) => {
                sendJson(res, 200, await this.controller.upsertService(body));
            };
        }
        if (method === 'POST' && pathname === '/api/admin/rate-limits') {
            return async (_req, res, body) => {
                sendJson(res, 200, await this.controller.upsertRateLimit(body));
            };
        }
        return null;
    }
}
exports.HdoAdminServer = HdoAdminServer;
async function readBody(req) {
    const chunks = [];
    for await (const chunk of req) {
        chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
    }
    const raw = Buffer.concat(chunks).toString('utf8').trim();
    return raw ? JSON.parse(raw) : {};
}
function sendJson(res, status, data) {
    sendText(res, status, JSON.stringify(data), 'application/json; charset=utf-8');
}
function sendText(res, status, data, contentType = 'text/plain; charset=utf-8') {
    res.writeHead(status, {
        'content-type': contentType,
        'cache-control': 'no-store',
        'access-control-allow-origin': '*'
    });
    res.end(data);
}
function sendFile(res, path, contentType) {
    res.writeHead(200, {
        'content-type': contentType,
        'cache-control': 'public, max-age=3600',
        'access-control-allow-origin': '*'
    });
    (0, node_fs_1.createReadStream)(path).pipe(res);
}
