/**
 * Configuration Management
 *
 * Loads and validates environment variables with type safety
 * Supports database-backed configuration with .env fallback
 * Includes dynamic config refresh when database settings change
 */

import { randomUUID } from 'crypto';
import dotenv from 'dotenv';
import { z } from 'zod';
import { EventEmitter } from 'events';
import Redis from 'ioredis';
import { settingsService } from '../services/settingsService';
import { logger } from '../utils/logger';

// Event emitter for config changes
const configEmitter = new EventEmitter();

// Config change event types
export type ConfigChangeEvent = 'gemini' | 'paypal' | 'pricing' | 'credits' | 'rateLimits' | 'all';

/**
 * Subscribe to config change events
 * Services can use this to refresh their config when database settings change
 */
export function onConfigChange(event: ConfigChangeEvent, listener: () => void): void {
  configEmitter.on(`config:${event}`, listener);
}

/**
 * Remove a config change listener
 */
export function offConfigChange(event: ConfigChangeEvent, listener: () => void): void {
  configEmitter.off(`config:${event}`, listener);
}

// Load environment variables
dotenv.config();

// Configuration schema with validation
const ConfigSchema = z.object({
  // Node environment
  nodeEnv: z.enum(['development', 'production', 'test']).default('development'),
  port: z.coerce.number().default(3000),

  // Database
  databaseUrl: z.string().url(),
  databasePoolSize: z.coerce.number().default(20),

  // Redis
  redisHost: z.string().default('localhost'),
  redisPort: z.coerce.number().default(6379),
  redisPassword: z.string().optional(),
  redisDb: z.coerce.number().default(0),

  // JWT secrets (always from .env, never from database)
  jwtAccessSecret: z.string().min(32),
  jwtRefreshSecret: z.string().min(32),
  jwtAdminSecret: z.string().min(32),
  jwtAccessExpiry: z.string().default('15m'),
  jwtRefreshExpiry: z.string().default('7d'),

  // Google Gemini API (optional - can be from database)
  geminiApiKey: z.string().optional(),

  // PayPal (optional - can be from database)
  paypalClientId: z.string().optional(),
  paypalClientSecret: z.string().optional(),
  paypalWebhookId: z.string().optional(),
  paypalMode: z.enum(['sandbox', 'live']).default('sandbox'),
  paypalPlanStarterMonthly: z.string().optional(),
  paypalPlanStarterAnnual: z.string().optional(),
  paypalPlanProfessionalMonthly: z.string().optional(),
  paypalPlanProfessionalAnnual: z.string().optional(),
  paypalPlanEnterpriseMonthly: z.string().optional(),
  paypalPlanEnterpriseAnnual: z.string().optional(),
  paypalProductId: z.string().optional(),

  // Email (SendGrid)
  sendgridApiKey: z.string().optional(),
  sendgridFromEmail: z.string().email().default('noreply@translate.press.zone'),
  sendgridFromName: z.string().default('translate.press.zone'),

  // Monitoring
  sentryDsn: z.string().url().optional(),
  logLevel: z.enum(['error', 'warn', 'info', 'debug']).default('info'),

  // Alerting
  slackWebhookUrl: z.string().url().optional(),

  // App URLs
  frontendUrl: z.string().url().default('https://translate.press.zone'),
  adminPanelUrl: z.string().url().default('https://admin.translate.press.zone'),
  apiUrl: z.string().url().default('https://api.translate.press.zone'),

  // CORS
  corsAllowedOrigins: z.string().transform((val) => val.split(',')),

  // Rate Limiting
  rateLimitStarter: z.coerce.number().default(60),
  rateLimitProfessional: z.coerce.number().default(120),
  rateLimitEnterprise: z.coerce.number().default(0), // 0 = unlimited

  // Content Limits
  maxSyncChars: z.coerce.number().default(5000),
  maxAsyncChars: z.coerce.number().default(50000),

  // Webhook Settings
  webhookMaxRetries: z.coerce.number().default(5),
  webhookRetryDelayMs: z.coerce.number().default(2000),

  // Pricing (cost per 1K characters in USD)
  pricePerThousandCharacters: z.coerce.number().default(0.002),

  // Credit Allocations
  creditsStarter: z.coerce.number().default(1000000),
  creditsProfessional: z.coerce.number().default(4000000),
  creditsEnterprise: z.coerce.number().default(15000000),
});

