// src/config/env.test.ts
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';

describe('env config', () => {
  const originalEnv = process.env;

  beforeEach(() => {
    vi.resetModules();
    process.env = { ...originalEnv };
  });

  afterEach(() => {
    process.env = originalEnv;
  });

  /**
   * Sets up minimal valid environment variables for config parsing.
   */
  function setValidEnv(overrides: Record<string, string> = {}) {
    process.env = {
      NODE_ENV: 'test',
      // Database (required)
      DB_HOST: 'localhost',
      DB_PORT: '5432',
      DB_USER: 'testuser',
      DB_PASSWORD: 'testpass',
      DB_NAME: 'testdb',
      // Redis (required)
      REDIS_URL: 'redis://localhost:6379',
      // Auth (required - min 32 chars)
      JWT_SECRET: 'this-is-a-test-secret-that-is-at-least-32-characters-long',
      ...overrides,
    };
  }

  describe('successful config parsing', () => {
    it('should parse valid configuration with all required fields', async () => {
      setValidEnv();

      const { config } = await import('./env');

      expect(config.database.host).toBe('localhost');
      expect(config.database.port).toBe(5432);
      expect(config.database.user).toBe('testuser');
      expect(config.database.password).toBe('testpass');
      expect(config.database.name).toBe('testdb');
      expect(config.redis.url).toBe('redis://localhost:6379');
      expect(config.auth.jwtSecret).toBe(
        'this-is-a-test-secret-that-is-at-least-32-characters-long',
      );
    });

    it('should use default values for optional fields', async () => {
      setValidEnv();

      const { config } = await import('./env');

      // Worker defaults
      expect(config.worker.concurrency).toBe(1);
      expect(config.worker.lockDuration).toBe(30000);
      expect(config.worker.emailConcurrency).toBe(10);
      expect(config.worker.analyticsConcurrency).toBe(1);
      expect(config.worker.cleanupConcurrency).toBe(10);
      expect(config.worker.weeklyAnalyticsConcurrency).toBe(1);

      // Server defaults
      expect(config.server.port).toBe(3001);
      expect(config.server.nodeEnv).toBe('test');
      expect(config.server.storagePath).toBe('/var/www/flyer-crawler.projectium.com/flyer-images');

      // AI defaults
      expect(config.ai.geminiRpm).toBe(5);
      expect(config.ai.priceQualityThreshold).toBe(0.5);

      // SMTP defaults
      expect(config.smtp.port).toBe(587);
      expect(config.smtp.secure).toBe(false);
    });

    it('should parse custom port values', async () => {
      setValidEnv({
        DB_PORT: '5433',
        PORT: '4000',
        SMTP_PORT: '465',
      });

      const { config } = await import('./env');

      expect(config.database.port).toBe(5433);
      expect(config.server.port).toBe(4000);
      expect(config.smtp.port).toBe(465);
    });

    it('should parse boolean SMTP_SECURE correctly', async () => {
      setValidEnv({
        SMTP_SECURE: 'true',
      });

      const { config } = await import('./env');

      expect(config.smtp.secure).toBe(true);
    });

    it('should parse false for SMTP_SECURE when set to false', async () => {
      setValidEnv({
        SMTP_SECURE: 'false',
      });

      const { config } = await import('./env');

      expect(config.smtp.secure).toBe(false);
    });

    it('should parse worker concurrency values', async () => {
      setValidEnv({
        WORKER_CONCURRENCY: '5',
        WORKER_LOCK_DURATION: '60000',
        EMAIL_WORKER_CONCURRENCY: '20',
        ANALYTICS_WORKER_CONCURRENCY: '3',
        CLEANUP_WORKER_CONCURRENCY: '15',
        WEEKLY_ANALYTICS_WORKER_CONCURRENCY: '2',
      });

      const { config } = await import('./env');

      expect(config.worker.concurrency).toBe(5);
      expect(config.worker.lockDuration).toBe(60000);
      expect(config.worker.emailConcurrency).toBe(20);
      expect(config.worker.analyticsConcurrency).toBe(3);
      expect(config.worker.cleanupConcurrency).toBe(15);
      expect(config.worker.weeklyAnalyticsConcurrency).toBe(2);
    });

    it('should parse AI configuration values', async () => {
      setValidEnv({
        GEMINI_API_KEY: 'test-gemini-key',
        GEMINI_RPM: '10',
        AI_PRICE_QUALITY_THRESHOLD: '0.75',
      });

      const { config } = await import('./env');

      expect(config.ai.geminiApiKey).toBe('test-gemini-key');
      expect(config.ai.geminiRpm).toBe(10);
      expect(config.ai.priceQualityThreshold).toBe(0.75);
    });

    it('should parse Google configuration values', async () => {
      setValidEnv({
        GOOGLE_MAPS_API_KEY: 'test-maps-key',
        GOOGLE_CLIENT_ID: 'test-client-id',
        GOOGLE_CLIENT_SECRET: 'test-client-secret',
      });

      const { config } = await import('./env');

      expect(config.google.mapsApiKey).toBe('test-maps-key');
      expect(config.google.clientId).toBe('test-client-id');
      expect(config.google.clientSecret).toBe('test-client-secret');
    });

    it('should parse optional SMTP configuration', async () => {
      setValidEnv({
        SMTP_HOST: 'smtp.example.com',
        SMTP_USER: 'smtp-user',
        SMTP_PASS: 'smtp-pass',
        SMTP_FROM_EMAIL: 'noreply@example.com',
      });

      const { config } = await import('./env');

      expect(config.smtp.host).toBe('smtp.example.com');
      expect(config.smtp.user).toBe('smtp-user');
      expect(config.smtp.pass).toBe('smtp-pass');
      expect(config.smtp.fromEmail).toBe('noreply@example.com');
    });

    it('should parse optional JWT_SECRET_PREVIOUS for rotation', async () => {
      setValidEnv({
        JWT_SECRET_PREVIOUS: 'old-secret-that-is-at-least-32-characters-long',
      });

      const { config } = await import('./env');

      expect(config.auth.jwtSecretPrevious).toBe('old-secret-that-is-at-least-32-characters-long');
    });

    it('should handle empty string values as undefined for optional int fields', async () => {
      setValidEnv({
        GEMINI_RPM: '',
        AI_PRICE_QUALITY_THRESHOLD: '   ',
      });

      const { config } = await import('./env');

      // Should use defaults when empty
      expect(config.ai.geminiRpm).toBe(5);
      expect(config.ai.priceQualityThreshold).toBe(0.5);
    });
  });

  describe('convenience helpers', () => {
    it('should export isProduction as false in test env', async () => {
      setValidEnv({ NODE_ENV: 'test' });

      const { isProduction } = await import('./env');

      expect(isProduction).toBe(false);
    });

    it('should export isTest as true in test env', async () => {
      setValidEnv({ NODE_ENV: 'test' });

      const { isTest } = await import('./env');

      expect(isTest).toBe(true);
    });

    it('should export isDevelopment as false in test env', async () => {
      setValidEnv({ NODE_ENV: 'test' });

      const { isDevelopment } = await import('./env');

      expect(isDevelopment).toBe(false);
    });

    it('should export isSmtpConfigured as false when SMTP not configured', async () => {
      setValidEnv();

      const { isSmtpConfigured } = await import('./env');

      expect(isSmtpConfigured).toBe(false);
    });

    it('should export isSmtpConfigured as true when all SMTP fields present', async () => {
      setValidEnv({
        SMTP_HOST: 'smtp.example.com',
        SMTP_USER: 'user',
        SMTP_PASS: 'pass',
        SMTP_FROM_EMAIL: 'noreply@example.com',
      });

      const { isSmtpConfigured } = await import('./env');

      expect(isSmtpConfigured).toBe(true);
    });

    it('should export isAiConfigured as false when Gemini not configured', async () => {
      setValidEnv();

      const { isAiConfigured } = await import('./env');

      expect(isAiConfigured).toBe(false);
    });

    it('should export isAiConfigured as true when Gemini key present', async () => {
      setValidEnv({
        GEMINI_API_KEY: 'test-key',
      });

      const { isAiConfigured } = await import('./env');

      expect(isAiConfigured).toBe(true);
    });

    it('should export isGoogleMapsConfigured as false when not configured', async () => {
      setValidEnv();

      const { isGoogleMapsConfigured } = await import('./env');

      expect(isGoogleMapsConfigured).toBe(false);
    });

    it('should export isGoogleMapsConfigured as true when Maps key present', async () => {
      setValidEnv({
        GOOGLE_MAPS_API_KEY: 'test-maps-key',
      });

      const { isGoogleMapsConfigured } = await import('./env');

      expect(isGoogleMapsConfigured).toBe(true);
    });
  });

  describe('validation errors', () => {
    it('should throw error when DB_HOST is missing', async () => {
      setValidEnv();
      delete process.env.DB_HOST;

      await expect(import('./env')).rejects.toThrow('CONFIGURATION ERROR');
    });

    it('should throw error when DB_USER is missing', async () => {
      setValidEnv();
      delete process.env.DB_USER;

      await expect(import('./env')).rejects.toThrow('CONFIGURATION ERROR');
    });

    it('should throw error when DB_PASSWORD is missing', async () => {
      setValidEnv();
      delete process.env.DB_PASSWORD;

      await expect(import('./env')).rejects.toThrow('CONFIGURATION ERROR');
    });

    it('should throw error when DB_NAME is missing', async () => {
      setValidEnv();
      delete process.env.DB_NAME;

      await expect(import('./env')).rejects.toThrow('CONFIGURATION ERROR');
    });

    it('should throw error when REDIS_URL is missing', async () => {
      setValidEnv();
      delete process.env.REDIS_URL;

      await expect(import('./env')).rejects.toThrow('CONFIGURATION ERROR');
    });

    it('should throw error when REDIS_URL is invalid', async () => {
      setValidEnv({
        REDIS_URL: 'not-a-valid-url',
      });

      await expect(import('./env')).rejects.toThrow('CONFIGURATION ERROR');
    });

    it('should throw error when JWT_SECRET is missing', async () => {
      setValidEnv();
      delete process.env.JWT_SECRET;

      await expect(import('./env')).rejects.toThrow('CONFIGURATION ERROR');
    });

    it('should throw error when JWT_SECRET is too short', async () => {
      setValidEnv({
        JWT_SECRET: 'short',
      });

      await expect(import('./env')).rejects.toThrow('CONFIGURATION ERROR');
    });

    it('should include field path in error message', async () => {
      setValidEnv();
      delete process.env.DB_HOST;

      await expect(import('./env')).rejects.toThrow('database.host');
    });
  });

  describe('environment modes', () => {
    it('should set nodeEnv to development by default', async () => {
      setValidEnv();
      delete process.env.NODE_ENV;

      const { config } = await import('./env');

      expect(config.server.nodeEnv).toBe('development');
    });

    it('should accept production as NODE_ENV', async () => {
      setValidEnv({
        NODE_ENV: 'production',
      });

      const { config, isProduction, isDevelopment, isTest } = await import('./env');

      expect(config.server.nodeEnv).toBe('production');
      expect(isProduction).toBe(true);
      expect(isDevelopment).toBe(false);
      expect(isTest).toBe(false);
    });

    it('should accept development as NODE_ENV', async () => {
      setValidEnv({
        NODE_ENV: 'development',
      });

      const { config, isProduction, isDevelopment, isTest } = await import('./env');

      expect(config.server.nodeEnv).toBe('development');
      expect(isProduction).toBe(false);
      expect(isDevelopment).toBe(true);
      expect(isTest).toBe(false);
    });
  });

  describe('server configuration', () => {
    it('should parse FRONTEND_URL when provided', async () => {
      setValidEnv({
        FRONTEND_URL: 'https://example.com',
      });

      const { config } = await import('./env');

      expect(config.server.frontendUrl).toBe('https://example.com');
    });

    it('should parse BASE_URL when provided', async () => {
      setValidEnv({
        BASE_URL: '/api/v1',
      });

      const { config } = await import('./env');

      expect(config.server.baseUrl).toBe('/api/v1');
    });

    it('should parse STORAGE_PATH when provided', async () => {
      setValidEnv({
        STORAGE_PATH: '/custom/storage/path',
      });

      const { config } = await import('./env');

      expect(config.server.storagePath).toBe('/custom/storage/path');
    });
  });

  describe('Redis configuration', () => {
    it('should parse REDIS_PASSWORD when provided', async () => {
      setValidEnv({
        REDIS_PASSWORD: 'redis-secret',
      });

      const { config } = await import('./env');

      expect(config.redis.password).toBe('redis-secret');
    });
  });
});
