import { describe, it, expect } from '#test';
import { type Config, validateConfig, resolveConfig } from './config.ts';
import { AssertionError } from './util/asserts.ts';


const suite = describe('config');
const validateSuite = describe(suite, 'validateConfig()');
const resolveSuite = describe(suite, 'resolveConfig()');

it(validateSuite, 'should validate that object must be used', () => {
  const err = new AssertionError('API config must be an "object"');

  expect(() => validateConfig(0)).toThrow(err);
  expect(() => validateConfig(15)).toThrow(err);
  expect(() => validateConfig(true)).toThrow(err);
  expect(() => validateConfig(false)).toThrow(err);
  expect(() => validateConfig(null)).toThrow(err);
  expect(() => validateConfig(undefined)).toThrow(err);
});

it(validateSuite, 'should validate that clusterId is a non-empty string', () => {
  const err = new AssertionError('Property "clusterId" must be a non-empty string');

  expect(() => validateConfig({})).toThrow(err);
  expect(() => validateConfig({ clusterId: undefined })).toThrow(err);
  expect(() => validateConfig({ clusterId: [1, 2, 3] })).toThrow(err);
  expect(() => validateConfig({ clusterId: '' })).toThrow(err);
});

it(validateSuite, 'should validate that market is a non-empty string', () => {
  const err = new AssertionError('Property "market" must be a non-empty string');

  expect(() => validateConfig({ clusterId: 'w000000' })).toThrow(err);
  expect(() => validateConfig({ clusterId: 'w000000', market: 0 })).toThrow(err);
  expect(() => validateConfig({ clusterId: 'w000000', market: 1337 })).toThrow(err);
  expect(() => validateConfig({ clusterId: 'w000000', market: true })).toThrow(err);
  expect(() => validateConfig({ clusterId: 'w000000', market: false })).toThrow(err);
  expect(() => validateConfig({ clusterId: 'w000000', market: null })).toThrow(err);
  expect(() => validateConfig({ clusterId: 'w000000', market: undefined })).toThrow(err);
  expect(() => validateConfig({ clusterId: 'w000000', market: '' })).toThrow(err);
  expect(() => validateConfig({ clusterId: 'w000000', market: {} })).toThrow(err);
  expect(() => validateConfig({ clusterId: 'w000000', market: ['a', 'b'] })).toThrow(err);
});

it.ignore(validateSuite, 'should validate that session is a function', () => {
  // TODO(csv): implement this
});

it(validateSuite, 'should validate that locale is a non-empty string', () => {
  const err = new AssertionError('Property "locale" must be a string and valid locale, e.g. "en-US".');
  const base = { clusterId: 'w000000', market: 'a', session: () => {} };

  expect(() => validateConfig({ ...base })).toThrow(err);
  expect(() => validateConfig({ ...base, locale: 0 })).toThrow(err);
  expect(() => validateConfig({ ...base, locale: 4242 })).toThrow(err);
  expect(() => validateConfig({ ...base, locale: true })).toThrow(err);
  expect(() => validateConfig({ ...base, locale: false })).toThrow(err);
  expect(() => validateConfig({ ...base, locale: null })).toThrow(err);
  expect(() => validateConfig({ ...base, locale: undefined })).toThrow(err);
  expect(() => validateConfig({ ...base, locale: '' })).toThrow(err);
  expect(() => validateConfig({ ...base, locale: {} })).toThrow(err);
  expect(() => validateConfig({ ...base, locale: ['jane', 'john'] })).toThrow(err);
});

