'use strict';

const test = require('ava');
const UnleashClientMetrics = require('./index');
const moment = require('moment');
const lolex = require('lolex');

const { EventEmitter } = require('events');

const appName = 'appName';
const instanceId = 'instanceId';

test('should work without state', t => {
    const store = new EventEmitter();
    const metrics = new UnleashClientMetrics(store);

    t.truthy(metrics.getAppsWithToggles());
    t.truthy(metrics.getTogglesMetrics());

    metrics.destroy();
});

test.cb('data should expire', t => {
    const clock = lolex.install();

    const store = new EventEmitter();
    const metrics = new UnleashClientMetrics(store);

    metrics.addPayload({
        appName,
        instanceId,
        bucket: {
            start: Date.now() - 2000,
            stop: Date.now() - 1000,
            toggles: {
                toggleX: {
                    yes: 123,
                    no: 0,
                },
            },
        },
    });

    let lastHourExpires = 0;
    metrics.lastHourList.on('expire', () => {
        lastHourExpires++;
    });

    let lastMinExpires = 0;
    metrics.lastMinuteList.on('expire', () => {
        lastMinExpires++;
    });

    clock.tick(60 * 1000);
    t.true(lastMinExpires === 1);
    t.true(lastHourExpires === 0);

    clock.tick(60 * 60 * 1000);
    t.true(lastMinExpires === 1);
    t.true(lastHourExpires === 1);

    clock.uninstall();
    t.end();
});

test('should listen to metrics from store', t => {
    const store = new EventEmitter();
    const metrics = new UnleashClientMetrics(store);
    store.emit('metrics', {
        appName,
        instanceId,
        bucket: {
            start: new Date(),
            stop: new Date(),
            toggles: {
                toggleX: {
                    yes: 123,
                    no: 0,
                },
            },
        },
    });

    t.truthy(metrics.apps[appName].count === 123);
    t.truthy(metrics.globalCount === 123);

    t.deepEqual(metrics.getTogglesMetrics().lastHour.toggleX, {
        yes: 123,
        no: 0,
    });
    t.deepEqual(metrics.getTogglesMetrics().lastMinute.toggleX, {
        yes: 123,
        no: 0,
    });

    metrics.addPayload({
        appName,
        instanceId,
        bucket: {
            start: new Date(),
            stop: new Date(),
            toggles: {
                toggleX: {
                    yes: 10,
                    no: 10,
                },
            },
        },
    });

    t.truthy(metrics.globalCount === 143);
    t.deepEqual(metrics.getTogglesMetrics().lastHour.toggleX, {
        yes: 133,
        no: 10,
    });
    t.deepEqual(metrics.getTogglesMetrics().lastMinute.toggleX, {
        yes: 133,
        no: 10,
    });

    metrics.destroy();
});

test('should build up list of seend toggles when new metrics arrives', t => {
    const store = new EventEmitter();
    const metrics = new UnleashClientMetrics(store);
    store.emit('metrics', {
        appName,
        instanceId,
        bucket: {
            start: new Date(),
            stop: new Date(),
            toggles: {
                toggleX: {
                    yes: 123,
                    no: 0,
                },
                toggleY: {
                    yes: 50,
                    no: 50,
                },
            },
        },
    });

    const appToggles = metrics.getAppsWithToggles();
    const togglesForApp = metrics.getSeenTogglesByAppName(appName);

    t.truthy(appToggles.length === 1);
    t.truthy(appToggles[0].seenToggles.length === 2);
    t.truthy(appToggles[0].seenToggles.includes('toggleX'));
    t.truthy(appToggles[0].seenToggles.includes('toggleY'));

    t.truthy(togglesForApp.length === 2);
    t.truthy(togglesForApp.includes('toggleX'));
    t.truthy(togglesForApp.includes('toggleY'));
    metrics.destroy();
});

test('should handle a lot of toggles', t => {
    const store = new EventEmitter();
    const metrics = new UnleashClientMetrics(store);

    const toggleCounts = {};
    for (let i = 0; i < 100; i++) {
        toggleCounts[`toggle${i}`] = { yes: i, no: i };
    }

    store.emit('metrics', {
        appName,
        instanceId,
        bucket: {
            start: new Date(),
            stop: new Date(),
            toggles: toggleCounts,
        },
    });

    const seenToggles = metrics.getSeenTogglesByAppName(appName);

    t.truthy(seenToggles.length === 100);
    metrics.destroy();
});

