const {agentProvider, mockManager, fixtureManager} = require('../../utils/e2e-framework');
const {stripeMocker} = require('../../utils/e2e-framework-mock-manager');
const models = require('../../../core/server/models');
const assert = require('assert/strict');
const urlService = require('../../../core/server/services/url');
const DomainEvents = require('@tryghost/domain-events');

let membersAgent, adminAgent;

async function getPost(id) {
    // eslint-disable-next-line dot-notation
    return await models['Post'].where('id', id).fetch({require: true});
}

describe('Create Stripe Checkout Session for Donations', function () {
    before(async function () {
        const agents = await agentProvider.getAgentsForMembers();
        membersAgent = agents.membersAgent;
        adminAgent = agents.adminAgent;

        await fixtureManager.init('posts', 'members');
        await adminAgent.loginAsOwner();
    });

    beforeEach(function () {
        mockManager.mockStripe();
        mockManager.mockMail();
    });

    afterEach(function () {
        mockManager.restore();
    });

    it('Can create an anonymous checkout session for a donation', async function () {
        // Fake a visit to a post
        const post = await getPost(fixtureManager.get('posts', 0).id);
        const url = urlService.getUrlByResourceId(post.id, {absolute: false});

        await membersAgent.post('/api/create-stripe-checkout-session/')
            .body({
                customerEmail: 'paid@test.com',
                type: 'donation',
                successUrl: 'https://example.com/?type=success',
                cancelUrl: 'https://example.com/?type=cancel',
                metadata: {
                    test: 'hello',
                    urlHistory: [
                        {
                            path: url,
                            time: Date.now(),
                            referrerMedium: null,
                            referrerSource: 'ghost-explore',
                            referrerUrl: 'https://example.com/blog/'
                        }
                    ]
                }
            })
            .expectStatus(200)
            .matchBodySnapshot();

        // Send a webhook of a paid invoice for this session
        await stripeMocker.sendWebhook({
            type: 'invoice.payment_succeeded',
            data: {
                object: {
                    type: 'invoice',
                    paid: true,
                    amount_paid: 1200,
                    currency: 'usd',
                    customer: (stripeMocker.checkoutSessions[0].customer),
                    customer_name: 'Paid Test',
                    customer_email: 'exampledonation@example.com',
                    metadata: {
                        ...(stripeMocker.checkoutSessions[0].invoice_creation?.invoice_data?.metadata ?? {})
                    }
                }
            }
        });

        // Check email received
        mockManager.assert.sentEmail({
            subject: '💰 One-time payment received: $12.00 from Paid Test',
            to: 'jbloggs@example.com'
        });

        // Check stored in database
        const lastDonation = await models.DonationPaymentEvent.findOne({
            email: 'exampledonation@example.com'
        }, {require: true});
        assert.equal(lastDonation.get('amount'), 1200);
        assert.equal(lastDonation.get('currency'), 'usd');
        assert.equal(lastDonation.get('email'), 'exampledonation@example.com');
        assert.equal(lastDonation.get('name'), 'Paid Test');
        assert.equal(lastDonation.get('member_id'), null);

        // Check referrer
        assert.equal(lastDonation.get('referrer_url'), 'example.com');
        assert.equal(lastDonation.get('referrer_medium'), 'Ghost Network');
        assert.equal(lastDonation.get('referrer_source'), 'Ghost Explore');

        // Check attributed correctly
        assert.equal(lastDonation.get('attribution_id'), post.id);
        assert.equal(lastDonation.get('attribution_type'), 'post');
        assert.equal(lastDonation.get('attribution_url'), url);
    });

    it('Can create a member checkout session for a donation', async function () {
        // Fake a visit to a post
        const post = await getPost(fixtureManager.get('posts', 0).id);
        const url = urlService.getUrlByResourceId(post.id, {absolute: false});

        const email = 'test-member-create-donation-session@email.com';

        const membersService = require('../../../core/server/services/members');
        const member = await membersService.api.members.create({email, name: 'Member Test'});
        const token = await membersService.api.getMemberIdentityToken(member.get('transient_id'));

        await DomainEvents.allSettled();

        // Check email received
        mockManager.assert.sentEmail({
            subject: '🥳 Free member signup: Member Test',
            to: 'jbloggs@example.com'
        });

        await membersAgent.post('/api/create-stripe-checkout-session/')
            .body({
                customerEmail: email,
                identity: token,
                type: 'donation',
                successUrl: 'https://example.com/?type=success',
                cancelUrl: 'https://example.com/?type=cancel',
                metadata: {
                    test: 'hello',
                    urlHistory: [
                        {
                            path: url,
                            time: Date.now(),
                            referrerMedium: null,
                            referrerSource: 'ghost-explore',
                            referrerUrl: 'https://example.com/blog/'
                        }
                    ]
                }
            })
            .expectStatus(200)
            .matchBodySnapshot();

        // Send a webhook of a paid invoice for this session
        await stripeMocker.sendWebhook({
            type: 'invoice.payment_succeeded',
            data: {
                object: {
                    type: 'invoice',
                    paid: true,
                    amount_paid: 1220,
                    currency: 'eur',
                    customer: (stripeMocker.checkoutSessions[0].customer),
                    customer_name: 'Member Test',
                    customer_email: email,
                    metadata: {
                        ...(stripeMocker.checkoutSessions[0].invoice_creation?.invoice_data?.metadata ?? {})
                    }
                }
            }
        });

        // Check email received
        mockManager.assert.sentEmail({
            subject: '💰 One-time payment received: €12.20 from Member Test',
            to: 'jbloggs@example.com'
        });

        // Check stored in database
        const lastDonation = await models.DonationPaymentEvent.findOne({
            email
        }, {require: true});
        assert.equal(lastDonation.get('amount'), 1220);
        assert.equal(lastDonation.get('currency'), 'eur');
        assert.equal(lastDonation.get('email'), email);
        assert.equal(lastDonation.get('name'), 'Member Test');
        assert.equal(lastDonation.get('member_id'), member.id);

        // Check referrer
        assert.equal(lastDonation.get('referrer_url'), 'example.com');
        assert.equal(lastDonation.get('referrer_medium'), 'Ghost Network');
        assert.equal(lastDonation.get('referrer_source'), 'Ghost Explore');

        // Check attributed correctly
        assert.equal(lastDonation.get('attribution_id'), post.id);
        assert.equal(lastDonation.get('attribution_type'), 'post');
        assert.equal(lastDonation.get('attribution_url'), url);
    });
});
