/**
 * Application Bootstrap
 * Entry point for running the ZinTrust server
 * Sealed namespace for immutability
 */
import { Application } from './Application.js';
import { Server } from './Server.js';
import { appConfig } from '../config/app.js';
import { Env } from '../config/env.js';
import { Logger } from '../config/logger.js';
import { ErrorFactory } from '../exceptions/ZintrustError.js';
import { ProjectRuntime } from '../runtime/ProjectRuntime.js';
import { WorkerProjectAutoImports } from '../runtime/WorkerProjectAutoImports.js';
import { loadWorkersModule } from '../runtime/WorkersModule.js';
let appInstance;
let serverInstance;
let isShuttingDown = false;
let shutdownHandlersRegistered = false;
const logBootstrapErrorDetails = (error) => {
    // Best-effort: surface startup config validation details (already redacted)
    // so container runs show which env vars are missing/misconfigured.
    try {
        const details = error?.details;
        if (details?.errors !== undefined) {
            Logger.error('Startup configuration errors:', details.errors);
        }
    }
    catch {
        // best-effort logging
    }
    // Best-effort: surface startup health-check report details.
    try {
        const details = error?.details;
        if (details?.report !== undefined) {
            Logger.error('Startup health report:', details.report);
        }
    }
    catch {
        // best-effort logging
    }
};
const startSchedulesIfNeeded = async (application) => {
    try {
        if (Env.getBool('SCHEDULES_ENABLED', false) === false)
            return;
        const runtime = appConfig.detectRuntime();
        if (runtime !== 'nodejs' && runtime !== 'fargate')
            return;
        const { SchedulerRuntime } = await import('../scheduler/SchedulerRuntime.js');
        const coreSchedules = await import('../schedules/index.js');
        let appSchedules = {};
        try {
            const appSchedulesModuleId = '@app/' + 'Schedules';
            appSchedules = (await import(appSchedulesModuleId));
        }
        catch {
            /* v8 ignore next */
            appSchedules = {};
        }
        const isSchedule = (value) => {
            if (value === null || value === undefined || typeof value !== 'object')
                return false;
            return ('name' in value &&
                typeof value.name === 'string' &&
                value.name.trim().length > 0);
        };
        const coreScheduleEntries = Object.values(coreSchedules).filter(isSchedule);
        const appScheduleEntries = Object.values(appSchedules).filter(isSchedule);
        SchedulerRuntime.registerMany(coreScheduleEntries, 'core');
        SchedulerRuntime.registerMany(appScheduleEntries, 'app');
        SchedulerRuntime.start();
        // Add shutdown hook to stop schedules gracefully
        const shutdownManager = application.getContainer().get('shutdownManager');
        if ((typeof shutdownManager === 'object' || typeof shutdownManager === 'function') &&
            shutdownManager !== null &&
            'add' in shutdownManager &&
            typeof shutdownManager.add === 'function') {
            shutdownManager.add(async () => {
                const scheduleTimeoutMs = Env.getInt('SCHEDULE_SHUTDOWN_TIMEOUT_MS', 30000);
                await SchedulerRuntime.stop(scheduleTimeoutMs);
            });
        }
    }
    catch (err) {
        Logger.warn('Failed to start schedules:', err);
    }
};
const withTimeout = async (promise, timeoutMs, label) => {
    if (!Number.isFinite(timeoutMs) || timeoutMs <= 0)
        return promise;
    let timeoutId;
    try {
        const timeoutPromise = new Promise((_, reject) => {
            timeoutId = globalThis.setTimeout(() => {
                reject(ErrorFactory.createGeneralError(label, { timeoutMs }));
            }, timeoutMs);
        });
        return await Promise.race([promise, timeoutPromise]);
    }
    finally {
        if (timeoutId !== undefined)
            globalThis.clearTimeout(timeoutId);
    }
};
const gracefulShutdown = async (signal) => {
    if (isShuttingDown)
        return;
    isShuttingDown = true;
    const shutdownBudgetMs = Env.getInt('SHUTDOWN_TIMEOUT', 1500);
    const minForceExitMs = shutdownBudgetMs + 250;
    const forceExitMs = Math.max(Env.getInt('SHUTDOWN_FORCE_EXIT_MS', 10000), minForceExitMs);
    const deadlineMs = Date.now() + shutdownBudgetMs;
    const remainingMs = () => Math.max(0, deadlineMs - Date.now());
    Logger.info(`${signal} received, shutting down gracefully...`);
    try {
        const forceExitTimer = globalThis.setTimeout(() => {
            process.exit(0);
        }, forceExitMs);
        // Best-effort: don't keep the process alive just for this timer
        forceExitTimer.unref?.();
        await withTimeout((async () => {
            // Shutdown worker management system FIRST (before database closes)
            if (appConfig.worker === true &&
                (appConfig.detectRuntime() === 'nodejs' || appConfig.detectRuntime() === 'lambda') &&
                appConfig.dockerWorker === false) {
                try {
                    const workers = await loadWorkersModule();
                    const workerBudgetMs = Math.min(15000, remainingMs());
                    await withTimeout(workers.WorkerShutdown.shutdown({
                        signal,
                        timeout: workerBudgetMs,
                        forceExit: false,
                    }), workerBudgetMs, 'Worker shutdown timed out');
                }
                catch (error) {
                    Logger.warn('Worker shutdown failed (continuing with app shutdown)', error);
                }
            }
            if (serverInstance !== undefined) {
                await serverInstance.close();
            }
            if (appInstance !== undefined) {
                try {
                    const appBudgetMs = Math.min(5000, remainingMs());
                    await withTimeout(appInstance.shutdown(), appBudgetMs, 'App shutdown timed out');
                }
                catch (error) {
                    Logger.warn('App shutdown failed or timed out, forcing exit', error);
                }
            }
        })(), shutdownBudgetMs, 'Graceful shutdown timed out');
        globalThis.clearTimeout(forceExitTimer);
        process.exit(0);
    }
    catch (error) {
        Logger.error('Graceful shutdown failed:', error);
        process.exit(1);
    }
};
async function useWorkerStarter() {
    // Check if workers are enabled in this environment
    if (!appConfig.worker || appConfig.dockerWorker === true) {
        Logger.info('Workers disabled in this runtime (WORKER_ENABLED=false && DOCKER_WORKER=true), skipping worker management initialization');
        return;
    }
    // Initialize worker management system
    let workerInit = null;
    try {
        const projectWorkerEntrypoint = await WorkerProjectAutoImports.tryImportProjectWorkerEntrypoint();
        if (!projectWorkerEntrypoint.ok && projectWorkerEntrypoint.reason === 'import-failed') {
            Logger.warn(`Project worker entrypoint import failed: ${projectWorkerEntrypoint.errorMessage ?? 'Unknown error'}`);
        }
        const workers = await loadWorkersModule();
        if (workers?.WorkerInit !== undefined) {
            workerInit = workers.WorkerInit;
            await workers.WorkerInit.initialize({
                enableResourceMonitoring: true,
                enableHealthMonitoring: true,
                enableAutoScaling: false, // Disabled by default, enable via config
                registerShutdownHandlers: true,
                resourceMonitoringInterval: 60000,
            });
            Logger.info('Worker management system initialized');
        }
    }
    catch {
        // Logger.warn('Worker management system initialization failed (non-fatal)', error as Error);
        // Non-fatal - application can still run without worker management
    }
    if (workerInit?.autoStartPersistedWorkers) {
        await workerInit.autoStartPersistedWorkers();
    }
}
/**
 * Bootstrap implementation
 */
