"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.createServer = void 0;
const cron_handler_1 = require("./cron-handler");
const body_parser_1 = __importDefault(require("body-parser"));
const express_1 = __importDefault(require("express"));
const http_1 = __importDefault(require("http"));
const multer_1 = __importDefault(require("multer"));
const socket_io_1 = require("socket.io");
const cors_1 = __importDefault(require("cors"));
const flows_endpoint_1 = require("./flows-endpoint");
const guards_1 = require("./guards");
const logger_1 = require("./logger");
const steps_1 = require("./steps");
const call_step_file_1 = require("./call-step-file");
const LoggerFactory_1 = require("./LoggerFactory");
const generate_trace_id_1 = require("./generate-trace-id");
const flows_config_endpoint_1 = require("./flows-config-endpoint");
const api_endpoints_1 = require("./api-endpoints");
const createServer = async (lockedData, eventManager, state, config) => {
    const printer = lockedData.printer;
    const app = (0, express_1.default)();
    const server = http_1.default.createServer(app);
    const io = new socket_io_1.Server(server);
    const loggerFactory = new LoggerFactory_1.LoggerFactory(config.isVerbose, io);
    const upload = (0, multer_1.default)();
    const allSteps = [...steps_1.systemSteps, ...lockedData.activeSteps];
    const cronManager = (0, cron_handler_1.setupCronHandlers)(lockedData, eventManager, state, loggerFactory);
    const asyncHandler = (step) => {
        return async (req, res) => {
            const traceId = (0, generate_trace_id_1.generateTraceId)();
            const { name: stepName, flows } = step.config;
            const logger = loggerFactory.create({ traceId, flows, stepName });
            logger.debug('[API] Received request, processing step', { path: req.path });
            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            const request = {
                body: req.body,
                headers: req.headers,
                pathParams: req.params,
                queryParams: req.query,
                files: req.files,
            };
            try {
                const data = request;
                const result = await (0, call_step_file_1.callStepFile)({
                    contextInFirstArg: false,
                    data,
                    step,
                    printer,
                    logger,
                    eventManager,
                    state,
                    traceId,
                });
                if (!result) {
                    res.status(500).json({ error: 'Internal server error' });
                    return;
                }
                if (result.headers) {
                    Object.entries(result.headers).forEach(([key, value]) => res.setHeader(key, value));
                }
                res.status(result.status);
                res.json(result.body);
            }
            catch (error) {
                logger.error('[API] Internal server error', { error });
                console.log(error);
                res.status(500).json({ error: 'Internal server error' });
            }
        };
    };
    app.use(body_parser_1.default.json());
    app.use(body_parser_1.default.urlencoded({ extended: true }));
    const router = express_1.default.Router();
    const addRoute = (step) => {
        const { method, path } = step.config;
        logger_1.globalLogger.debug('[API] Registering route', step.config);
        const handler = asyncHandler(step);
        const methods = {
            GET: () => router.get(path, handler),
            POST: () => router.post(path, upload.any(), handler),
            PUT: () => router.put(path, upload.any(), handler),
            DELETE: () => router.delete(path, handler),
            PATCH: () => router.patch(path, upload.any(), handler),
            OPTIONS: () => router.options(path, handler),
            HEAD: () => router.head(path, handler),
        };
        const methodHandler = methods[method];
        if (!methodHandler) {
            throw new Error(`Unsupported method: ${method}`);
        }
        methodHandler();
    };
    const removeRoute = (step) => {
        const { path, method } = step.config;
        const routerStack = router.stack;
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        const filteredStack = routerStack.filter((layer) => {
            if (layer.route) {
                const match = layer.route.path === path && layer.route.methods[method.toLowerCase()];
                return !match;
            }
            return true;
        });
        router.stack = filteredStack;
    };
    allSteps.filter(guards_1.isApiStep).forEach(addRoute);
    app.use((0, cors_1.default)());
    app.use(router);
    (0, api_endpoints_1.apiEndpoints)(lockedData, app, io);
    (0, flows_endpoint_1.flowsEndpoint)(lockedData, app);
    (0, flows_config_endpoint_1.flowsConfigEndpoint)(app, process.cwd());
    server.on('error', (error) => {
        console.error('Server error:', error);
    });
    const close = async () => {
        cronManager.close();
        await io.close();
        server.close();
    };
    return { app, server, socketServer: io, close, removeRoute, addRoute, cronManager };
};
exports.createServer = createServer;
