import { Env } from '../config/env.js';
import { Logger } from '../config/logger.js';
import { MicroserviceManager, getEnabledServices, isMicroservicesEnabled, } from './MicroserviceManager.js';
import { getServiceId, serviceMatchesAllowList, } from './ServiceManifest.js';
import fs from '../node-singletons/fs.js';
import * as path from '../node-singletons/path.js';
import { ProjectRuntime } from '../runtime/ProjectRuntime.js';
const projectCwd = process.cwd();
/**
 * Discover services from filesystem
 */
async function runDiscoverServices(state) {
    if (!isMicroservicesEnabled()) {
        return [];
    }
    try {
        const manifestServices = await discoverServicesFromManifest(state);
        if (manifestServices.length > 0) {
            Logger.info(`✅ Discovered ${manifestServices.length} microservices from static manifest`);
            return manifestServices;
        }
        const domains = getDomains(state.servicesDir);
        const services = [];
        for (const domain of domains) {
            const domainServices = discoverServicesInDomain(state, domain, services.length);
            services.push(...domainServices);
        }
        Logger.info(`✅ Discovered ${services.length} microservices`);
        return services;
    }
    catch (err) {
        Logger.error('Failed to discover microservices', err);
        handleDiscoveryError(err);
        return [];
    }
}
/**
 * Register discovered services with manager
 */
async function runRegisterServices(self) {
    const services = await self.discoverServices();
    const manager = MicroserviceManager.getInstance();
    for (const config of services) {
        manager.register(config);
    }
    Logger.info(`📋 Registered ${services.length} services with manager`);
}
/**
 * Initialize services (discover, register, run migrations if needed)
 */
async function runInitialize(self) {
    if (isMicroservicesEnabled() === false) {
        Logger.info('ℹ️  Microservices disabled (MICROSERVICES env var not set)');
        return;
    }
    Logger.info('🚀 Initializing microservices...');
    // Discover and register services
    await self.registerServices();
    // Run migrations if configured
    const services = self.getAllServiceConfigs();
    for (const config of services) {
        if (config.database?.migrations === true) {
            Logger.info(`📦 Service ${config.name} has migrations enabled (database isolation: ${config.database.isolation})`);
        }
    }
    Logger.info('✅ Microservices initialized');
}
/**
 * Microservice Bootstrap - Handles service discovery and initialization
 */
export const MicroserviceBootstrap = Object.freeze(() => {
    let instance;
    return {
        getInstance() {
            instance ??= this.create();
            return instance;
        },
        /**
         * Reset the singleton instance (for testing)
         */
        reset() {
            instance = undefined;
        },
        /**
         * Create a new microservice bootstrap instance
         */
        create() {
            const state = {
                serviceConfigs: new Map(),
                servicesDir: path.join(projectCwd, 'src', 'services'),
            };
            const self = {
                /**
                 * Set custom services directory
                 */
                setServicesDir(dir) {
                    state.servicesDir = dir;
                },
                getServicesDir() {
                    return state.servicesDir;
                },
                /**
                 * Discover services from filesystem
                 */
                async discoverServices() {
                    return runDiscoverServices(state);
                },
                /**
                 * Register discovered services with manager
                 */
                async registerServices() {
                    return runRegisterServices(this);
                },
                /**
                 * Get service configuration
                 */
                getServiceConfig(domain, name) {
                    return state.serviceConfigs.get(getServiceKey(domain, name));
                },
                /**
                 * Get all discovered service configurations
                 */
                getAllServiceConfigs() {
                    return Array.from(state.serviceConfigs.values());
                },
                /**
                 * Check if service has database isolation
                 */
                isServiceIsolated(domain, name) {
                    const config = this.getServiceConfig(domain, name);
                    return config?.database?.isolation === 'isolated' || false;
                },
                /**
                 * Get service auth strategy
                 */
                getServiceAuthStrategy(domain, name) {
                    const config = this.getServiceConfig(domain, name);
                    return config?.auth?.strategy ?? 'none';
                },
                /**
                 * Check if service has tracing enabled
                 */
                isTracingEnabled(domain, name) {
                    const config = this.getServiceConfig(domain, name);
                    return config?.tracing?.enabled ?? false;
                },
                /**
                 * Get tracing sampling rate (0.0 to 1.0)
                 */
                getTracingSamplingRate(domain, name) {
                    const config = this.getServiceConfig(domain, name);
                    return config?.tracing?.samplingRate ?? 1;
                },
                /**
                 * Initialize services (discover, register, run migrations if needed)
                 */
                async initialize() {
                    return runInitialize(this);
                },
            };
            return self;
        },
    };
})();
/**
 * Generate service key for registry lookup
 */