// Parse and validate configuration
function loadConfig() {
  try {
    const config = ConfigSchema.parse({
      // Node environment
      nodeEnv: process.env.NODE_ENV,
      port: process.env.PORT,

      // Database
      databaseUrl: process.env.DATABASE_URL,
      databasePoolSize: process.env.DATABASE_POOL_SIZE,

      // Redis
      redisHost: process.env.REDIS_HOST,
      redisPort: process.env.REDIS_PORT,
      redisPassword: process.env.REDIS_PASSWORD,
      redisDb: process.env.REDIS_DB,

      // JWT
      jwtAccessSecret: process.env.JWT_ACCESS_SECRET,
      jwtRefreshSecret: process.env.JWT_REFRESH_SECRET,
      jwtAdminSecret: process.env.JWT_ADMIN_SECRET,
      jwtAccessExpiry: process.env.JWT_ACCESS_EXPIRY,
      jwtRefreshExpiry: process.env.JWT_REFRESH_EXPIRY,

      // Google Gemini API
      geminiApiKey: process.env.GEMINI_API_KEY,

      // PayPal
      paypalClientId: process.env.PAYPAL_CLIENT_ID,
      paypalClientSecret: process.env.PAYPAL_CLIENT_SECRET,
      paypalWebhookId: process.env.PAYPAL_WEBHOOK_ID,
      paypalMode: process.env.PAYPAL_MODE,
      paypalPlanStarterMonthly: process.env.PAYPAL_PLAN_STARTER_MONTHLY,
      paypalPlanStarterAnnual: process.env.PAYPAL_PLAN_STARTER_ANNUAL,
      paypalPlanProfessionalMonthly: process.env.PAYPAL_PLAN_PROFESSIONAL_MONTHLY,
      paypalPlanProfessionalAnnual: process.env.PAYPAL_PLAN_PROFESSIONAL_ANNUAL,
      paypalPlanEnterpriseMonthly: process.env.PAYPAL_PLAN_ENTERPRISE_MONTHLY,
      paypalPlanEnterpriseAnnual: process.env.PAYPAL_PLAN_ENTERPRISE_ANNUAL,
      paypalProductId: process.env.PAYPAL_PRODUCT_ID,

      // Email
      sendgridApiKey: process.env.SENDGRID_API_KEY,
      sendgridFromEmail: process.env.SENDGRID_FROM_EMAIL,
      sendgridFromName: process.env.SENDGRID_FROM_NAME,

      // Monitoring
      sentryDsn: process.env.SENTRY_DSN,
      logLevel: process.env.LOG_LEVEL,

      // Alerting
      slackWebhookUrl: process.env.SLACK_WEBHOOK_URL,

      // App URLs
      frontendUrl: process.env.FRONTEND_URL,
      adminPanelUrl: process.env.ADMIN_PANEL_URL,
      apiUrl: process.env.API_URL,

      // CORS
      corsAllowedOrigins: process.env.CORS_ALLOWED_ORIGINS,

      // Rate Limiting
      rateLimitStarter: process.env.RATE_LIMIT_STARTER,
      rateLimitProfessional: process.env.RATE_LIMIT_PROFESSIONAL,
      rateLimitEnterprise: process.env.RATE_LIMIT_ENTERPRISE,

      // Content Limits
      maxSyncChars: process.env.MAX_SYNC_CHARS,
      maxAsyncChars: process.env.MAX_ASYNC_CHARS,

      // Webhook Settings
      webhookMaxRetries: process.env.WEBHOOK_MAX_RETRIES,
      webhookRetryDelayMs: process.env.WEBHOOK_RETRY_DELAY_MS,

      // Pricing
      pricePerThousandCharacters: process.env.PRICE_PER_1K_CHARACTERS,

      // Credit Allocations
      creditsStarter: process.env.CREDITS_STARTER,
      creditsProfessional: process.env.CREDITS_PROFESSIONAL,
      creditsEnterprise: process.env.CREDITS_ENTERPRISE,
    });

    return config;
  } catch (error) {
    if (error instanceof z.ZodError) {
      const errorMessages = error.errors
        .map((err) => `${err.path.join('.')}: ${err.message}`)
        .join(', ');
      throw new Error(`Configuration validation failed: ${errorMessages}`);
    }
    throw error;
  }
}

