import type { ConfigService } from '@nestjs/config';
import {
  getRequiredBoolean,
  getRequiredEnum,
  getRequiredNumber,
  getRequiredString,
} from './config.util';
import { configFixtures } from '@test-fixtures/common/config.fixture';

const makeConfig = (value: unknown): ConfigService =>
  ({
    getOrThrow: jest.fn().mockReturnValue(value),
  }) as unknown as ConfigService;

describe('config utils', () => {
  describe('getRequiredNumber', () => {
    it('parses numbers from strings', () => {
      const config = makeConfig(configFixtures.number.stringValue);
      expect(getRequiredNumber(config, configFixtures.number.key)).toBe(
        configFixtures.number.expectedStringParsed,
      );
    });

    it('returns numeric values directly', () => {
      const config = makeConfig(configFixtures.number.numberValue);
      expect(getRequiredNumber(config, configFixtures.number.key)).toBe(
        configFixtures.number.expectedNumber,
      );
    });

    it('throws on non-numeric input', () => {
      const config = makeConfig(configFixtures.number.invalidValue);
      expect(() =>
        getRequiredNumber(config, configFixtures.number.key),
      ).toThrow(configFixtures.number.invalidMessage);
    });
  });

  describe('getRequiredBoolean', () => {
    it('returns boolean values directly', () => {
      const config = makeConfig(configFixtures.boolean.trueValue);
      expect(getRequiredBoolean(config, configFixtures.boolean.key)).toBe(true);
    });

    it('parses boolean strings', () => {
      const configTrue = makeConfig(configFixtures.boolean.trueString);
      const configFalse = makeConfig(configFixtures.boolean.falseString);
      expect(getRequiredBoolean(configTrue, configFixtures.boolean.key)).toBe(
        true,
      );
      expect(getRequiredBoolean(configFalse, configFixtures.boolean.key)).toBe(
        false,
      );
    });

    it('throws on invalid boolean strings', () => {
      const config = makeConfig(configFixtures.boolean.invalidValue);
      expect(() =>
        getRequiredBoolean(config, configFixtures.boolean.key),
      ).toThrow(configFixtures.boolean.invalidMessage);
    });
  });

  describe('getRequiredString', () => {
    it('trims and returns strings', () => {
      const config = makeConfig(configFixtures.string.paddedValue);
      expect(getRequiredString(config, configFixtures.string.key)).toBe(
        configFixtures.string.expectedValue,
      );
    });

    it('throws on empty strings', () => {
      const config = makeConfig(configFixtures.string.emptyValue);
      expect(() =>
        getRequiredString(config, configFixtures.string.key),
      ).toThrow(configFixtures.string.invalidMessage);
    });

    it('throws when value is not a string', () => {
      const config = makeConfig(configFixtures.string.invalidValue);
      expect(() =>
        getRequiredString(config, configFixtures.string.key),
      ).toThrow(configFixtures.string.invalidMessage);
    });
  });

  describe('getRequiredEnum', () => {
    it('accepts allowed values', () => {
      const config = makeConfig(configFixtures.enum.okValue);
      expect(
        getRequiredEnum(
          config,
          configFixtures.enum.key,
          configFixtures.enum.allowed,
        ),
      ).toBe(configFixtures.enum.okValue);
    });

    it('throws when value is not allowed', () => {
      const config = makeConfig(configFixtures.enum.invalidValue);
      expect(() =>
        getRequiredEnum(
          config,
          configFixtures.enum.key,
          configFixtures.enum.allowed,
        ),
      ).toThrow(configFixtures.enum.invalidMessage);
    });
  });
});