function getServiceKey(domain, name) {
    return getServiceId(domain, name);
}
async function discoverServicesFromManifest(state) {
    await ProjectRuntime.tryLoadNodeRuntime();
    const manifest = ProjectRuntime.getServiceManifest();
    if (manifest.length === 0)
        return [];
    const enabledServices = getEnabledServices();
    const discovered = manifest
        .filter((entry) => serviceMatchesAllowList(entry.id, entry.name, enabledServices))
        .map((entry, index) => createServiceConfigFromManifest(entry, index));
    state.serviceConfigs.clear();
    for (const config of discovered) {
        state.serviceConfigs.set(getServiceKey(config.domain, config.name), config);
    }
    return discovered;
}
/**
 * Get all domains in services directory
 */
function getDomains(servicesDir) {
    if (!fs.existsSync(servicesDir))
        return [];
    return fs.readdirSync(servicesDir).filter((file) => {
        const filePath = path.join(servicesDir, file);
        return fs.statSync(filePath).isDirectory();
    });
}
/**
 * Check if a service is enabled via environment
 */
function isServiceEnabled(serviceName, enabledServices) {
    return enabledServices.length === 0 || enabledServices.includes(serviceName);
}
/**
 * Load service configuration from file
 */
function loadServiceConfig(domain, serviceName, configPath, index) {
    const configData = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
    return {
        id: getServiceId(domain, serviceName),
        name: serviceName,
        domain,
        port: configData.port ?? 3001 + index,
        version: configData.version,
        description: configData.description,
        dependencies: configData.dependencies ?? [],
        healthCheck: configData.healthCheck ?? '/health',
        database: {
            isolation: configData.database?.isolation ?? 'shared',
            migrations: configData.database?.migrations !== false,
        },
        auth: {
            strategy: configData.auth?.strategy ?? 'none',
            secretKey: configData.auth?.secretKey,
            publicKey: configData.auth?.publicKey,
        },
        tracing: {
            enabled: configData.tracing?.enabled ?? false,
            samplingRate: configData.tracing?.samplingRate ?? 1,
        },
    };
}
function createServiceConfigFromManifest(entry, index) {
    return {
        id: entry.id,
        name: entry.name,
        domain: entry.domain,
        port: entry.port ?? 3001 + index,
        version: entry.version ?? '1.0.0',
        description: entry.description,
        dependencies: [],
        healthCheck: entry.healthCheck ?? '/health',
        database: {
            isolation: 'shared',
            migrations: true,
        },
        auth: {
            strategy: 'none',
        },
        tracing: {
            enabled: false,
            samplingRate: 1,
        },
    };
}
/**
 * Try to load service configuration if it exists
 */
function tryLoadServiceConfig(state, domain, serviceName, domainPath, index) {
    const configPath = path.join(domainPath, serviceName, 'service.config.json');
    if (!fs.existsSync(configPath))
        return null;
    const config = loadServiceConfig(domain, serviceName, configPath, index);
    state.serviceConfigs.set(getServiceKey(domain, serviceName), config);
    return config;
}
/**
 * Discover all services within a specific domain
 */
function discoverServicesInDomain(state, domain, startIndex) {
    const domainPath = path.join(state.servicesDir, domain);
    const serviceNames = fs.readdirSync(domainPath).filter((file) => {
        const filePath = path.join(domainPath, file);
        return fs.statSync(filePath).isDirectory() && file !== 'shared';
    });
    const services = [];
    const enabledServices = getEnabledServices();
    for (const serviceName of serviceNames) {
        if (isServiceEnabled(serviceName, enabledServices)) {
            const config = tryLoadServiceConfig(state, domain, serviceName, domainPath, startIndex + services.length);
            if (config)
                services.push(config);
        }
    }
    return services;
}
/**
 * Handle discovery errors gracefully
 */
function handleDiscoveryError(err) {
    if (err.code !== 'ENOENT') {
        Logger.error('Error discovering services:', err);
    }
}
/**
 * Check if using shared database isolation
 */
export function isDatabaseShared() {
    return Env.DATABASE_ISOLATION !== 'isolated';
}
/**
 * Get available authentication strategies
 */
export function getAuthStrategies() {
    return ['api-key', 'jwt', 'none', 'custom'];
}
/**
 * Get available database isolations
 */
export function getDatabaseIsolations() {
    return ['shared', 'isolated'];
}
/**
 * Check if request tracing is globally enabled
 */
export function isTracingGloballyEnabled() {
    return Env.MICROSERVICES_TRACING === true;
}
/**
 * Get global tracing sampling rate
 */
export function getGlobalTracingSamplingRate() {
    const rate = Env.MICROSERVICES_TRACING_RATE;
    return Math.min(Math.max(rate, 0), 1); // Clamp between 0 and 1
}
export const MicroservicesConfig = {
    isDatabaseShared,
    getAuthStrategies,
    getDatabaseIsolations,
    isTracingGloballyEnabled,
    getGlobalTracingSamplingRate,
};
export default MicroserviceBootstrap;
