import { SpectatorService, createServiceFactory } from '@ngneat/spectator';
import { LogLevel } from './log-level';
import { Logger } from './logger.service';
import { LOG_PROVIDER } from './tokens';
import { LOG_LEVEL } from '.';

describe('LoggerService', () => {
  let spectator: SpectatorService<Logger>;
  let logProviderMock1 = jasmine.createSpyObj('LogProvider', ['log']);
  let logProviderMock2 = jasmine.createSpyObj('LogProvider', ['log']);

  const createService = createServiceFactory({
    service: Logger,
    mocks: [Logger],
    providers: [
      {
        provide: LOG_PROVIDER,
        useValue: logProviderMock1,
        multi: true,
      },
      {
        provide: LOG_PROVIDER,
        useValue: logProviderMock2,
        multi: true,
      },
      {
        provide: LOG_LEVEL,
        useValue: LogLevel.DEBUG,
      },
    ],
  });

  beforeEach(() => {
    spectator = createService();
  });

  it('should create', () => {
    expect(spectator.service).toBeTruthy();
  });

  const testData: {
    serviceLogLevel: LogLevel;
    callLogLevel: LogLevel;
    shoudCall: boolean;
  }[] = [
    { serviceLogLevel: LogLevel.DEBUG, callLogLevel: LogLevel.DEBUG, shoudCall: true },
    { serviceLogLevel: LogLevel.DEBUG, callLogLevel: LogLevel.INFO, shoudCall: true },
    { serviceLogLevel: LogLevel.DEBUG, callLogLevel: LogLevel.WARN, shoudCall: true },
    { serviceLogLevel: LogLevel.DEBUG, callLogLevel: LogLevel.ERROR, shoudCall: true },

    { serviceLogLevel: LogLevel.INFO, callLogLevel: LogLevel.DEBUG, shoudCall: false },
    { serviceLogLevel: LogLevel.INFO, callLogLevel: LogLevel.INFO, shoudCall: true },
    { serviceLogLevel: LogLevel.INFO, callLogLevel: LogLevel.WARN, shoudCall: true },
    { serviceLogLevel: LogLevel.INFO, callLogLevel: LogLevel.ERROR, shoudCall: true },

    { serviceLogLevel: LogLevel.WARN, callLogLevel: LogLevel.DEBUG, shoudCall: false },
    { serviceLogLevel: LogLevel.WARN, callLogLevel: LogLevel.INFO, shoudCall: false },
    { serviceLogLevel: LogLevel.WARN, callLogLevel: LogLevel.WARN, shoudCall: true },
    { serviceLogLevel: LogLevel.WARN, callLogLevel: LogLevel.ERROR, shoudCall: true },

    { serviceLogLevel: LogLevel.ERROR, callLogLevel: LogLevel.DEBUG, shoudCall: false },
    { serviceLogLevel: LogLevel.ERROR, callLogLevel: LogLevel.INFO, shoudCall: false },
    { serviceLogLevel: LogLevel.ERROR, callLogLevel: LogLevel.WARN, shoudCall: false },
    { serviceLogLevel: LogLevel.ERROR, callLogLevel: LogLevel.ERROR, shoudCall: true },

    { serviceLogLevel: LogLevel.OFF, callLogLevel: LogLevel.DEBUG, shoudCall: false },
    { serviceLogLevel: LogLevel.OFF, callLogLevel: LogLevel.INFO, shoudCall: false },
    { serviceLogLevel: LogLevel.OFF, callLogLevel: LogLevel.WARN, shoudCall: false },
    { serviceLogLevel: LogLevel.OFF, callLogLevel: LogLevel.ERROR, shoudCall: false },
  ];

  describe('log()', () => {
    beforeEach(() => {
      logProviderMock1.log.calls.reset();
      logProviderMock2.log.calls.reset();
    });

    for (const test of testData) {
      if (test.shoudCall) {
        it(`should call logProvider if logLevel is ${LogLevel[test.callLogLevel]} and serviceLogLevel is ${
          LogLevel[test.serviceLogLevel]
        }`, () => {
          spectator.service['_logLevel'] = test.serviceLogLevel;
          spectator.service.log(test.callLogLevel, 'test');
          expect(logProviderMock1.log).toHaveBeenCalledTimes(1);
          expect(logProviderMock2.log).toHaveBeenCalledTimes(1);
        });
      } else {
        it(`should not call logProvider if logLevel is ${LogLevel[test.callLogLevel]} and serviceLogLevel is ${
          LogLevel[test.serviceLogLevel]
        }`, () => {
          spectator.service['_logLevel'] = test.serviceLogLevel;
          spectator.service.log(test.callLogLevel, 'test');
          expect(logProviderMock1.log).not.toHaveBeenCalled();
          expect(logProviderMock2.log).not.toHaveBeenCalled();
        });
      }
    }
  });

  describe('warn()', () => {
    it('should call log and logLevel should be WARN', () => {
      spyOn(spectator.service, 'log');

      spectator.service.warn('test', 'data');

      expect(spectator.service.log).toHaveBeenCalledWith(LogLevel.WARN, 'test', 'data');
    });
  });

  describe('info()', () => {
    it('should call log and logLevel should be INFO', () => {
      spyOn(spectator.service, 'log');

      spectator.service.info('test', 'data');

      expect(spectator.service.log).toHaveBeenCalledWith(LogLevel.INFO, 'test', 'data');
    });
  });

  describe('debug()', () => {
    it('should call log and logLevel should be DEBUG', () => {
      spyOn(spectator.service, 'log');

      spectator.service.debug('test', 'data');

      expect(spectator.service.log).toHaveBeenCalledWith(LogLevel.DEBUG, 'test', 'data');
    });
  });

  describe('error()', () => {
    it('should call log and logLevel should be ERROR', () => {
      spyOn(spectator.service, 'log');

      spectator.service.error('test', 'data');

      expect(spectator.service.log).toHaveBeenCalledWith(LogLevel.ERROR, 'test', 'data');
    });
  });
});