// Export configuration
export const config = loadConfig();

// Type export
export type Config = z.infer<typeof ConfigSchema>;

// Helper functions
export const isDevelopment = () => config.nodeEnv === 'development';
export const isProduction = () => config.nodeEnv === 'production';
export const isTest = () => config.nodeEnv === 'test';

export function buildRedisUrl(
  host: string,
  port: number,
  db: number,
  password?: string
): string {
  const auth = password ? `:${encodeURIComponent(password)}@` : '';
  return `redis://${auth}${host}:${port}/${db}`;
}

export const getRedisUrl = () => buildRedisUrl(
  config.redisHost,
  config.redisPort,
  config.redisDb,
  config.redisPassword
);

type ConfigUpdatePublisher = {
  connect(): Promise<unknown>;
  zrangebyscore(_key: string, _minimum: number, _maximum: number): Promise<string[]>;
  set(_key: string, _value: string, _mode: 'EX', _ttlSeconds: number): Promise<unknown>;
  publish(_channel: string, _message: string): Promise<number>;
  smembers(_key: string): Promise<string[]>;
  expire(_key: string, _ttlSeconds: number): Promise<number>;
  quit(): Promise<unknown>;
};

interface ConfigUpdateSubscriber {
  status?: string;
  on(_event: string, _listener: (..._args: any[]) => void): unknown;
  subscribe(_channel: string): Promise<unknown>;
  quit(): Promise<unknown>;
}

interface ConfigAcknowledgementClient {
  get(_key: string): Promise<string | null>;
  ping(): Promise<string>;
  sadd(_key: string, _member: string): Promise<number>;
  expire(_key: string, _ttlSeconds: number): Promise<number>;
  quit(): Promise<unknown>;
}

interface ConfigUpdateRecord {
  id: string;
  categories: ConfigChangeEvent[];
  workerIds: string[];
  settingVersions: Record<string, string>;
}

interface PublishConfigUpdateOptions {
  changeId?: string;
  acknowledgementTimeoutMs?: number;
  now?: () => number;
  sleep?: (_milliseconds: number) => Promise<void>;
  settingVersions?: Record<string, string>;
}

interface ConfigSubscriptionOptions {
  subscriber?: ConfigUpdateSubscriber;
  acknowledgements?: ConfigAcknowledgementClient;
  refresh?: (_categories?: ConfigChangeEvent[]) => Promise<void>;
  verifySettingVersions?: (_versions: Record<string, string>) => Promise<boolean>;
  categories?: ConfigChangeEvent[];
  workerId: string;
}

const CONFIG_CHANGE_TTL_SECONDS = 60;
const CONFIG_ACK_POLL_INTERVAL_MS = 50;
const CONFIG_ACK_TIMEOUT_MS = 5000;
const WORKER_HEARTBEAT_REGISTRY_KEY = 'worker:heartbeats';
const CONFIG_CHANGE_EVENTS: ConfigChangeEvent[] = [
  'gemini',
  'paypal',
  'pricing',
  'credits',
  'rateLimits',
  'all',
];

function isConfigChangeEvent(value: unknown): value is ConfigChangeEvent {
  return typeof value === 'string' && CONFIG_CHANGE_EVENTS.includes(value as ConfigChangeEvent);
}

