const {agentProvider, fixtureManager, configUtils} = require('../../utils/e2e-framework');
const should = require('should');
const models = require('../../../core/server/models');
const urlService = require('../../../core/server/services/url');
const memberAttributionService = require('../../../core/server/services/member-attribution');
const urlUtils = require('../../../core/shared/url-utils');

describe('Member Attribution Service', function () {
    before(async function () {
        await agentProvider.getAdminAPIAgent();
        await fixtureManager.init('posts');
    });

    /**
     * Test that getAttribution correctly resolves all model types that are supported
     */
    describe('getAttribution for models', function () {
        describe('without subdirectory', function () {
            it('resolves urls', async function () {
                const subdomainRelative = '/my-static-page/';
                const url = urlUtils.createUrl(subdomainRelative, false);
                const absoluteUrl = urlUtils.createUrl(subdomainRelative, true);

                const attribution = await memberAttributionService.service.getAttribution([
                    {
                        path: url,
                        time: Date.now()
                    }
                ]);
                attribution.should.match(({
                    id: null,
                    url: subdomainRelative,
                    type: 'url'
                }));

                (await attribution.fetchResource()).should.match(({
                    id: null,
                    url: absoluteUrl,
                    type: 'url',
                    title: subdomainRelative
                }));
            });

            it('resolves posts', async function () {
                const id = fixtureManager.get('posts', 0).id;
                const post = await models.Post.where('id', id).fetch({require: true});
                const url = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: true});

                const attribution = await memberAttributionService.service.getAttribution([
                    {
                        path: url,
                        time: Date.now()
                    }
                ]);
                attribution.should.match(({
                    id: post.id,
                    url,
                    type: 'post'
                }));

                const absoluteUrl = urlService.getUrlByResourceId(post.id, {absolute: true, withSubdirectory: true});

                (await attribution.fetchResource()).should.match(({
                    id: post.id,
                    url: absoluteUrl,
                    type: 'post',
                    title: post.get('title')
                }));
            });

            it('resolves removed resources', async function () {
                const id = fixtureManager.get('posts', 0).id;
                const post = await models.Post.where('id', id).fetch({require: true});
                const url = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: true});
                const urlWithoutSubdirectory = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: false});
                const absoluteUrl = urlService.getUrlByResourceId(post.id, {absolute: true, withSubdirectory: true});

                const attribution = await memberAttributionService.service.getAttribution([
                    {
                        path: url,
                        time: Date.now()
                    }
                ]);

                // Without subdirectory
                attribution.should.match(({
                    id: post.id,
                    url: urlWithoutSubdirectory,
                    type: 'post'
                }));

                // Unpublish this post
                await models.Post.edit({status: 'draft'}, {id});

                (await attribution.fetchResource()).should.match(({
                    id: null,
                    url: absoluteUrl,
                    type: 'url',
                    title: urlWithoutSubdirectory
                }));

                await models.Post.edit({status: 'published'}, {id});
            });

            it('resolves pages', async function () {
                const id = fixtureManager.get('posts', 5).id;
                const post = await models.Post.where('id', id).fetch({require: true});
                should(post.get('type')).eql('page');

                const url = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: true});

                const attribution = await memberAttributionService.service.getAttribution([
                    {
                        path: url,
                        time: Date.now()
                    }
                ]);
                attribution.should.match(({
                    id: post.id,
                    url,
                    type: 'page'
                }));

                const absoluteUrl = urlService.getUrlByResourceId(post.id, {absolute: true, withSubdirectory: true});

                (await attribution.fetchResource()).should.match(({
                    id: post.id,
                    url: absoluteUrl,
                    type: 'page',
                    title: post.get('title')
                }));
            });

            it('resolves tags', async function () {
                const id = fixtureManager.get('tags', 0).id;
                const tag = await models.Tag.where('id', id).fetch({require: true});
                const url = urlService.getUrlByResourceId(tag.id, {absolute: false, withSubdirectory: true});

                const attribution = await memberAttributionService.service.getAttribution([
                    {
                        path: url,
                        time: Date.now()
                    }
                ]);
                attribution.should.match(({
                    id: tag.id,
                    url,
                    type: 'tag'
                }));

                const absoluteUrl = urlService.getUrlByResourceId(tag.id, {absolute: true, withSubdirectory: true});

                (await attribution.fetchResource()).should.match(({
                    id: tag.id,
                    url: absoluteUrl,
                    type: 'tag',
                    title: tag.get('name')
                }));
            });

            it('resolves authors', async function () {
                const id = fixtureManager.get('users', 0).id;
                const author = await models.User.where('id', id).fetch({require: true});
                const url = urlService.getUrlByResourceId(author.id, {absolute: false, withSubdirectory: true});

                const attribution = await memberAttributionService.service.getAttribution([
                    {
                        path: url,
                        time: Date.now()
                    }
                ]);
                attribution.should.match(({
                    id: author.id,
                    url,
                    type: 'author'
                }));

                const absoluteUrl = urlService.getUrlByResourceId(author.id, {absolute: true, withSubdirectory: true});

                (await attribution.fetchResource()).should.match(({
                    id: author.id,
                    url: absoluteUrl,
                    type: 'author',
                    title: author.get('name')
                }));
            });
        });

        describe('with subdirectory', function () {
            beforeEach(function () {
                configUtils.set('url', 'https://siteurl.com/subdirectory/');
            });

            afterEach(async function () {
                await configUtils.restore();
            });

            it('resolves urls', async function () {
                const subdomainRelative = '/my-static-page/';
                const url = urlUtils.createUrl(subdomainRelative, false);
                const absoluteUrl = urlUtils.createUrl(subdomainRelative, true);

                const attribution = await memberAttributionService.service.getAttribution([
                    {
                        path: url,
                        time: Date.now()
                    }
                ]);
                attribution.should.match(({
                    id: null,
                    url: subdomainRelative,
                    type: 'url'
                }));

                (await attribution.fetchResource()).should.match(({
                    id: null,
                    url: absoluteUrl,
                    type: 'url',
                    title: subdomainRelative
                }));
            });

            it('resolves posts', async function () {
                const id = fixtureManager.get('posts', 0).id;
                const post = await models.Post.where('id', id).fetch({require: true});
                const url = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: true});
                const urlWithoutSubdirectory = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: false});

                // Check if we are actually testing with subdirectories
                should(url).startWith('/subdirectory/');

                const attribution = await memberAttributionService.service.getAttribution([
                    {
                        path: url,
                        time: Date.now()
                    }
                ]);

                // Without subdirectory
                attribution.should.match(({
                    id: post.id,
                    url: urlWithoutSubdirectory,
                    type: 'post'
                }));

                const absoluteUrl = urlService.getUrlByResourceId(post.id, {absolute: true, withSubdirectory: true});

                (await attribution.fetchResource()).should.match(({
                    id: post.id,
                    url: absoluteUrl,
                    type: 'post',
                    title: post.get('title')
                }));
            });

            it('resolves removed resources', async function () {
                const id = fixtureManager.get('posts', 0).id;
                const post = await models.Post.where('id', id).fetch({require: true});
                const url = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: true});
                const urlWithoutSubdirectory = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: false});
                const absoluteUrl = urlService.getUrlByResourceId(post.id, {absolute: true, withSubdirectory: true});

                // Check if we are actually testing with subdirectories
                should(url).startWith('/subdirectory/');

                const attribution = await memberAttributionService.service.getAttribution([
                    {
                        path: url,
                        time: Date.now()
                    }
                ]);

                // Without subdirectory
                attribution.should.match(({
                    id: post.id,
                    url: urlWithoutSubdirectory,
                    type: 'post'
                }));

                // Unpublish this post
                await models.Post.edit({status: 'draft'}, {id});

                (await attribution.fetchResource()).should.match(({
                    id: null,
                    url: absoluteUrl,
                    type: 'url',
                    title: urlWithoutSubdirectory
                }));
            });

            it('resolves pages', async function () {
                const id = fixtureManager.get('posts', 5).id;
                const post = await models.Post.where('id', id).fetch({require: true});
                should(post.get('type')).eql('page');

                const url = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: true});
                const urlWithoutSubdirectory = urlService.getUrlByResourceId(post.id, {absolute: false, withSubdirectory: false});

                const attribution = await memberAttributionService.service.getAttribution([
                    {
                        path: url,
                        time: Date.now()
                    }
                ]);
                attribution.should.match(({
                    id: post.id,
                    url: urlWithoutSubdirectory,
                    type: 'page'
                }));

                const absoluteUrl = urlService.getUrlByResourceId(post.id, {absolute: true, withSubdirectory: true});

                (await attribution.fetchResource()).should.match(({
                    id: post.id,
                    url: absoluteUrl,
                    type: 'page',
                    title: post.get('title')
                }));
            });

            it('resolves tags', async function () {
                const id = fixtureManager.get('tags', 0).id;
                const tag = await models.Tag.where('id', id).fetch({require: true});
                const url = urlService.getUrlByResourceId(tag.id, {absolute: false, withSubdirectory: true});
                const urlWithoutSubdirectory = urlService.getUrlByResourceId(tag.id, {absolute: false, withSubdirectory: false});

                const attribution = await memberAttributionService.service.getAttribution([
                    {
                        path: url,
                        time: Date.now()
                    }
                ]);
                attribution.should.match(({
                    id: tag.id,
                    url: urlWithoutSubdirectory,
                    type: 'tag'
                }));

                const absoluteUrl = urlService.getUrlByResourceId(tag.id, {absolute: true, withSubdirectory: true});

                (await attribution.fetchResource()).should.match(({
                    id: tag.id,
                    url: absoluteUrl,
                    type: 'tag',
                    title: tag.get('name')
                }));
            });

            it('resolves authors', async function () {
                const id = fixtureManager.get('users', 0).id;
                const author = await models.User.where('id', id).fetch({require: true});
                const url = urlService.getUrlByResourceId(author.id, {absolute: false, withSubdirectory: true});
                const urlWithoutSubdirectory = urlService.getUrlByResourceId(author.id, {absolute: false, withSubdirectory: false});

                const attribution = await memberAttributionService.service.getAttribution([
                    {
                        path: url,
                        time: Date.now()
                    }
                ]);
                attribution.should.match(({
                    id: author.id,
                    url: urlWithoutSubdirectory,
                    type: 'author'
                }));

                const absoluteUrl = urlService.getUrlByResourceId(author.id, {absolute: true, withSubdirectory: true});

                (await attribution.fetchResource()).should.match(({
                    id: author.id,
                    url: absoluteUrl,
                    type: 'author',
                    title: author.get('name')
                }));
            });
        });
    });

    /**
     * Test that getAttribution correctly resolves all model types that are supported
     */
    describe('getAttribution for referrer', function () {
        it('resolves urls', async function () {
            const attribution = await memberAttributionService.service.getAttribution([
                {
                    id: null,
                    path: '/',
                    time: Date.now(),
                    referrerSource: 'ghost-explore',
                    referrerMedium: null,
                    referrerUrl: null
                }
            ]);
            attribution.should.match(({
                id: null,
                url: '/',
                type: 'url',
                referrerSource: 'Ghost Explore',
                referrerMedium: 'Ghost Network',
                referrerUrl: null
            }));
        });

        it('resolves Portal signup URLs', async function () {
            // NOTE: We cannot test the actual hash URL here; the attribution below is what is receieved when navigating to /#/portal/signup?ref=ghost
            // TODO: We don't appear to have tests for parsing URLs for params.
            const attribution = await memberAttributionService.service.getAttribution([
                {
                    path: '/',
                    time: Date.now(),
                    referrerSource: 'casper'
                }
            ]);
            attribution.should.match(({
                id: null,
                url: '/',
                type: 'url',
                referrerSource: 'casper',
                referrerMedium: null,
                referrerUrl: null
            }));
        });
    });
});