import { existsSync, mkdirSync, rmSync, writeFileSync } from 'fs';
import { join } from 'path';
import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
import {
  getConfig,
  getDatabaseConfig,
  getLoggingConfig,
  getProviderConfig,
  getServiceConfig,
  initializeServiceConfig,
  isDevelopment,
  isProduction,
  isTest,
  resetConfig,
} from '../src/index';

const TEST_DIR = join(__dirname, 'real-usage-tests');

describe('Real Usage Scenarios', () => {
  let originalEnv: NodeJS.ProcessEnv;
  let originalCwd: string;

  beforeEach(() => {
    originalEnv = { ...process.env };
    originalCwd = process.cwd();

    resetConfig();

    if (existsSync(TEST_DIR)) {
      rmSync(TEST_DIR, { recursive: true, force: true });
    }

    setupRealUsageScenarios();
  });

  afterEach(() => {
    process.env = originalEnv;
    process.chdir(originalCwd);
    resetConfig();

    if (existsSync(TEST_DIR)) {
      rmSync(TEST_DIR, { recursive: true, force: true });
    }
  });

  test('should work like real data-ingestion usage', async () => {
    const dataServiceDir = join(TEST_DIR, 'apps', 'data-ingestion');
    process.chdir(dataServiceDir);

    // Simulate how data-ingestion would initialize config
    const config = await initializeServiceConfig();

    // Test typical data-ingestion config access patterns
    expect(config.app.name).toBe('data-ingestion');
    expect(config.service.port).toBe(3001);

    // Test database config access
    const dbConfig = getDatabaseConfig();
    expect(dbConfig.postgres.host).toBe('localhost');
    expect(dbConfig.postgres.port).toBe(5432);
    expect(dbConfig.questdb.host).toBe('localhost');

    // Test provider access
    const yahooConfig = getProviderConfig('yahoo');
    expect(yahooConfig).toBeDefined();
    expect((yahooConfig as any).enabled).toBe(true);

    // Test environment helpers
    expect(isDevelopment()).toBe(true);
    expect(isProduction()).toBe(false);
  });

  test('should work like real web-api usage', async () => {
    const webApiDir = join(TEST_DIR, 'apps', 'web-api');
    process.chdir(webApiDir);

    const config = await initializeServiceConfig();

    expect(config.app.name).toBe('web-api');
    expect(config.service.port).toBe(4000);

    // Web API should have access to all the same configs
    const serviceConfig = getServiceConfig();
    expect(serviceConfig.name).toBe('web-api');

    const loggingConfig = getLoggingConfig();
    expect(loggingConfig.level).toBe('info');
  });

  test('should work like real shared library usage', async () => {
    const cacheLibDir = join(TEST_DIR, 'libs', 'cache');
    process.chdir(cacheLibDir);

    const config = await initializeServiceConfig();

    // Libraries should inherit from root config
    expect(config.app.name).toBe('cache-lib');
    expect(config.app.version).toBe('1.0.0'); // From root

    // Should have access to cache config
    const dbConfig = getDatabaseConfig();
    expect(dbConfig.dragonfly).toBeDefined();
    expect(dbConfig.dragonfly.host).toBe('localhost');
    expect(dbConfig.dragonfly.port).toBe(6379);
  });

  test('should handle production environment correctly', async () => {
    process.env.NODE_ENV = 'production';

    const dataServiceDir = join(TEST_DIR, 'apps', 'data-ingestion');
    process.chdir(dataServiceDir);

    resetConfig();
    const config = await initializeServiceConfig();

    expect(config.environment).toBe('production');
    expect(config.logging.level).toBe('warn'); // Production should use different log level

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

  test('should handle test environment correctly', async () => {
    process.env.NODE_ENV = 'test';

    const dataServiceDir = join(TEST_DIR, 'apps', 'data-ingestion');
    process.chdir(dataServiceDir);

    resetConfig();
    const config = await initializeServiceConfig();

    expect(config.environment).toBe('test');
    expect(config.logging.level).toBe('debug'); // Test should use debug level

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

  test('should work with environment variable overrides in production', async () => {
    process.env.NODE_ENV = 'production';
    process.env.DATABASE_POSTGRES_HOST = 'prod-db.example.com';
    process.env.DATABASE_POSTGRES_PORT = '5433';
    process.env.EOD_API_KEY = 'prod-eod-key';
    process.env.SERVICE_PORT = '8080';

    const dataServiceDir = join(TEST_ROOT, 'apps', 'data-ingestion');
    process.chdir(dataServiceDir);

    resetConfig();
    const _config = await initializeServiceConfig();

    // Environment variables should override file configs
    const dbConfig = getDatabaseConfig();
    expect(dbConfig.postgres.host).toBe('prod-db.example.com');
    expect(dbConfig.postgres.port).toBe(5433);

    const serviceConfig = getServiceConfig();
    expect(serviceConfig.port).toBe(8080);

    const eodConfig = getProviderConfig('eod');
    expect((eodConfig as any).apiKey).toBe('prod-eod-key');
  });

  test('should handle missing provider configurations gracefully', async () => {
    const dataServiceDir = join(TEST_DIR, 'apps', 'data-ingestion');
    process.chdir(dataServiceDir);

    const _config = await initializeServiceConfig();

    // Should throw for non-existent providers
    expect(() => getProviderConfig('nonexistent')).toThrow(
      'Provider configuration not found: nonexistent'
    );

    // Should work for providers that exist but might not be configured
    // (they should have defaults from schema)
    const yahooConfig = getProviderConfig('yahoo');
    expect(yahooConfig).toBeDefined();
  });

  test('should support dynamic config access patterns', async () => {
    const dataServiceDir = join(TEST_DIR, 'apps', 'data-ingestion');
    process.chdir(dataServiceDir);

    const _config = await initializeServiceConfig();

    // Test various access patterns used in real applications
    const configManager = (await import('../src/index')).getConfigManager();

    // Direct path access
    expect(configManager.getValue('app.name')).toBe('data-ingestion');
    expect(configManager.getValue('service.port')).toBe(3001);

    // Check if paths exist
    expect(configManager.has('app.name')).toBe(true);
    expect(configManager.has('nonexistent.path')).toBe(false);

    // Typed access
    const port = configManager.getValue<number>('service.port');
    expect(typeof port).toBe('number');
    expect(port).toBe(3001);
  });

  test('should handle config updates at runtime', async () => {
    const dataServiceDir = join(TEST_DIR, 'apps', 'data-ingestion');
    process.chdir(dataServiceDir);

    await initializeServiceConfig();
    const configManager = (await import('../src/index')).getConfigManager();

    // Update config at runtime (useful for testing)
    configManager.set({
      service: {
        port: 9999,
      },
    });

    const updatedConfig = getConfig();
    expect(updatedConfig.service.port).toBe(9999);

    // Other values should be preserved
    expect(updatedConfig.app.name).toBe('data-ingestion');
  });

  test('should work across multiple service initializations', async () => {
    // Simulate multiple services in the same process (like tests)

    // First service
    const dataServiceDir = join(TEST_DIR, 'apps', 'data-ingestion');
    process.chdir(dataServiceDir);

    let config = await initializeServiceConfig();
    expect(config.app.name).toBe('data-ingestion');

    // Reset and switch to another service
    resetConfig();
    const webApiDir = join(TEST_DIR, 'apps', 'web-api');
    process.chdir(webApiDir);

    config = await initializeServiceConfig();
    expect(config.app.name).toBe('web-api');

    // Each service should get its own config
    expect(config.service.port).toBe(4000); // web-api port
  });
});

const TEST_ROOT = TEST_DIR;

function setupRealUsageScenarios() {
  const scenarios = {
    root: TEST_ROOT,
    dataService: join(TEST_ROOT, 'apps', 'data-ingestion'),
    webApi: join(TEST_ROOT, 'apps', 'web-api'),
    cacheLib: join(TEST_ROOT, 'libs', 'cache'),
  };

  // Create directory structure
  Object.values(scenarios).forEach(dir => {
    mkdirSync(join(dir, 'config'), { recursive: true });
  });

  // Root config (monorepo/config/*)
  const rootConfigs = {
    development: {
      app: {
        name: 'stock-bot-monorepo',
        version: '1.0.0',
      },
      database: {
        postgres: {
          host: 'localhost',
          port: 5432,
          database: 'trading_bot',
          username: 'trading_user',
          password: 'trading_pass_dev',
        },
        questdb: {
          host: 'localhost',
          port: 9009,
          database: 'questdb',
        },
        mongodb: {
          host: 'localhost',
          port: 27017,
          database: 'stock',
        },
        dragonfly: {
          host: 'localhost',
          port: 6379,
        },
      },
      logging: {
        level: 'info',
        format: 'json',
      },
      providers: {
        yahoo: {
          name: 'Yahoo Finance',
          enabled: true,
          priority: 1,
          baseUrl: 'https://query1.finance.yahoo.com',
        },
        eod: {
          name: 'EOD Historical Data',
          enabled: false,
          priority: 2,
          apiKey: 'demo-api-key',
          baseUrl: 'https://eodhistoricaldata.com/api',
        },
      },
    },
    production: {
      logging: {
        level: 'warn',
      },
      database: {
        postgres: {
          host: 'prod-postgres.internal',
          port: 5432,
        },
      },
    },
    test: {
      logging: {
        level: 'debug',
      },
      database: {
        postgres: {
          database: 'trading_bot_test',
        },
      },
    },
  };

  Object.entries(rootConfigs).forEach(([env, config]) => {
    writeFileSync(join(scenarios.root, 'config', `${env}.json`), JSON.stringify(config, null, 2));
  });

  // Data service config
  writeFileSync(
    join(scenarios.dataService, 'config', 'development.json'),
    JSON.stringify(
      {
        app: {
          name: 'data-ingestion',
        },
        service: {
          name: 'data-ingestion',
          port: 3001,
          workers: 2,
        },
      },
      null,
      2
    )
  );

  // Web API config
  writeFileSync(
    join(scenarios.webApi, 'config', 'development.json'),
    JSON.stringify(
      {
        app: {
          name: 'web-api',
        },
        service: {
          name: 'web-api',
          port: 4000,
          cors: {
            origin: ['http://localhost:3000', 'http://localhost:4200'],
          },
        },
      },
      null,
      2
    )
  );

  // Cache lib config
  writeFileSync(
    join(scenarios.cacheLib, 'config', 'development.json'),
    JSON.stringify(
      {
        app: {
          name: 'cache-lib',
        },
        service: {
          name: 'cache-lib',
        },
      },
      null,
      2
    )
  );

  // Root .env file
  writeFileSync(
    join(scenarios.root, '.env'),
    `NODE_ENV=development
DEBUG=true
# Provider API keys
EOD_API_KEY=demo-key
WEBSHARE_API_KEY=demo-webshare-key
`
  );

  // Service-specific .env files
  writeFileSync(
    join(scenarios.dataService, '.env'),
    `SERVICE_DEBUG=true
DATA_SERVICE_RATE_LIMIT=1000
`
  );
}