test('should have correct values for lastMinute', t => {
    const clock = lolex.install();

    const store = new EventEmitter();
    const metrics = new UnleashClientMetrics(store);

    const now = new Date();
    const input = [
        {
            start: moment(now).subtract(1, 'hour'),
            stop: moment(now).subtract(59, 'minutes'),
            toggles: {
                toggle: { yes: 10, no: 10 },
            },
        },
        {
            start: moment(now).subtract(30, 'minutes'),
            stop: moment(now).subtract(29, 'minutes'),
            toggles: {
                toggle: { yes: 10, no: 10 },
            },
        },
        {
            start: moment(now).subtract(2, 'minutes'),
            stop: moment(now).subtract(1, 'minutes'),
            toggles: {
                toggle: { yes: 10, no: 10 },
            },
        },
        {
            start: moment(now).subtract(2, 'minutes'),
            stop: moment(now).subtract(59, 'seconds'),
            toggles: {
                toggle: { yes: 10, no: 10 },
            },
        },
        {
            start: moment(now),
            stop: moment(now).subtract(30, 'seconds'),
            toggles: {
                toggle: { yes: 10, no: 10 },
            },
        },
    ];

    input.forEach(bucket => {
        store.emit('metrics', {
            appName,
            instanceId,
            bucket,
        });
    });

    const seenToggles = metrics.getSeenTogglesByAppName(appName);
    t.truthy(seenToggles.length === 1);

    // metrics.se
    let c = metrics.getTogglesMetrics();
    t.deepEqual(c.lastMinute.toggle, { yes: 20, no: 20 });

    clock.tick(10 * 1000);
    c = metrics.getTogglesMetrics();
    t.deepEqual(c.lastMinute.toggle, { yes: 10, no: 10 });

    clock.tick(20 * 1000);
    c = metrics.getTogglesMetrics();
    t.deepEqual(c.lastMinute.toggle, { yes: 0, no: 0 });

    metrics.destroy();
    clock.uninstall();
});

test('should have correct values for lastHour', t => {
    const clock = lolex.install();

    const store = new EventEmitter();
    const metrics = new UnleashClientMetrics(store);

    const now = new Date();
    const input = [
        {
            start: moment(now).subtract(1, 'hour'),
            stop: moment(now).subtract(59, 'minutes'),
            toggles: {
                toggle: { yes: 10, no: 10 },
            },
        },
        {
            start: moment(now).subtract(30, 'minutes'),
            stop: moment(now).subtract(29, 'minutes'),
            toggles: {
                toggle: { yes: 10, no: 10 },
            },
        },
        {
            start: moment(now).subtract(15, 'minutes'),
            stop: moment(now).subtract(14, 'minutes'),
            toggles: {
                toggle: { yes: 10, no: 10 },
            },
        },
        {
            start: moment(now).add(59, 'minutes'),
            stop: moment(now).add(1, 'hour'),
            toggles: {
                toggle: { yes: 11, no: 11 },
            },
        },
    ];

    input.forEach(bucket => {
        store.emit('metrics', {
            appName,
            instanceId,
            bucket,
        });
    });

    const seenToggles = metrics.getSeenTogglesByAppName(appName);

    t.truthy(seenToggles.length === 1);

    // metrics.se
    let c = metrics.getTogglesMetrics();
    t.deepEqual(c.lastHour.toggle, { yes: 41, no: 41 });

    clock.tick(10 * 1000);
    c = metrics.getTogglesMetrics();
    t.deepEqual(c.lastHour.toggle, { yes: 41, no: 41 });

    // at 30
    clock.tick(30 * 60 * 1000);
    c = metrics.getTogglesMetrics();
    t.deepEqual(c.lastHour.toggle, { yes: 31, no: 31 });

    // at 45
    clock.tick(15 * 60 * 1000);
    c = metrics.getTogglesMetrics();
    t.deepEqual(c.lastHour.toggle, { yes: 21, no: 21 });

    // at 1:15
    clock.tick(30 * 60 * 1000);
    c = metrics.getTogglesMetrics();
    t.deepEqual(c.lastHour.toggle, { yes: 11, no: 11 });

    // at 2:00
    clock.tick(45 * 60 * 1000);
    c = metrics.getTogglesMetrics();
    t.deepEqual(c.lastHour.toggle, { yes: 0, no: 0 });

    metrics.destroy();
    clock.uninstall();
});

test('should not fail when toggle metrics is missing yes/no field', t => {
    const store = new EventEmitter();
    const metrics = new UnleashClientMetrics(store);
    store.emit('metrics', {
        appName,
        instanceId,
        bucket: {
            start: new Date(),
            stop: new Date(),
            toggles: {
                toggleX: {
                    yes: 123,
                    no: 0,
                },
            },
        },
    });

    metrics.addPayload({
        appName,
        instanceId,
        bucket: {
            start: new Date(),
            stop: new Date(),
            toggles: {
                toggleX: {
                    blue: 10,
                    green: 10,
                },
            },
        },
    });

    t.is(metrics.globalCount, 123);
    t.deepEqual(metrics.getTogglesMetrics().lastMinute.toggleX, {
        yes: 123,
        no: 0,
    });

    metrics.destroy();
});