function parseConfigUpdateRecord(value: string): ConfigUpdateRecord {
  const parsed = JSON.parse(value) as Partial<ConfigUpdateRecord>;
  if (
    typeof parsed.id !== 'string' ||
    !Array.isArray(parsed.categories) ||
    !parsed.categories.every(isConfigChangeEvent) ||
    !Array.isArray(parsed.workerIds) ||
    !parsed.workerIds.every((workerId) => typeof workerId === 'string') ||
    typeof parsed.settingVersions !== 'object' ||
    parsed.settingVersions === null ||
    Array.isArray(parsed.settingVersions) ||
    !Object.entries(parsed.settingVersions).every(
      ([key, version]) => key.length > 0 && typeof version === 'string'
    )
  ) {
    throw new Error('Invalid config update record');
  }
  return parsed as ConfigUpdateRecord;
}

export async function publishConfigUpdate(
  categories: ConfigChangeEvent[],
  publisher?: ConfigUpdatePublisher,
  options: PublishConfigUpdateOptions = {}
): Promise<{ changeId: string; workerIds: string[] }> {
  const client: ConfigUpdatePublisher = publisher ?? new Redis(getRedisUrl(), {
    lazyConnect: true,
    maxRetriesPerRequest: 1,
  });
  const now = options.now ?? Date.now;
  const sleep = options.sleep ?? ((milliseconds) => new Promise((resolve) => {
    setTimeout(resolve, milliseconds);
  }));
  const changeId = options.changeId ?? randomUUID();
  const timeoutMs = options.acknowledgementTimeoutMs ?? CONFIG_ACK_TIMEOUT_MS;
  const settingVersions = options.settingVersions;
  if (!settingVersions || Object.keys(settingVersions).length === 0) {
    throw new Error('Setting versions are required for config update acknowledgement');
  }

  try {
    await client.connect();
    const publishedAt = now();
    const heartbeatTtlMs = Number(process.env.WORKER_HEARTBEAT_TTL_SECONDS || 15) * 1000;
    const workerIds = await client.zrangebyscore(
      WORKER_HEARTBEAT_REGISTRY_KEY,
      publishedAt - heartbeatTtlMs,
      publishedAt
    );
    if (workerIds.length === 0) {
      throw new Error('No healthy workers available for config update');
    }

    const record: ConfigUpdateRecord = { id: changeId, categories, workerIds, settingVersions };
    await client.set(
      `config:change:${changeId}`,
      JSON.stringify(record),
      'EX',
      CONFIG_CHANGE_TTL_SECONDS
    );
    await client.set(
      'config:change:latest',
      changeId,
      'EX',
      CONFIG_CHANGE_TTL_SECONDS
    );
    await client.publish('config:updated', JSON.stringify({ changeId }));

    const deadline = now() + timeoutMs;
    while (true) {
      const acknowledgements = await client.smembers(`config:change:${changeId}:acks`);
      if (workerIds.every((workerId) => acknowledgements.includes(workerId))) {
        await client.expire(`config:change:${changeId}:acks`, CONFIG_CHANGE_TTL_SECONDS);
        return { changeId, workerIds };
      }
      if (now() >= deadline) {
        throw new Error('Timed out waiting for worker configuration acknowledgement');
      }
      await sleep(CONFIG_ACK_POLL_INTERVAL_MS);
    }
  } finally {
    await client.quit();
  }
}

