import * as RuntimeConfig from '../../config/index.js';
import { StartupHealthChecks } from '../../health/StartupHealthChecks.js';
import { loadQueueMonitorModule, loadWorkersModule } from '../../runtime/WorkersModule.js';
import { registerCachesFromRuntimeConfig } from '../../cache/CacheRuntimeRegistration.js';
import broadcastConfig from '../../config/broadcast.js';
import { Cloudflare } from '../../config/cloudflare.js';
import { FeatureFlags } from '../../config/features.js';
import { Logger } from '../../config/logger.js';
import notificationConfig from '../../config/notification.js';
import { StartupConfigValidator } from '../../config/StartupConfigValidator.js';
import { existsSync } from '../../node-singletons/fs.js';
import * as path from '../../node-singletons/path.js';
import { pathToFileURL } from '../../node-singletons/url.js';
import { registerDatabasesFromRuntimeConfig } from '../../orm/DatabaseRuntimeRegistration.js';
import { registerMasterRoutes, tryImportOptional } from './registerRoute.js';
import { registerWorkerShutdownHook } from './worker.js';
import { StartupConfigFile, StartupConfigFileRegistry } from '../../runtime/StartupConfigFileRegistry.js';
import { registerBroadcastersFromRuntimeConfig } from '../../tools/broadcast/BroadcastRuntimeRegistration.js';
import { registerNotificationChannelsFromRuntimeConfig } from '../../tools/notification/NotificationRuntimeRegistration.js';
import { registerQueuesFromRuntimeConfig } from '../../tools/queue/QueueRuntimeRegistration.js';
import { registerDisksFromRuntimeConfig } from '../../tools/storage/StorageRuntimeRegistration.js';
const loadRuntimeQueueConfig = async () => {
    try {
        const modulePath = '@runtime-config/queue';
        const loaded = (await import(modulePath));
        return loaded.default ?? queueConfig ?? undefined;
    }
    catch {
        return queueConfig ?? undefined;
    }
};
const readRuntimeConfig = (key, fallback) => {
    try {
        const value = RuntimeConfig[key];
        return (value ?? fallback);
    }
    catch {
        return fallback;
    }
};
const appConfig = readRuntimeConfig('appConfig', {
    port: 7777,
    dockerWorker: false,
    worker: false,
});
// exported solely for tests to exercise the default detectRuntime handler
const cacheConfig = readRuntimeConfig('cacheConfig', RuntimeConfig.cacheConfig);
const databaseConfig = readRuntimeConfig('databaseConfig', {
    default: 'sqlite',
    connections: {},
});
const queueConfig = readRuntimeConfig('queueConfig', RuntimeConfig.queueConfig);
const storageConfig = readRuntimeConfig('storageConfig', RuntimeConfig.storageConfig);
// eslint-disable-next-line @typescript-eslint/require-await
const dbLoader = async () => {
    registerDatabasesFromRuntimeConfig(databaseConfig);
};
const queuesLoader = async () => {
    await registerQueuesFromRuntimeConfig(queueConfig);
};
// eslint-disable-next-line @typescript-eslint/require-await
const cachesLoader = async () => {
    registerCachesFromRuntimeConfig(cacheConfig);
};
const registerFromRuntimeConfig = async () => {
    await dbLoader();
    await queuesLoader();
    await cachesLoader();
    registerBroadcastersFromRuntimeConfig({
        default: broadcastConfig.default,
        drivers: broadcastConfig.drivers,
    });
    registerDisksFromRuntimeConfig(storageConfig);
    registerNotificationChannelsFromRuntimeConfig({
        default: notificationConfig.default,
        drivers: notificationConfig.drivers,
    });
};
/**
 * Helper: Register ConnectionManager shutdown hook
 */
const registerConnectionManagerHook = (shutdownManager) => {
    shutdownManager.add(async () => {
        try {
            const mod = await import('../../orm/ConnectionManager.js');
            await mod.ConnectionManager.shutdownIfInitialized();
        }
        catch {
            /* ignore import failures in restrictive runtimes */
        }
    });
};
/**
 * Helper: Register Database reset hook
 */
const registerDatabaseResetHook = (shutdownManager) => {
    shutdownManager.add(async () => {
        try {
            const mod = await import('../../orm/Database.js');
            mod.resetDatabase();
        }
        catch {
            /* ignore import failures in restrictive runtimes */
        }
    });
};
/**
 * Helper: Register generic reset hook for modules with reset() method
 */
const registerResetHook = (shutdownManager, modulePath, exportName) => {
    shutdownManager.add(async () => {
        try {
            const mod = (await import(modulePath));
            const resetModule = mod[exportName];
            if (resetModule?.reset) {
                resetModule.reset();
            }
        }
        catch {
            /* ignore import failures in restrictive runtimes */
        }
    });
};
/**
 * Helper: Register FileLogWriter flush hook
 */