const BootstrapFunctions = Object.freeze({
    /**
     * Bootstrap and start the server
     */
    async start() {
        try {
            // Ensure project-installed adapters/drivers are registered for web server.
            // (This is driven by src/zintrust.plugins.ts generated by `zin plugin install`.)
            try {
                const { PluginAutoImports } = await import('../runtime/PluginAutoImports.js');
                if (appConfig.dockerWorker === true) {
                    const officialImports = await PluginAutoImports.tryImportRuntimeAutoImports('base');
                    if (!officialImports.ok) {
                        Logger.warn('Official plugin auto-imports failed:', ErrorFactory.createGeneralError('officialImports', officialImports.errorMessage));
                    }
                }
                const projectImports = await PluginAutoImports.tryImportProjectAutoImports();
                if (!projectImports.ok && projectImports.reason !== 'not-found') {
                    Logger.warn('Project plugin auto-imports failed:', ErrorFactory.createGeneralError('projectImports', projectImports.errorMessage));
                }
            }
            catch (error) {
                // best-effort; run without plugins if loader fails (e.g. non-Node runtime)
                Logger.warn('Plugin auto-imports loader skipped:', error);
            }
            try {
                await ProjectRuntime.tryLoadNodeRuntime();
            }
            catch (error) {
                Logger.warn('Project runtime hook loader skipped:', error);
            }
            // Create application instance
            // if (Env.ZINTRUST_PROJECT_ROOT) {
            // }
            const app = Application.create(Env.ZINTRUST_PROJECT_ROOT || undefined);
            appInstance = app;
            // Boot application
            await app.boot();
            // Get port and host from environment
            const port = Env.getInt('PORT', 7777);
            const host = Env.get('HOST', 'localhost');
            // Create and start server
            const server = Server.create(app, port, host);
            serverInstance = server;
            // Start listening
            await server.listen();
            Logger.info(`Server running at http://${host}:${port}`);
            Logger.info(`ZinTrust documentation at http://${host}:${port}/zintrust-doc`);
            // Start schedules for long-running runtimes (Node.js / Fargate)
            await startSchedulesIfNeeded(app);
            if (appConfig.worker === true &&
                appConfig.dockerWorker === false &&
                (appConfig.detectRuntime() === 'nodejs' || appConfig.detectRuntime() === 'lambda')) {
                await useWorkerStarter();
            }
        }
        catch (error) {
            try {
                Logger.error('Failed to bootstrap application:', error);
            }
            catch {
                // best-effort logging
            }
            logBootstrapErrorDetails(error);
            process.exit(1);
        }
    },
    /**
     * Handle graceful shutdown
     */
    setupShutdownHandler() {
        if (shutdownHandlersRegistered)
            return;
        shutdownHandlersRegistered = true;
        process.on('SIGTERM', async () => {
            await gracefulShutdown('SIGTERM');
        });
        process.on('SIGINT', async () => {
            await gracefulShutdown('SIGINT');
        });
    },
});
// Run bootstrap
await BootstrapFunctions.start().catch((error) => {
    try {
        Logger.error('Failed to bootstrap application:', error);
    }
    catch {
        // best-effort logging
    }
    process.exit(1);
});
// Handle graceful shutdown
BootstrapFunctions.setupShutdownHandler();
