import { describe, expect, it } from 'vitest';
import { ConfigError, loadConfig } from '../config.js';

const BASE = { DATABASE_URL: 'postgres://u:p@localhost:5432/db' };

function problemsOf(fn: () => unknown): ReadonlyArray<string> {
  try {
    fn();
  } catch (error) {
    if (error instanceof ConfigError) {
      return error.problems;
    }
    throw error;
  }
  throw new Error('expected ConfigError was not thrown');
}

describe('loadConfig', () => {
  it('returns typed config for a valid full env', () => {
    const config = loadConfig({
      ...BASE,
      PORT: '8080',
      HOST: '127.0.0.1',
      LOG_LEVEL: 'debug',
      NODE_ENV: 'production',
      REDIS_URL: 'redis://localhost:6379',
      FLAG_NEW_CHECKOUT: 'true',
      FLAG_BETA_SEARCH: 'false',
    });

    expect(config.port).toBe(8080);
    expect(config.host).toBe('127.0.0.1');
    expect(config.logLevel).toBe('debug');
    expect(config.nodeEnv).toBe('production');
    expect(config.databaseUrl).toBe(BASE.DATABASE_URL);
    expect(config.redisUrl).toBe('redis://localhost:6379');
    expect(config.flags).toEqual({ new_checkout: true, beta_search: false });
  });

  it('applies defaults when optional vars are absent', () => {
    const config = loadConfig(BASE);
    expect(config.port).toBe(3000);
    expect(config.host).toBe('0.0.0.0');
    expect(config.logLevel).toBe('info');
    expect(config.nodeEnv).toBe('development');
    expect(config.redisUrl).toBeUndefined();
    expect(config.flags).toEqual({});
  });

  it('fails fast naming DATABASE_URL when missing', () => {
    expect(() => loadConfig({})).toThrowError(ConfigError);
    expect(problemsOf(() => loadConfig({}))).toContain('DATABASE_URL is required');
  });

  it('rejects invalid PORT values naming the var', () => {
    for (const bad of ['not-a-number', '0', '70000', '3.5']) {
      expect(() => loadConfig({ ...BASE, PORT: bad })).toThrowError(ConfigError);
      expect(problemsOf(() => loadConfig({ ...BASE, PORT: bad })).join(' ')).toContain('PORT');
    }
  });

  it('rejects invalid NODE_ENV', () => {
    expect(problemsOf(() => loadConfig({ ...BASE, NODE_ENV: 'staging' })).join(' ')).toContain(
      'NODE_ENV',
    );
  });

  it('defaults cookieSecure to true and parses COOKIE_SECURE', () => {
    expect(loadConfig(BASE).cookieSecure).toBe(true);
    expect(loadConfig({ ...BASE, COOKIE_SECURE: 'false' }).cookieSecure).toBe(false);
    expect(loadConfig({ ...BASE, COOKIE_SECURE: 'true' }).cookieSecure).toBe(true);
    expect(problemsOf(() => loadConfig({ ...BASE, COOKIE_SECURE: 'yes' })).join(' ')).toContain(
      'COOKIE_SECURE',
    );
  });

  it('rejects FLAG_* values that are not true/false', () => {
    expect(problemsOf(() => loadConfig({ ...BASE, FLAG_BROKEN: 'yes' })).join(' ')).toContain(
      'FLAG_BROKEN',
    );
  });

  it('reports every problem in a single ConfigError', () => {
    const problems = problemsOf(() =>
      loadConfig({ PORT: 'nope', NODE_ENV: 'staging', FLAG_BAD: 'maybe' }),
    );
    expect(problems.some((p) => p.includes('DATABASE_URL'))).toBe(true);
    expect(problems.some((p) => p.includes('PORT'))).toBe(true);
    expect(problems.some((p) => p.includes('NODE_ENV'))).toBe(true);
    expect(problems.some((p) => p.includes('FLAG_BAD'))).toBe(true);
  });

  it('never echoes secret values in error messages', () => {
    let message = '';
    try {
      loadConfig({ DATABASE_URL: 'postgres://u:supersecret@h/db', PORT: 'nope' });
    } catch (error) {
      message = (error as Error).message;
    }
    expect(message).not.toContain('supersecret');
    expect(message).toContain('PORT');
  });
});