export function startConfigSubscription(options: ConfigSubscriptionOptions): {
  checkReady(): Promise<void>;
  close(): Promise<void>;
} {
  const subscriber = options.subscriber ?? new Redis(getRedisUrl());
  const acknowledgements = options.acknowledgements ?? new Redis(getRedisUrl());
  const refresh = options.refresh ?? refreshConfigFromDatabase;
  const verifySettingVersions = options.verifySettingVersions ?? (
    (versions) => settingsService.matchesSettingVersions(versions)
  );
  let applicationQueue: Promise<void> = Promise.resolve();
  let subscriptionReady = false;

  const applyChange = async (changeId: string): Promise<void> => {
    const serializedRecord = await acknowledgements.get(`config:change:${changeId}`);
    if (!serializedRecord) {
      throw new Error(`Config update record ${changeId} expired or is unavailable`);
    }
    const record = parseConfigUpdateRecord(serializedRecord);
    if (!record.workerIds.includes(options.workerId)) {
      return;
    }

    const categories = record.categories.includes('all')
      ? options.categories
      : options.categories
        ? record.categories.filter((category) => options.categories?.includes(category))
        : record.categories;
    if (!categories || categories.length > 0) {
      await refresh(categories);
    }
    if (!(await verifySettingVersions(record.settingVersions))) {
      throw new Error(`Config update ${record.id} was superseded before application`);
    }
    const acknowledgementKey = `config:change:${record.id}:acks`;
    await acknowledgements.sadd(acknowledgementKey, options.workerId);
    await acknowledgements.expire(acknowledgementKey, CONFIG_CHANGE_TTL_SECONDS);
  };

  const enqueue = (operation: () => Promise<void>, failureMessage: string): Promise<void> => {
    const queued = applicationQueue.then(operation);
    applicationQueue = queued.catch(() => undefined);
    return queued.catch((error) => {
      logger.error(failureMessage, { error });
    });
  };

  subscriber.on('ready', () => enqueue(async () => {
    subscriptionReady = false;
    await subscriber.subscribe('config:updated');
    await refresh(options.categories);
    const latestChangeId = await acknowledgements.get('config:change:latest');
    if (latestChangeId) {
      await applyChange(latestChangeId);
    }
    subscriptionReady = true;
  }, 'Failed to subscribe to config updates'));

  subscriber.on('message', (channel: string, message: string) => {
    if (channel !== 'config:updated') {
      return;
    }

    return enqueue(async () => {
      const parsed = JSON.parse(message) as { changeId?: unknown };
      if (typeof parsed.changeId !== 'string') {
        throw new Error('Config update message does not contain a version');
      }
      await applyChange(parsed.changeId);
    }, 'Failed to apply config update');
  });

  subscriber.on('error', (error: Error) => {
    subscriptionReady = false;
    logger.error('Config subscription Redis error', { error });
  });

  return {
    async checkReady(): Promise<void> {
      if (!subscriptionReady || (subscriber.status && subscriber.status !== 'ready')) {
        throw new Error('Config subscription is not ready');
      }
      const response = await acknowledgements.ping();
      if (response !== 'PONG') {
        throw new Error('Config acknowledgement Redis is not ready');
      }
    },
    async close(): Promise<void> {
      await applicationQueue;
      const results = await Promise.allSettled([
        subscriber.quit(),
        acknowledgements.quit(),
      ]);
      const failures = results
        .filter((result): result is PromiseRejectedResult => result.status === 'rejected')
        .map((result) => result.reason);
      if (failures.length > 0) {
        throw new AggregateError(failures, 'Config subscription shutdown failed');
      }
    },
  };
}

// PayPal plan ID mapping
// Legacy fallback: prefer tierService.getPayPalPlanId() for database-driven lookups.
export const getPayPalPlanId = (tier: string, cycle: 'monthly' | 'annual'): string => {
  const plans: Record<string, Record<string, string | undefined>> = {
    starter: {
      monthly: config.paypalPlanStarterMonthly,
      annual: config.paypalPlanStarterAnnual,
    },
    professional: {
      monthly: config.paypalPlanProfessionalMonthly,
      annual: config.paypalPlanProfessionalAnnual,
    },
    enterprise: {
      monthly: config.paypalPlanEnterpriseMonthly,
      annual: config.paypalPlanEnterpriseAnnual,
    },
  };

  return plans[tier]?.[cycle] || '';
};

// Get rate limit for tier
// Legacy fallback: prefer tierService.getRateLimit() for database-driven lookups.
export const getRateLimit = (tier: string): number => {
  const limits: Record<string, number> = {
    starter: config.rateLimitStarter,
    professional: config.rateLimitProfessional,
    enterprise: config.rateLimitEnterprise,
  };

  return limits[tier] ?? 60;
};

// Get credit allocation for tier
// Legacy fallback: prefer tierService.getCreditAllocation() for database-driven lookups.
export const getCreditAllocation = (tier: string): number => {
  const allocations: Record<string, number> = {
    starter: config.creditsStarter,
    professional: config.creditsProfessional,
    enterprise: config.creditsEnterprise,
  };

  return allocations[tier] ?? 100000;
};

