/**
 * Dependency Injection Utilities
 *
 * Provides interfaces and factory helpers for dependency injection,
 * replacing service locator anti-pattern with explicit dependency injection.
 *
 * @packageDocumentation
 */
// =============================================================================
// DEPENDENCY CONTAINER
// =============================================================================
/**
 * Dependency container for managing application dependencies.
 *
 * This is a simple container that allows registering and retrieving dependencies.
 * For most use cases, prefer passing dependencies directly to constructors.
 *
 * Use this container only when:
 * - Multiple services need the same dependencies
 * - You need late binding of dependencies
 * - You're migrating from service locators incrementally
 */
export class DependencyContainer {
    dependencies = new Map();
    /**
     * Register a dependency
     */
    register(key, dependency) {
        this.dependencies.set(key, dependency);
    }
    /**
     * Get a dependency
     * @throws Error if dependency not found
     */
    get(key) {
        const dep = this.dependencies.get(key);
        if (dep === undefined) {
            throw new Error(`Dependency '${key}' not found. Register it before use.`);
        }
        return dep;
    }
    /**
     * Get a dependency or undefined if not found
     */
    getOptional(key) {
        return this.dependencies.get(key);
    }
    /**
     * Check if a dependency is registered
     */
    has(key) {
        return this.dependencies.has(key);
    }
    /**
     * Clear all dependencies (useful for testing)
     */
    clear() {
        this.dependencies.clear();
    }
    /**
     * Get all registered dependency keys
     */
    keys() {
        return Array.from(this.dependencies.keys());
    }
}
// =============================================================================
// DEPENDENCY KEYS
// =============================================================================
/**
 * Well-known dependency keys for type-safe registration
 */
export const DependencyKeys = {
    DATABASE: 'database',
    REDIS: 'redis',
    CONFIG: 'config',
    POOL: 'pool',
    LOGGER: 'logger',
};
// =============================================================================
// FACTORY HELPERS
// =============================================================================
/**
 * Creates a factory function with pre-bound dependencies.
 *
 * This is useful for creating services that need dependencies but also
 * want to allow overrides for testing.
 *
 * @example
 * ```typescript
 * const createService = withDependencies(
 *   (deps: CoreDependencies) => new MyService(deps.database, deps.redis),
 *   () => getDefaultDependencies()
 * );
 *
 * // Use with defaults
 * const service = createService();
 *
 * // Override for testing
 * const testService = createService({ redis: mockRedis });
 * ```
 */
export function withDependencies(factory, getDefaults) {
    return (overrides) => {
        const defaults = getDefaults();
        const merged = { ...defaults, ...overrides };
        return factory(merged);
    };
}
/**
 * Type guard to check if an object has all required dependencies
 */
export function hasRequiredDependencies(deps, required) {
    return required.every((key) => {
        const value = deps[key];
        return value !== undefined && value !== null;
    });
}
// =============================================================================
// LAZY INITIALIZATION HELPERS
// =============================================================================
/**
 * Creates a lazy-initialized dependency.
 *
 * The factory function is called only once on first access.
 * Subsequent accesses return the cached value.
 *
 * @example
 * ```typescript
 * const lazyDb = lazy(() => getDatabase());
 *
 * // First call initializes
 * const db1 = lazyDb();
 *
 * // Subsequent calls return cached value
 * const db2 = lazyDb();
 * ```
 */
export function lazy(factory) {
    let instance;
    let initialized = false;
    return () => {
        if (!initialized) {
            instance = factory();
            initialized = true;
        }
        return instance;
    };
}
/**
 * Creates an async lazy-initialized dependency.
 *
 * @example
 * ```typescript
 * const lazyConnection = lazyAsync(async () => connectToDatabase());
 *
 * // First call initializes
 * const conn1 = await lazyConnection();
 *
 * // Subsequent calls return cached promise
 * const conn2 = await lazyConnection();
 * ```
 */
export function lazyAsync(factory) {
    let promise;
    return () => {
        if (!promise) {
            promise = factory();
        }
        return promise;
    };
}
// =============================================================================
// TESTING UTILITIES
// =============================================================================
/**
 * Creates a mock dependency container for testing.
 *
 * @example
 * ```typescript
 * const mocks = createMockDependencies({
 *   database: mockDatabase,
 *   redis: mockRedis,
 * });
 * ```
 */
export function createMockDependencies(overrides = {}) {
    return {
        database: overrides.database,
        redis: overrides.redis,
        config: overrides.config,
        pool: overrides.pool ?? null,
    };
}
/**
 * Verify that all required dependencies are provided.
 * Throws descriptive error if any are missing.
 */
export function validateDependencies(deps, required, serviceName) {
    const missing = required.filter((key) => {
        const value = deps[key];
        return value === undefined || value === null;
    });
    if (missing.length > 0) {
        throw new Error(`${serviceName} is missing required dependencies: ${missing.join(', ')}. ` +
            `Either inject them via constructor or ensure they are initialized before use.`);
    }
}
// =============================================================================
// DEFAULT CONTAINER INSTANCE
// =============================================================================
/**
 * Default global container instance.
 *
 * IMPORTANT: Prefer explicit dependency injection over using this container.
 * This is provided for gradual migration from service locators.
 */
export const defaultContainer = new DependencyContainer();
//# sourceMappingURL=di.js.map