const { test } = require('tap');
const log4js = require('../../lib/log4js');
const categories = require('../../lib/categories');

test('log4js category inherit all appenders from direct parent', (batch) => {
  batch.test('should inherit appenders from direct parent', (t) => {
    const config = {
      appenders: {
        stdout1: { type: 'dummy-appender', label: 'stdout1' },
        stdout2: { type: 'dummy-appender', label: 'stdout2' },
      },
      categories: {
        default: { appenders: ['stdout1'], level: 'ERROR' },
        catA: { appenders: ['stdout1', 'stdout2'], level: 'INFO' },
        'catA.catB': { level: 'DEBUG' },
      },
    };

    log4js.configure(config);

    const childCategoryName = 'catA.catB';
    const childAppenders = categories.appendersForCategory(childCategoryName);
    const childLevel = categories.getLevelForCategory(childCategoryName);

    t.ok(childAppenders);
    t.equal(childAppenders.length, 2, 'inherited 2 appenders');
    t.ok(
      childAppenders.some((a) => a.label === 'stdout1'),
      'inherited stdout1'
    );
    t.ok(
      childAppenders.some((a) => a.label === 'stdout2'),
      'inherited stdout2'
    );
    t.equal(childLevel.levelStr, 'DEBUG', 'child level overrides parent');
    t.end();
  });

  batch.test(
    'multiple children should inherit config from shared parent',
    (t) => {
      const config = {
        appenders: {
          stdout1: { type: 'dummy-appender', label: 'stdout1' },
          stdout2: { type: 'dummy-appender', label: 'stdout2' },
        },
        categories: {
          default: { appenders: ['stdout1'], level: 'ERROR' },
          catA: { appenders: ['stdout1'], level: 'INFO' },
          'catA.catB.cat1': { level: 'DEBUG' }, // should get sdtout1, DEBUG
          'catA.catB.cat2': { appenders: ['stdout2'] }, // should get sdtout1,sdtout2, INFO
        },
      };

      log4js.configure(config);

      const child1CategoryName = 'catA.catB.cat1';
      const child1Appenders =
        categories.appendersForCategory(child1CategoryName);
      const child1Level = categories.getLevelForCategory(child1CategoryName);

      t.equal(child1Appenders.length, 1, 'inherited 1 appender');
      t.ok(
        child1Appenders.some((a) => a.label === 'stdout1'),
        'inherited stdout1'
      );
      t.equal(child1Level.levelStr, 'DEBUG', 'child level overrides parent');

      const child2CategoryName = 'catA.catB.cat2';
      const child2Appenders =
        categories.appendersForCategory(child2CategoryName);
      const child2Level = categories.getLevelForCategory(child2CategoryName);

      t.ok(child2Appenders);
      t.equal(
        child2Appenders.length,
        2,
        'inherited 1 appenders, plus its original'
      );
      t.ok(
        child2Appenders.some((a) => a.label === 'stdout1'),
        'inherited stdout1'
      );
      t.ok(
        child2Appenders.some((a) => a.label === 'stdout2'),
        'kept stdout2'
      );
      t.equal(child2Level.levelStr, 'INFO', 'inherited parent level');

      t.end();
    }
  );

  batch.test('should inherit appenders from multiple parents', (t) => {
    const config = {
      appenders: {
        stdout1: { type: 'dummy-appender', label: 'stdout1' },
        stdout2: { type: 'dummy-appender', label: 'stdout2' },
      },
      categories: {
        default: { appenders: ['stdout1'], level: 'ERROR' },
        catA: { appenders: ['stdout1'], level: 'INFO' },
        'catA.catB': { appenders: ['stdout2'], level: 'INFO' }, // should get stdout1 and stdout2
        'catA.catB.catC': { level: 'DEBUG' }, // should get stdout1 and stdout2
      },
    };

    log4js.configure(config);

    const childCategoryName = 'catA.catB.catC';
    const childAppenders = categories.appendersForCategory(childCategoryName);

    t.ok(childAppenders);
    t.equal(childAppenders.length, 2, 'inherited 2 appenders');
    t.ok(
      childAppenders.some((a) => a.label === 'stdout1'),
      'inherited stdout1'
    );
    t.ok(
      childAppenders.some((a) => a.label === 'stdout1'),
      'inherited stdout1'
    );

    const firstParentName = 'catA.catB';
    const firstParentAppenders =
      categories.appendersForCategory(firstParentName);

    t.ok(firstParentAppenders);
    t.equal(firstParentAppenders.length, 2, 'ended up with 2 appenders');
    t.ok(
      firstParentAppenders.some((a) => a.label === 'stdout1'),
      'inherited stdout1'
    );
    t.ok(
      firstParentAppenders.some((a) => a.label === 'stdout2'),
      'kept stdout2'
    );

    t.end();
  });

  batch.test(
    'should inherit appenders from deep parent with missing direct parent',
    (t) => {
      const config = {
        appenders: {
          stdout1: { type: 'dummy-appender', label: 'stdout1' },
          stdout2: { type: 'dummy-appender', label: 'stdout2' },
        },
        categories: {
          default: { appenders: ['stdout1'], level: 'ERROR' },
          catA: { appenders: ['stdout1'], level: 'INFO' },
          // no catA.catB, but should get created, with stdout1
          'catA.catB.catC': { level: 'DEBUG' }, // should get stdout1
        },
      };

      log4js.configure(config);

      const childCategoryName = 'catA.catB.catC';
      const childAppenders = categories.appendersForCategory(childCategoryName);

      t.ok(childAppenders);
      t.equal(childAppenders.length, 1, 'inherited 1 appenders');
      t.ok(
        childAppenders.some((a) => a.label === 'stdout1'),
        'inherited stdout1'
      );

      const firstParentCategoryName = 'catA.catB';
      const firstParentAppenders = categories.appendersForCategory(
        firstParentCategoryName
      );

      t.ok(firstParentAppenders, 'catA.catB got created implicitily');
      t.equal(
        firstParentAppenders.length,
        1,
        'created with 1 inherited appender'
      );
      t.ok(
        firstParentAppenders.some((a) => a.label === 'stdout1'),
        'inherited stdout1'
      );

      t.end();
    }
  );

  batch.test('should deal gracefully with missing parent', (t) => {
    const config = {
      appenders: {
        stdout1: { type: 'dummy-appender', label: 'stdout1' },
        stdout2: { type: 'dummy-appender', label: 'stdout2' },
      },
      categories: {
        default: { appenders: ['stdout1'], level: 'ERROR' },
        // no catA nor catA.catB, but should get created, with default values
        'catA.catB.catC': { appenders: ['stdout2'], level: 'DEBUG' }, // should get stdout2, DEBUG
      },
    };

    log4js.configure(config);

    const childCategoryName = 'catA.catB.catC';
    const childAppenders = categories.appendersForCategory(childCategoryName);

    t.ok(childAppenders);
    t.equal(childAppenders.length, 1);
    t.ok(childAppenders.some((a) => a.label === 'stdout2'));

    t.end();
  });

  batch.test(
    'should not get duplicate appenders if parent has the same one',
    (t) => {
      const config = {
        appenders: {
          stdout1: { type: 'dummy-appender', label: 'stdout1' },
          stdout2: { type: 'dummy-appender', label: 'stdout2' },
        },
        categories: {
          default: { appenders: ['stdout1'], level: 'ERROR' },
          catA: { appenders: ['stdout1', 'stdout2'], level: 'INFO' },
          'catA.catB': { appenders: ['stdout1'], level: 'DEBUG' },
        },
      };

      log4js.configure(config);

      const childCategoryName = 'catA.catB';
      const childAppenders = categories.appendersForCategory(childCategoryName);

      t.ok(childAppenders);
      t.equal(childAppenders.length, 2, 'inherited 1 appender');
      t.ok(
        childAppenders.some((a) => a.label === 'stdout1'),
        'still have stdout1'
      );
      t.ok(
        childAppenders.some((a) => a.label === 'stdout2'),
        'inherited stdout2'
      );
      t.end();
    }
  );

  batch.test('inherit:falses should disable inheritance', (t) => {
    const config = {
      appenders: {
        stdout1: { type: 'dummy-appender', label: 'stdout1' },
        stdout2: { type: 'dummy-appender', label: 'stdout2' },
      },
      categories: {
        default: { appenders: ['stdout1'], level: 'ERROR' },
        catA: { appenders: ['stdout1'], level: 'INFO' },
        'catA.catB': { appenders: ['stdout2'], level: 'INFO', inherit: false }, // should not inherit from catA
      },
    };

    log4js.configure(config);

    const childCategoryName = 'catA.catB';
    const childAppenders = categories.appendersForCategory(childCategoryName);

    t.ok(childAppenders);
    t.equal(childAppenders.length, 1, 'inherited no appender');
    t.ok(
      childAppenders.some((a) => a.label === 'stdout2'),
      'kept stdout2'
    );

    t.end();
  });

  batch.test(
    'inheritance should stop if direct parent has inherit off',
    (t) => {
      const config = {
        appenders: {
          stdout1: { type: 'dummy-appender', label: 'stdout1' },
          stdout2: { type: 'dummy-appender', label: 'stdout2' },
        },
        categories: {
          default: { appenders: ['stdout1'], level: 'ERROR' },
          catA: { appenders: ['stdout1'], level: 'INFO' },
          'catA.catB': {
            appenders: ['stdout2'],
            level: 'INFO',
            inherit: false,
          }, // should not inherit from catA
          'catA.catB.catC': { level: 'DEBUG' }, // should inherit from catB only
        },
      };

      log4js.configure(config);

      const childCategoryName = 'catA.catB.catC';
      const childAppenders = categories.appendersForCategory(childCategoryName);

      t.ok(childAppenders);
      t.equal(childAppenders.length, 1, 'inherited 1 appender');
      t.ok(
        childAppenders.some((a) => a.label === 'stdout2'),
        'inherited stdout2'
      );

      const firstParentCategoryName = 'catA.catB';
      const firstParentAppenders = categories.appendersForCategory(
        firstParentCategoryName
      );

      t.ok(firstParentAppenders);
      t.equal(firstParentAppenders.length, 1, 'did not inherit new appenders');
      t.ok(
        firstParentAppenders.some((a) => a.label === 'stdout2'),
        'kept stdout2'
      );

      t.end();
    }
  );

  batch.test('should inherit level when it is missing', (t) => {
    const config = {
      appenders: {
        stdout1: { type: 'dummy-appender', label: 'stdout1' },
        stdout2: { type: 'dummy-appender', label: 'stdout2' },
      },
      categories: {
        default: { appenders: ['stdout1'], level: 'ERROR' },
        catA: { appenders: ['stdout1'], level: 'INFO' },
        // no catA.catB, but should get created, with stdout1, level INFO
        'catA.catB.catC': {}, // should get stdout1, level INFO
      },
    };

    log4js.configure(config);

    const childCategoryName = 'catA.catB.catC';
    const childLevel = categories.getLevelForCategory(childCategoryName);

    t.equal(childLevel.levelStr, 'INFO', 'inherited level');

    const firstParentCategoryName = 'catA.catB';
    const firstParentLevel = categories.getLevelForCategory(
      firstParentCategoryName
    );

    t.equal(
      firstParentLevel.levelStr,
      'INFO',
      'generate parent inherited level from base'
    );

    t.end();
  });

  batch.end();
});