// Get pricing (unified for all models)
export const getTranslationPrice = (): number => {
  return config.pricePerThousandCharacters;
};

/**
 * Database-backed configuration cache
 */
interface DatabaseConfig {
  gemini?: {
    apiKey: string;
  };
  paypal?: {
    clientId: string;
    clientSecret: string;
    webhookId: string;
    mode: 'sandbox' | 'live';
    productId?: string;
  };
  pricing?: {
    per1kCharacters: number;
  };
  credits?: {
    starter: number;
    professional: number;
    enterprise: number;
  };
  rateLimits?: {
    starter: number;
    professional: number;
    enterprise: number;
  };
}

let databaseConfig: DatabaseConfig = {};
let configInitialized = false;

export function useEnvironmentConfigFallback(): void {
  databaseConfig = {};
  configInitialized = true;
}

/**
 * Initialize configuration from database
 * Loads Modal API, PayPal, pricing, and other configs from database
 * Falls back to .env if not found in database
 * Call this once on application startup after database connection
 */
export async function initializeConfigFromDatabase(): Promise<void> {
  if (configInitialized) {
    logger.warn('Configuration already initialized from database');
    return;
  }

  try {
    logger.info('Initializing configuration from database...');

    // Load Gemini config
    const geminiConfig = await settingsService.getGeminiConfig();
    if (geminiConfig) {
      databaseConfig.gemini = geminiConfig;
      logger.info('Gemini API config loaded from database');
    } else if (config.geminiApiKey) {
      logger.info('Gemini API config using .env fallback');
    } else {
      logger.warn('Gemini API config not found in database or .env');
    }

    // Load PayPal config
    const paypalConfig = await settingsService.getPayPalConfig();
    if (paypalConfig) {
      databaseConfig.paypal = paypalConfig;
      logger.info('PayPal config loaded from database');
    } else if (
      config.paypalClientId &&
      config.paypalClientSecret &&
      config.paypalWebhookId
    ) {
      logger.info('PayPal config using .env fallback');
    } else {
      logger.warn('PayPal config not found in database or .env');
    }

    // Load pricing config
    databaseConfig.pricing = await settingsService.getPricingConfig();
    logger.info('Pricing config loaded from database', {
      per1kCharacters: databaseConfig.pricing.per1kCharacters,
    });

    // Load credit allocations
    databaseConfig.credits = await settingsService.getCreditAllocations();
    logger.info('Credit allocations loaded from database', databaseConfig.credits);

    // Load rate limits
    databaseConfig.rateLimits = await settingsService.getRateLimits();
    logger.info('Rate limits loaded from database', databaseConfig.rateLimits);

    configInitialized = true;
    logger.info('Configuration initialization complete');
  } catch (error) {
    logger.error('Failed to initialize configuration from database', { error });
    throw error;
  }
}

/**
 * Refresh configuration from database
 * Call this after admin updates settings to apply changes without restart
 *
 * @param keys Optional array of specific config keys to refresh. If not provided, refreshes all.
 */
