"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createAdminApiServer = createAdminApiServer;
const express_1 = __importDefault(require("express"));
const node_http_1 = require("node:http");
const cors_1 = __importDefault(require("cors"));
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
const socket_io_1 = require("socket.io");
const AdminEvents_js_1 = require("../infrastructure/events/AdminEvents.js");
const optionalString = (value) => {
    const trimmed = value?.trim();
    return trimmed ? trimmed : undefined;
};
const index_js_1 = require("./routes/index.js");
const DEFAULT_ADMIN_ORIGIN = "https://apanel.lychee.finance";
function createAdminApiServer(config, cryptoBox, logger, bot, supportModule) {
    if (!config.signalsAdminPort) {
        logger.info("Signals admin API disabled: SIGNALS_ADMIN_PORT not set.");
        return undefined;
    }
    if (!config.signalsAdminToken) {
        logger.warn("Signals admin API disabled: SIGNALS_ADMIN_TOKEN (or ADMIN_PASSWORD) not set.");
        return undefined;
    }
    const app = (0, express_1.default)();
    const corsOrigin = optionalString(process.env.SIGNALS_ADMIN_CORS_ORIGIN) ?? DEFAULT_ADMIN_ORIGIN;
    app.use((0, cors_1.default)({
        origin: corsOrigin,
        credentials: true,
        methods: ["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"],
        allowedHeaders: ["Content-Type", "Authorization"],
    }));
    app.use(express_1.default.json());
    app.get("/", (_req, res) => {
        res.json({ status: "ok", service: "quant-whisper-bot", timestamp: new Date().toISOString() });
    });
    app.get("/health", (_req, res) => {
        res.json({ status: "healthy", service: "signals-admin-api" });
    });
    app.use("/api/v1", (0, index_js_1.createAdminRouter)(config, cryptoBox, bot, supportModule));
    app.use((err, _req, res, _next) => {
        logger.error("Admin API error.", { error: err.message });
        res.status(500).json({ error: "Internal server error." });
    });
    const httpServer = (0, node_http_1.createServer)(app);
    const io = new socket_io_1.Server(httpServer, {
        cors: {
            origin: corsOrigin,
            methods: ["GET", "POST"],
            credentials: true,
        },
        pingTimeout: 60000,
        pingInterval: 25000,
    });
    io.use((socket, next) => {
        const handshakeToken = optionalString(socket.handshake.auth?.token) ||
            optionalString(socket.handshake.headers.authorization?.startsWith("Bearer ")
                ? socket.handshake.headers.authorization.slice(7)
                : undefined);
        if (!handshakeToken) {
            logger.warn("WebSocket authentication failed: missing token.", { socketId: socket.id });
            return next(new Error("Authentication error: missing token"));
        }
        if (config.jwtSecret) {
            try {
                const decoded = jsonwebtoken_1.default.verify(handshakeToken, config.jwtSecret);
                if (decoded.role === "admin" || decoded.role === "super_admin") {
                    socket.data.adminUser = { id: decoded.id ?? "admin", role: decoded.role };
                    return next();
                }
            }
            catch {
                // Not a valid JWT for this secret — fall through to static-token check.
            }
        }
        if (handshakeToken === config.signalsAdminToken) {
            socket.data.adminUser = { id: "admin", role: "admin" };
            return next();
        }
        logger.warn("WebSocket authentication failed: invalid token.", { socketId: socket.id });
        return next(new Error("Authentication error: invalid token"));
    });
    io.on("connection", (socket) => {
        logger.debug("Admin dashboard connected via WebSocket.", {
            socketId: socket.id,
            adminUser: socket.data.adminUser,
        });
        socket.on("disconnect", (reason) => {
            logger.debug("Admin dashboard disconnected from WebSocket.", { socketId: socket.id, reason });
        });
    });
    const forwardPaymentEvent = (eventName) => (transaction) => {
        io.emit(eventName, transaction);
    };
    AdminEvents_js_1.adminEvents.on(AdminEvents_js_1.ADMIN_EVENTS.PAYMENT_PENDING, forwardPaymentEvent("payment:pending"));
    AdminEvents_js_1.adminEvents.on(AdminEvents_js_1.ADMIN_EVENTS.PAYMENT_CONFIRMED, forwardPaymentEvent("payment:confirmed"));
    AdminEvents_js_1.adminEvents.on(AdminEvents_js_1.ADMIN_EVENTS.PAYMENT_EXPIRED, forwardPaymentEvent("payment:expired"));
    if (supportModule) {
        supportModule.eventBus.on("ticket:created", (ticket) => {
            io.emit("support:ticket:created", ticket);
        });
        supportModule.eventBus.on("ticket:updated", (ticket) => {
            io.emit("support:ticket:updated", ticket);
        });
        supportModule.eventBus.on("ticket:assigned", (payload) => {
            io.emit("support:ticket:assigned", payload);
        });
        supportModule.eventBus.on("ticket:statusChanged", (payload) => {
            io.emit("support:ticket:statusChanged", payload);
        });
        supportModule.eventBus.on("ticket:closed", (payload) => {
            io.emit("support:ticket:closed", payload);
        });
        supportModule.eventBus.on("message:created", (payload) => {
            io.emit("support:message:created", payload);
        });
        supportModule.eventBus.on("message:read", (payload) => {
            io.emit("support:message:read", payload);
        });
        supportModule.eventBus.on("agent:online", (payload) => {
            io.emit("support:agent:online", payload);
        });
    }
    const port = config.signalsAdminPort;
    const host = optionalString(process.env.SIGNALS_ADMIN_HOST) ?? "127.0.0.1";
    return {
        app,
        httpServer,
        io,
        port,
        host,
        close: () => new Promise((resolve, reject) => {
            io.close();
            httpServer.close((err) => {
                if (err)
                    reject(err);
                else
                    resolve();
            });
        })
    };
}
