const assert = require('node:assert/strict');
const ObjectID = require('bson-objectid').default;
const InMemoryMilestoneRepository = require('../../../../../core/server/services/milestones/in-memory-milestone-repository');
const Milestone = require('../../../../../core/server/services/milestones/milestone');
const DomainEvents = require('@tryghost/domain-events');
const sinon = require('sinon');

describe('InMemoryMilestoneRepository', function () {
    let repository;
    let domainEventsSpy;

    beforeAll(async function () {
        const resourceId = new ObjectID();
        domainEventsSpy = sinon.spy(DomainEvents, 'dispatch');
        repository = new InMemoryMilestoneRepository({DomainEvents});
        const milestoneCreatePromises = [];

        const validInputs = [
            {
                type: 'arr',
                value: 20000,
                createdAt: '2023-01-01T00:00:00Z',
                id: resourceId // duplicate id
            },
            {
                type: 'arr',
                value: 1000,
                createdAt: '2023-01-01T00:00:00Z',
                currency: 'gbp'
            },
            {
                type: 'arr',
                value: 2000,
                createdAt: '2023-01-30T00:00:00Z',
                currency: 'gbp'
            },
            {
                type: 'arr',
                value: 50000,
                createdAt: '2023-02-01T01:00:00Z',
                emailSentAt: '2023-02-01T01:00:00Z',
                currency: 'usd'
            },
            {
                type: 'arr',
                value: 60000,
                createdAt: '2023-02-01T01:00:00Z',
                emailSentAt: '2023-02-01T01:00:00Z',
                currency: 'usd'
            },
            {
                type: 'members',
                value: 100,
                createdAt: '2023-01-01T00:00:00Z',
                emailSentAt: '2023-01-01T00:00:00Z',
                id: resourceId // duplicate id
            },
            {
                type: 'members',
                value: 500,
                createdAt: '2023-02-01T00:00:00Z',
                emailSentAt: '2023-02-01T00:00:00Z'
            },
            {
                type: 'members',
                value: 600,
                createdAt: '2023-02-01T00:00:00Z',
                emailSentAt: '2023-02-01T00:00:00Z'
            }
        ];

        validInputs.forEach(validInput => milestoneCreatePromises.push(Milestone.create(validInput)));

        const milestones = await Promise.all(milestoneCreatePromises);

        for (const milestone of milestones) {
            await repository.save(milestone);
        }
    });

    afterAll(function () {
        sinon.restore();
    });

    it('Can dispatch events when saving a new Milestone', async function () {
        assert(domainEventsSpy.callCount === 6);
    });

    it('Can return the latest milestone for members', async function () {
        const latestMemberCountMilestone = await repository.getLatestByType('members');
        const timeDiff = new Date(latestMemberCountMilestone.createdAt).getTime() - new Date('2023-02-01T00:00:00.000Z').getTime();
        assert(timeDiff === 0);
        assert(latestMemberCountMilestone.type === 'members');
        assert(latestMemberCountMilestone.value === 600);
    });

    it('Can return the latest milestone for ARR', async function () {
        const latestArrMilestone = await repository.getLatestByType('arr');
        const timeDiff = new Date(latestArrMilestone.createdAt).getTime() - new Date('2023-02-01T01:00:00Z').getTime();
        assert(timeDiff === 0);
        assert(latestArrMilestone.value === 60000);
        assert(latestArrMilestone.type === 'arr');
        assert(latestArrMilestone.currency === 'usd');
    });

    it('Can return the latest milestone for ARR for a specific currency', async function () {
        const latestArrMilestone = await repository.getLatestByType('arr', 'gbp');
        const timeDiff = new Date(latestArrMilestone.createdAt).getTime() - new Date('2023-01-30T00:00:00Z').getTime();
        assert(timeDiff === 0);
        assert(latestArrMilestone.value === 2000);
        assert(latestArrMilestone.type === 'arr');
        assert(latestArrMilestone.currency === 'gbp');
    });

    it('Can return the last sent email', async function () {
        const lastEmailSentMilestone = await repository.getLastEmailSent();
        const timeDiff = new Date(lastEmailSentMilestone.emailSentAt).getTime() - new Date('2023-02-01T01:00:00Z').getTime();
        assert(timeDiff === 0);
    });

    it('Can return the ARR milestone for a given value', async function () {
        const arrMilestoneForValue = await repository.getByARR(50000, 'usd');
        const timeDiff = new Date(arrMilestoneForValue.createdAt).getTime() - new Date('2023-02-01T01:00:00Z').getTime();
        assert(timeDiff === 0);
        assert(arrMilestoneForValue.type === 'arr');
        assert(arrMilestoneForValue.value === 50000);
        assert(arrMilestoneForValue.currency === 'usd');
        assert(arrMilestoneForValue.name === 'arr-50000-usd');
    });

    it('Can return the Members count milestone for a given value', async function () {
        const membersCountForValue = await repository.getByCount(100);
        const timeDiff = new Date(membersCountForValue.createdAt).getTime() - new Date('2023-01-01T00:00:00Z').getTime();
        assert(timeDiff === 0);
        assert(membersCountForValue.type === 'members');
        assert(membersCountForValue.value === 100);
        assert(membersCountForValue.name === 'members-100');
    });

    it('Can return all achieved milestones by type', async function () {
        const allArrUSDMilestones = await repository.getAllByType('arr');

        assert(allArrUSDMilestones.length === 2);

        const allArrGBPMilestones = await repository.getAllByType('arr', 'gbp');

        assert(allArrGBPMilestones.length === 2);

        const allMembersMilestones = await repository.getAllByType('members');

        assert(allMembersMilestones.length === 3);
    });
});
