import { D1Provider } from '../../storage/providers/cloudflare/d1Provider.js';
import { KvProvider } from '../../storage/providers/cloudflare/kvProvider.js';
import { R2Provider } from '../../storage/providers/cloudflare/r2Provider.js';
import { FileSystemProvider } from '../../storage/providers/fileSystem/fileSystemProvider.js';
import { InMemoryProvider } from '../../storage/providers/inMemory/inMemoryProvider.js';
import { SupabaseProvider } from '../../storage/providers/supabase/supabaseProvider.js';
import { configurationError } from '../../types-global/errors.js';
import { logger } from '../../utils/internal/logger.js';
import { requestContextService } from '../../utils/internal/requestContext.js';
/** Evaluated at call time (not module load) so worker.ts can set IS_SERVERLESS before first use. */
function isServerless() {
    return typeof process === 'undefined' || process.env.IS_SERVERLESS === 'true';
}
/**
 * Retrieves a Cloudflare binding from globalThis by key.
 * Throws a ConfigurationError if the binding is not present.
 */
function getGlobalBinding(key, context) {
    const g = globalThis;
    if (!(key in g) || g[key] == null) {
        throw configurationError(`${key} binding not available in globalThis. Ensure wrangler.toml is configured correctly.`, context);
    }
    return g[key];
}
/**
 * Creates and returns a storage provider instance based on the provided configuration.
 *
 * This factory decouples the application from concrete storage implementations,
 * allowing the backend to be selected via environment configuration. In serverless
 * environments, automatically falls back to in-memory storage for non-compatible providers.
 *
 * Provider Selection Logic:
 * - Serverless environment: Only `in-memory`, `cloudflare-r2`, `cloudflare-kv` allowed
 * - Node environment: All providers available
 * - Missing config: Throws ConfigurationError
 *
 * @param config - The application configuration object, typically resolved from DI container.
 * @param deps - Optional pre-resolved dependencies for providers (useful for testing/Workers).
 * @returns An instance of a class that implements the IStorageProvider interface.
 *
 * @throws {McpError} JsonRpcErrorCode.ConfigurationError - If:
 *   - filesystem provider selected but STORAGE_FILESYSTEM_PATH not set
 *   - supabase provider selected but SUPABASE_URL or SUPABASE_SERVICE_ROLE_KEY not set
 *   - cloudflare-r2 provider selected in non-serverless environment
 *   - cloudflare-kv provider selected in non-serverless environment
 *   - unknown provider type specified
 *
 * @example
 * ```typescript
 * // Standard usage — DI container resolves clients and passes via deps
 * const provider = createStorageProvider(config, {
 *   supabaseClient: resolvedSupabaseClient,
 * });
 *
 * // Worker usage with Cloudflare bindings
 * const provider = createStorageProvider(config, {
 *   r2Bucket: env.R2_BUCKET,
 *   kvNamespace: env.KV_NAMESPACE,
 * });
 * ```
 */
export function createStorageProvider(config, deps = {}) {
    const context = requestContextService.createRequestContext({
        operation: 'createStorageProvider',
    });
    const providerType = config.storage.providerType;
    if (isServerless() &&
        !['in-memory', 'cloudflare-r2', 'cloudflare-kv', 'cloudflare-d1'].includes(providerType)) {
        throw configurationError(`Storage provider '${providerType}' is not supported in serverless environments. ` +
            `Use one of: 'in-memory', 'cloudflare-r2', 'cloudflare-kv', 'cloudflare-d1'.`);
    }
    logger.info(`Creating storage provider of type: ${providerType}`, context);
    switch (providerType) {
        case 'in-memory':
            return new InMemoryProvider();
        case 'filesystem':
            if (!config.storage.filesystemPath) {
                throw configurationError('STORAGE_FILESYSTEM_PATH must be set for the filesystem storage provider.', context);
            }
            return new FileSystemProvider(config.storage.filesystemPath);
        case 'supabase':
            if (!config.supabase?.url || !config.supabase?.serviceRoleKey) {
                throw configurationError('SUPABASE_URL and SUPABASE_SERVICE_ROLE_KEY must be set for the supabase storage provider.', context);
            }
            if (!deps.supabaseClient) {
                throw configurationError('Supabase client must be provided via deps for the supabase storage provider.', context);
            }
            return new SupabaseProvider(deps.supabaseClient);
        case 'cloudflare-r2':
            if (isServerless()) {
                if (deps.r2Bucket) {
                    return new R2Provider(deps.r2Bucket);
                }
                const r2Binding = getGlobalBinding('R2_BUCKET', context);
                return new R2Provider(r2Binding);
            }
            throw configurationError('Cloudflare R2 storage is only available in a Cloudflare Worker environment.', context);
        case 'cloudflare-kv':
            if (isServerless()) {
                if (deps.kvNamespace) {
                    return new KvProvider(deps.kvNamespace);
                }
                const kvBinding = getGlobalBinding('KV_NAMESPACE', context);
                return new KvProvider(kvBinding);
            }
            throw configurationError('Cloudflare KV storage is only available in a Cloudflare Worker environment.', context);
        case 'cloudflare-d1':
            if (isServerless()) {
                if (deps.d1Database) {
                    return new D1Provider(deps.d1Database);
                }
                const d1Binding = getGlobalBinding('DB', context);
                return new D1Provider(d1Binding);
            }
            throw configurationError('Cloudflare D1 storage is only available in a Cloudflare Worker environment.', context);
        default: {
            const exhaustiveCheck = providerType;
            throw configurationError(`Unhandled storage provider type: ${String(exhaustiveCheck)}`, context);
        }
    }
}
//# sourceMappingURL=storageFactory.js.map