it(validateSuite, 'should validate that touchpoint is a valid enum value', () => {
  const err = new AssertionError('Property "touchpoint" must be one of: desktop, mobile');
  const base = { clusterId: 'w000000', market: 'a', locale: 'a', session: () => ({ customerKey: '', sessionKey: '' }) };

  expect(() => validateConfig({ ...base })).toThrow(err);
  expect(() => validateConfig({ ...base, touchpoint: 0 })).toThrow(err);
  expect(() => validateConfig({ ...base, touchpoint: 9001 })).toThrow(err);
  expect(() => validateConfig({ ...base, touchpoint: true })).toThrow(err);
  expect(() => validateConfig({ ...base, touchpoint: false })).toThrow(err);
  expect(() => validateConfig({ ...base, touchpoint: null })).toThrow(err);
  expect(() => validateConfig({ ...base, touchpoint: undefined })).toThrow(err);
  expect(() => validateConfig({ ...base, touchpoint: '' })).toThrow(err);
  expect(() => validateConfig({ ...base, touchpoint: ['a', 'b'] })).toThrow(err);
  expect(() => validateConfig({ ...base, touchpoint: 'foo' })).toThrow(err);
  expect(() => validateConfig({ ...base, touchpoint: 'desktopp' })).toThrow(err);
});

it(validateSuite, 'should not throw errors on valid configurations', () => {
  const session = () => ({ customerKey: '', sessionKey: '' });
  validateConfig({ clusterId: 'wabcdef0', market: 'se', locale: 'sv-SE', touchpoint: 'mobile', session });
  validateConfig({ clusterId: 'wABCDEF012', market: 'gb', locale: 'en-GB', touchpoint: 'desktop', session });
  validateConfig({ clusterId: 'https://example.com/', market: 'uk', locale: 'en-GB', touchpoint: 'desktop', session });
  validateConfig({ clusterId: 'http://localhost:12345', market: 'uk', locale: 'en-GB', touchpoint: 'desktop', session });
});

it(validateSuite, 'should have correct properties for the `Config` interface', () => {
  ({
    clusterId: 'w123abc',
    market: 'se',
    locale: 'en-US',
    touchpoint: 'desktop',
    session: () => ({ customerKey: '', sessionKey: '' })
  } satisfies Config);
});


it(resolveSuite, 'should return a correct resolved config', () => {
  const constantSession = () => ({ customerKey: '', sessionKey: '' });
  const config = resolveConfig({ clusterId: 'w123ABC', market: 'se', locale: 'sv-SE', touchpoint: 'desktop', session: constantSession });
  const { market, locale, touchpoint, session, clusterUrl, ...rest } = config;

  expect(market).toBe('se');
  expect(locale).toBe('sv-SE');
  expect(touchpoint).toBe('desktop');
  expect(session).toBe(constantSession);
  expect(clusterUrl).toBe('https://w123abc.api.esales.apptus.cloud/');
  expect(rest).toEqual({});
});

it(resolveSuite, 'should allow case insensitive `clusterId`', () => {
  const constantSession = () => ({ customerKey: '', sessionKey: '' });
  const config = resolveConfig({ clusterId: 'wABcdEF12', market: 'se', locale: 'sv-SE', touchpoint: 'desktop', session: constantSession });
  const { market, locale, touchpoint, session, clusterUrl, ...rest } = config;

  expect(market).toBe('se');
  expect(locale).toBe('sv-SE');
  expect(touchpoint).toBe('desktop');
  expect(session).toBe(constantSession);
  expect(clusterUrl).toBe('https://wabcdef12.api.esales.apptus.cloud/');
  expect(rest).toEqual({});
});

it(resolveSuite, 'should return a correct processed config when using URL for `clusterId`', () => {
  const constantSession = () => ({ customerKey: '', sessionKey: '' });
  const config = resolveConfig({ clusterId: 'https://localhost:3000/', market: 'de', locale: 'no-NO', touchpoint: 'mobile', session: constantSession });
  const { market, locale, touchpoint, session, clusterUrl, ...rest } = config;

  expect(market).toBe('de');
  expect(locale).toBe('no-NO');
  expect(touchpoint).toBe('mobile');
  expect(session).toBe(constantSession);
  expect(clusterUrl).toBe('https://localhost:3000/');
  expect(rest).toEqual({});
});

it(resolveSuite, 'should automatically normalize trailing slash for `clusterUrl`', () => {
  const constantSession = () => ({ customerKey: '', sessionKey: '' });
  const config = resolveConfig({ clusterId: 'https://localhost:12345', market: 'nada', locale: 'zip', touchpoint: 'mobile', session: constantSession });
  expect(config.clusterUrl).toBe('https://localhost:12345/');
});
