"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
require("dotenv/config");
require("reflect-metadata");
const os_1 = __importDefault(require("os"));
const http_1 = __importDefault(require("http"));
const redis_client_1 = require("../infrastructure/cache/redis-client");
const data_source_1 = require("../infrastructure/persistence/data-source");
const websocket_server_1 = require("../infrastructure/websocket/websocket-server");
const transaction_notification_service_1 = require("../application/services/transaction-notification.service");
const onesignal_push_gateway_1 = require("../infrastructure/push/onesignal-push-gateway");
const repositories_factory_1 = require("./factories/repositories.factory");
const services_factory_1 = require("./factories/services.factory");
const use_cases_factory_1 = require("./factories/use-cases.factory");
const controllers_factory_1 = require("./factories/controllers.factory");
const middleware_factory_1 = require("./factories/middleware.factory");
const routes_factory_1 = require("./factories/routes.factory");
const app_factory_1 = require("./factories/app.factory");
const env_1 = require("./config/env");
const getLanAddresses = () => {
    const interfaces = os_1.default.networkInterfaces();
    const addresses = [];
    for (const name of Object.keys(interfaces)) {
        const iface = interfaces[name];
        if (!iface)
            continue;
        for (const entry of iface) {
            if (entry.family === 'IPv4' && !entry.internal) {
                addresses.push(entry.address);
            }
        }
    }
    return addresses;
};
const startServer = async () => {
    await (0, data_source_1.initializeDatabase)();
    const repositories = (0, repositories_factory_1.createRepositories)();
    const services = (0, services_factory_1.createServices)(redis_client_1.redisClient);
    const useCases = (0, use_cases_factory_1.createUseCases)(repositories, services);
    const controllers = (0, controllers_factory_1.createControllers)(useCases);
    const middlewares = (0, middleware_factory_1.createMiddlewares)(services.logger, redis_client_1.redisClient);
    const routes = (0, routes_factory_1.createRoutes)(controllers, middlewares);
    const app = (0, app_factory_1.createApp)(services.logger, redis_client_1.redisClient, routes, middlewares);
    const httpServer = http_1.default.createServer(app);
    const sockets = new Set();
    httpServer.on('connection', (socket) => {
        sockets.add(socket);
        socket.on('close', () => sockets.delete(socket));
    });
    let closeWebSocketServer;
    if (env_1.env.WEBSOCKET_ENABLED === 'true') {
        const websocketResult = (0, websocket_server_1.createWebSocketServer)({
            httpServer,
            path: env_1.env.WEBSOCKET_PATH,
            logger: services.logger,
        });
        closeWebSocketServer = websocketResult.close;
        const pushGateway = env_1.env.ONESIGNAL_APP_ID && env_1.env.ONESIGNAL_REST_API_KEY
            ? new onesignal_push_gateway_1.OneSignalPushGateway(env_1.env.ONESIGNAL_APP_ID, env_1.env.ONESIGNAL_REST_API_KEY, repositories.deviceRepository, services.logger)
            : undefined;
        if (pushGateway) {
            services.logger.info('[OneSignal] Push gateway enabled', {
                appId: env_1.env.ONESIGNAL_APP_ID,
            });
        }
        else {
            services.logger.info('[OneSignal] Push gateway disabled: missing app ID or REST API key');
        }
        const transactionNotificationService = new transaction_notification_service_1.TransactionNotificationService(services.eventPublisher, repositories.addressRepository, repositories.walletRepository, repositories.deviceRepository, repositories.tokenRepository, repositories.networkRepository, websocketResult.gateway, pushGateway);
        transactionNotificationService.start();
    }
    const server = httpServer.listen(env_1.env.PORT, env_1.env.HOST, () => {
        const lanAddresses = getLanAddresses();
        services.logger.info(`Server running on ${env_1.env.HOST}:${env_1.env.PORT}`, { port: env_1.env.PORT, host: env_1.env.HOST, env: env_1.env.NODE_ENV });
        if (lanAddresses.length > 0) {
            services.logger.info('Server reachable on the local network at:', {
                addresses: lanAddresses.map((address) => `http://${address}:${env_1.env.PORT}`),
            });
        }
    });
    let isShuttingDown = false;
    const gracefulShutdown = async (signal) => {
        if (isShuttingDown) {
            services.logger.info(`Shutdown already in progress; ignoring ${signal}`);
            return;
        }
        isShuttingDown = true;
        services.logger.info(`Received ${signal}. Shutting down gracefully...`);
        if (closeWebSocketServer) {
            await closeWebSocketServer();
        }
        for (const socket of sockets) {
            socket.destroy();
        }
        if (typeof server.closeAllConnections === 'function') {
            server.closeAllConnections();
        }
        const shutdownTimeout = setTimeout(() => {
            services.logger.warn('Graceful shutdown timed out; forcing exit');
            process.exit(1);
        }, 10_000);
        server.close(async () => {
            try {
                await (0, data_source_1.closeDatabase)();
                await (0, redis_client_1.closeRedisClient)();
                services.logger.info('Server closed');
            }
            catch (error) {
                services.logger.error('Error during shutdown cleanup', { error });
            }
            finally {
                clearTimeout(shutdownTimeout);
                process.exit(0);
            }
        });
    };
    process.on('SIGTERM', () => void gracefulShutdown('SIGTERM'));
    process.on('SIGINT', () => void gracefulShutdown('SIGINT'));
};
startServer().catch((error) => {
    // eslint-disable-next-line no-console
    console.error('Failed to start server', error);
    process.exit(1);
});
//# sourceMappingURL=server.js.map