const registerFileLogFlushHook = (shutdownManager) => {
    shutdownManager.add(async () => {
        try {
            const mod = await import('../../config/FileLogWriter.js');
            mod.FileLogWriter.flush();
        }
        catch {
            /* ignore import failures in restrictive runtimes */
        }
    });
};
export const registerFrameworkShutdownHooks = (shutdownManager) => {
    // Register framework-level shutdown hooks for long-lived resources
    registerConnectionManagerHook(shutdownManager);
    // Ensure worker management system is asked to shutdown BEFORE databases are reset
    registerWorkerShutdownHook(shutdownManager);
    // Database and cache reset
    registerDatabaseResetHook(shutdownManager);
    registerResetHook(shutdownManager, '@cache/Cache', 'Cache');
    // File logging
    registerFileLogFlushHook(shutdownManager);
    // Registry resets
    registerResetHook(shutdownManager, '@broadcast/BroadcastRegistry', 'BroadcastRegistry');
    registerResetHook(shutdownManager, '@storage/StorageDiskRegistry', 'StorageDiskRegistry');
    registerResetHook(shutdownManager, '@notification/NotificationChannelRegistry', 'NotificationChannelRegistry');
    registerResetHook(shutdownManager, '@mail/MailDriverRegistry', 'MailDriverRegistry');
    registerResetHook(shutdownManager, '@tools/queue/Queue', 'Queue');
};
const initializeArtifactDirectories = async (resolvedBasePath) => {
    if (resolvedBasePath === '')
        return;
    if (typeof process === 'undefined')
        return;
    const globalAny = globalThis;
    if (globalAny.CF !== undefined)
        return;
    if (typeof globalAny.WebSocketPair === 'function')
        return;
    if (globalAny.caches !== undefined)
        return;
    let nodeFs;
    try {
        nodeFs = await import('../../node-singletons/fs.js');
    }
    catch {
        return;
    }
    const dirs = ['logs', 'storage', 'tmp'];
    for (const dir of dirs) {
        const fullPath = path.join(resolvedBasePath, dir);
        try {
            if (!nodeFs.existsSync(fullPath)) {
                nodeFs.mkdirSync(fullPath, { recursive: true });
                Logger.info(`✓ Created directory: ${dir}`);
            }
        }
        catch (error) {
            Logger.warn(`Failed to create ${dir} directory`, error);
        }
    }
};
const extractRedisConfigFromQueueConfig = () => {
    const redisConfig = queueConfig.drivers?.redis ?? {};
    const redisHost = typeof redisConfig['host'] === 'string' ? redisConfig['host'] : '127.0.0.1';
    const redisPort = typeof redisConfig['port'] === 'number' && Number.isFinite(redisConfig['port'])
        ? redisConfig['port']
        : 6379;
    const redisPassword = typeof redisConfig['password'] === 'string' ? redisConfig['password'] : '';
    const redisDb = typeof redisConfig['database'] === 'number' && Number.isFinite(redisConfig['database'])
        ? redisConfig['database']
        : 0;
    return {
        host: redisHost,
        port: redisPort,
        password: redisPassword,
        db: redisDb,
    };
};
const loadAndValidateQueueMonitorModule = async () => {
    let workersModule;
    try {
        workersModule = (await loadQueueMonitorModule());
    }
    catch (error) {
        Logger.warn('Failed to load Queue Monitor module', error);
        return null;
    }
    if (!workersModule || !('QueueMonitor' in workersModule)) {
        Logger.warn('Queue Monitor module not available');
        return null;
    }
    const queueMonitorModule = workersModule;
    const { QueueMonitor } = queueMonitorModule;
    if (QueueMonitor === undefined || typeof QueueMonitor.create !== 'function') {
        Logger.warn('Queue Monitor module does not expose QueueMonitor.create');
        return null;
    }
    return queueMonitorModule;
};
const initializeQueueMonitor = async (router) => {
    const runQueueConfig = await loadRuntimeQueueConfig();
    const monitorConfig = runQueueConfig?.monitor;
    if (monitorConfig === undefined) {
        return;
    }
    if (monitorConfig.enabled === false) {
        return;
    }
    const queueMonitorModule = await loadAndValidateQueueMonitorModule();
    if (queueMonitorModule === null) {
        Logger.debug('Queue Monitor is enabled in configuration but module failed to load or is invalid. Skipping Queue Monitor initialization.');
        return;
    }
    const redisConfig = extractRedisConfigFromQueueConfig();
    const { QueueMonitor } = queueMonitorModule;
    const monitor = QueueMonitor.create({
        ...monitorConfig,
        redis: redisConfig,
    });
    try {
        monitor.registerRoutes(router);
    }
    catch (error) {
        Logger.error('Failed to register Queue Monitor routes', error);
    }
    Logger.info(`Queue Monitor routes registered at http://127.0.0.1:${appConfig.port}${monitorConfig.basePath ?? ''}`);
    Logger.info(`Queue Monitor enqueue endpoint at http://127.0.0.1:${appConfig.port}/test/enqueue`);
};
const initializeWorkers = async (router) => {
    const workers = await loadWorkersModule();
    if (workers?.WorkerInit !== undefined && typeof workers.registerWorkerRoutes === 'function') {
        workers.registerWorkerRoutes(router, undefined, { middleware: undefined });
    }
};
const resolveLocalQueueRedisEntry = () => {
    if (typeof process === 'undefined' || typeof process.cwd !== 'function')
        return null;
    const cwd = process.cwd();
    if (cwd.trim() === '')
        return null;
    const localEntry = path.join(cwd, 'dist', 'packages', 'queue-redis', 'src', 'index.js');
    return existsSync(localEntry) ? localEntry : null;
};
const loadQueueHttpGatewayModule = async () => {
    try {
        return (await import('@zintrust/queue-redis'));
    }
    catch {
        const localEntry = resolveLocalQueueRedisEntry();
        if (localEntry === null)
            return undefined;
        const url = pathToFileURL(localEntry).href;
        return (await import(url));
    }
};
const initializeQueueHttpGateway = async (router) => {
    try {
        const module = await loadQueueHttpGatewayModule();
        if (module === undefined) {
            Logger.warn('Queue HTTP gateway module is unavailable (@zintrust/queue-redis not found)');
            return;
        }
        if (module.QueueHttpGateway === undefined ||
            typeof module.QueueHttpGateway.create !== 'function') {
            Logger.warn('Queue HTTP gateway module does not expose QueueHttpGateway.create');
            return;
        }
        module.QueueHttpGateway.create().registerRoutes(router);
        Logger.info('Queue HTTP gateway route registered at /api/_sys/queue/rpc');
    }
    catch (error) {
        Logger.warn('Failed to register Queue HTTP gateway routes', error);
    }
};
const initializeScheduleHttpGateway = async (router) => {
    try {
        const { ScheduleHttpGateway } = await import('../../scheduler/ScheduleHttpGateway.js');
        ScheduleHttpGateway.create().registerRoutes(router);
        Logger.info('Schedule HTTP gateway route registered at /api/_sys/schedule/rpc');
    }
    catch (error) {
        Logger.warn('Failed to register Schedule HTTP gateway routes', error);
    }
};
export const createLifecycle = (params) => {
    const boot = async () => {
        if (params.getBooted())
            return;
        Logger.info(`🚀 Booting ZinTrust Application in ${params.environment} mode...`);
        if (params.environment === 'development') {
            // Clear config registry cache to ensure fresh config loading in watch mode
            // This fixes the issue where config/middleware.ts changes are ignored in watch mode
            StartupConfigFileRegistry.clear();
        }
        StartupConfigValidator.assertValid();
        // Preload project-owned config overrides that must be available synchronously.
        await StartupConfigFileRegistry.preload([
            StartupConfigFile.Middleware,
            StartupConfigFile.Cache,
            StartupConfigFile.Database,
            StartupConfigFile.Queue,
            StartupConfigFile.Storage,
            StartupConfigFile.Mail,
            StartupConfigFile.Broadcast,
            StartupConfigFile.Notification,
        ]);
        FeatureFlags.initialize();
        await StartupHealthChecks.assertHealthy();
        await registerFromRuntimeConfig();
        await initializeArtifactDirectories(params.resolvedBasePath);
        await registerMasterRoutes(params.resolvedBasePath, params.router);
        if (Cloudflare.getWorkersEnv() === null &&
            appConfig.dockerWorker === false &&
            appConfig.worker === true) {
            await initializeWorkers(params.router);
            await initializeQueueMonitor(params.router);
            await initializeQueueHttpGateway(params.router);
            await initializeScheduleHttpGateway(params.router);
        }
        else if (!appConfig.dockerWorker) {
            Logger.info('Skipping worker module initialization (WORKER_ENABLED=false).');
        }
        // Register service providers
        // Bootstrap services
        Logger.info('✅ Application booted successfully');
        params.setBooted(true);
    };
    const shutdown = async () => {
        Logger.info('🛑 Shutting down application...');
        try {
            await params.shutdownManager.run();
        }
        catch (error) {
            Logger.error('Shutdown hook failed:', error);
        }
        // Ensure FileLogWriter.flush is attempted even if dynamic registration failed.
        try {
            const fileLogWriter = await tryImportOptional('@config/FileLogWriter');
            fileLogWriter?.FileLogWriter?.flush?.();
        }
        catch {
            /* best-effort */
        }
        params.setBooted(false);
        Logger.info('✅ Application shut down successfully');
    };
    return { boot, shutdown };
};