export async function refreshConfigFromDatabase(keys?: ConfigChangeEvent[]): Promise<void> {
  const keysToRefresh = keys || ['gemini', 'paypal', 'pricing', 'credits', 'rateLimits'];

  logger.info('Refreshing configuration from database...', { keys: keysToRefresh });

  // Clear settingsService cache to ensure fresh data
  settingsService.clearCache();

  try {
    for (const key of keysToRefresh) {
      switch (key) {
        case 'gemini': {
          const geminiConfig = await settingsService.getGeminiConfig();
          if (!geminiConfig) {
            throw new Error('Gemini API key is incomplete');
          }
          databaseConfig.gemini = geminiConfig;
          logger.info('Gemini API config refreshed from database');
          configEmitter.emit('config:gemini');
          break;
        }
        case 'paypal': {
          const paypalConfig = await settingsService.getPayPalConfig();
          if (paypalConfig) {
            databaseConfig.paypal = paypalConfig;
            logger.info('PayPal config refreshed from database');
          } else if (
            config.paypalClientId &&
            config.paypalClientSecret &&
            config.paypalWebhookId
          ) {
            delete databaseConfig.paypal;
            logger.info('PayPal config using .env fallback');
          } else {
            throw new Error('PayPal credentials are incomplete');
          }
          configEmitter.emit('config:paypal');
          break;
        }
        case 'pricing': {
          databaseConfig.pricing = await settingsService.getPricingConfig();
          logger.info('Pricing config refreshed from database');
          configEmitter.emit('config:pricing');
          break;
        }
        case 'credits': {
          databaseConfig.credits = await settingsService.getCreditAllocations();
          logger.info('Credit allocations refreshed from database');
          configEmitter.emit('config:credits');
          break;
        }
        case 'rateLimits': {
          databaseConfig.rateLimits = await settingsService.getRateLimits();
          logger.info('Rate limits refreshed from database');
          configEmitter.emit('config:rateLimits');
          break;
        }
      }
    }

    // Emit 'all' event if all configs were refreshed
    if (!keys || keys.length === 6) {
      configEmitter.emit('config:all');
    }

    logger.info('Configuration refresh complete');
  } catch (error) {
    logger.error('Failed to refresh configuration from database', { error });
    throw error;
  }
}

/**
 * Get Gemini API configuration
 * Prefers database config over .env
 *
 * @returns Gemini API key
 * @throws Error if Gemini config not found
 */
export function getGeminiConfig(): { apiKey: string } {
  if (!configInitialized) {
    logger.warn('Configuration not initialized, using .env values');
  }

  // Prefer database config
  if (databaseConfig.gemini) {
    return {
      apiKey: databaseConfig.gemini.apiKey,
    };
  }

  // Fall back to .env
  if (config.geminiApiKey) {
    return {
      apiKey: config.geminiApiKey,
    };
  }

  throw new Error('Gemini API configuration not found in database or environment variables');
}

/**
 * Get PayPal configuration
 * Prefers database config over .env
 *
 * @returns PayPal credentials and plan IDs
 * @throws Error if PayPal config not found
 */
export function getPayPalConfig(): {
  clientId: string;
  clientSecret: string;
  webhookId: string;
  mode: 'sandbox' | 'live';
  productId?: string;
} {
  if (!configInitialized) {
    logger.warn('Configuration not initialized, using .env values');
  }

  if (databaseConfig.paypal) {
    return {
      ...databaseConfig.paypal,
      productId: databaseConfig.paypal.productId ?? config.paypalProductId,
    };
  }

  if (
    config.paypalClientId &&
    config.paypalClientSecret &&
    config.paypalWebhookId
  ) {
    return {
      clientId: config.paypalClientId,
      clientSecret: config.paypalClientSecret,
      webhookId: config.paypalWebhookId,
      mode: config.paypalMode,
      productId: config.paypalProductId,
    };
  }

  throw new Error('Complete PayPal credentials are required in the database or environment');
}

/**
 * Get pricing configuration with database override
 *
 * @returns Price per 1K characters in USD
 */
export function getTranslationPriceFromDatabase(): number {
  if (databaseConfig.pricing) {
    return databaseConfig.pricing.per1kCharacters;
  }

  // Fall back to config
  return getTranslationPrice();
}

/**
 * Get credit allocation with database override
 *
 * @param tier Subscription tier
 * @returns Credit amount
 */
export function getCreditAllocationFromDatabase(
  tier: string
): number {
  if (databaseConfig.credits) {
    return (databaseConfig.credits as Record<string, number>)[tier] ?? getCreditAllocation(tier);
  }

  // Fall back to original function
  return getCreditAllocation(tier);
}

/**
 * Get rate limit with database override
 *
 * @param tier Subscription tier
 * @returns Rate limit (requests per minute)
 */
export function getRateLimitFromDatabase(tier: string): number {
  if (databaseConfig.rateLimits) {
    return (databaseConfig.rateLimits as Record<string, number>)[tier] ?? getRateLimit(tier);
  }

  // Fall back to original function
  return getRateLimit(tier);
}

/**
 * Check if configuration has been initialized from database
 */
export function isConfigInitialized(): boolean {
  return configInitialized;
}
