const should = require('should');
const sinon = require('sinon');
const ObjectId = require('bson-objectid');
const _ = require('lodash');
const testUtils = require('../../../../utils');
const urlUtils = require('../../../../../core/shared/url-utils');
const IndexGenerator = require('../../../../../core/frontend/services/sitemap/index-generator');
const PostGenerator = require('../../../../../core/frontend/services/sitemap/post-generator');
const PageGenerator = require('../../../../../core/frontend/services/sitemap/page-generator');
const TagGenerator = require('../../../../../core/frontend/services/sitemap/tag-generator');
const UserGenerator = require('../../../../../core/frontend/services/sitemap/user-generator');

should.Assertion.add('ValidUrlNode', function (options) {
    // Check urlNode looks correct
    /*eslint no-invalid-this: "off"*/
    let urlNode = this.obj;
    let flatNode;

    urlNode.should.be.an.Object().with.key('url');
    urlNode.url.should.be.an.Array();

    if (options.withImage) {
        urlNode.url.should.have.lengthOf(3);
    } else {
        urlNode.url.should.have.lengthOf(2);
    }

    /**
     * A urlNode looks something like:
     * { url:
     *   [ { loc: 'http://127.0.0.1:2369/author/' },
     *     { lastmod: '2014-12-22T11:54:00.100Z' },
     *     { 'image:image': [
     *       { 'image:loc': 'post-100.jpg' },
     *       { 'image:caption': 'post-100.jpg' }
     *     ] }
     *  ] }
     */
    flatNode = _.extend.apply(_, urlNode.url);

    if (options.withImage) {
        flatNode.should.be.an.Object().with.keys('loc', 'lastmod', 'image:image');
    } else {
        flatNode.should.be.an.Object().with.keys('loc', 'lastmod');
    }
});

describe('Generators', function () {
    let generator;

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

    describe('IndexGenerator', function () {
        beforeEach(function () {
            generator = new IndexGenerator({
                types: {
                    posts: new PostGenerator(),
                    pages: new PageGenerator(),
                    tags: new TagGenerator(),
                    authors: new UserGenerator()
                }
            });
        });

        describe('fn: getXml', function () {
            it('default', function () {
                const xml = generator.getXml();

                xml.should.match(/sitemap-tags.xml/);
                xml.should.match(/sitemap-posts.xml/);
                xml.should.match(/sitemap-pages.xml/);
                xml.should.match(/sitemap-authors.xml/);
            });
        });
    });

    describe('PostGenerator', function () {
        beforeEach(function () {
            generator = new PostGenerator();
        });

        describe('fn: createNodeFromDatum', function () {
            it('adds an image:image element if post has a cover image', function () {
                const urlNode = generator.createUrlNodeFromDatum('https://myblog.com/test/', testUtils.DataGenerator.forKnex.createPost({
                    feature_image: 'post-100.jpg',
                    page: false,
                    slug: 'test'
                }));

                urlNode.should.be.a.ValidUrlNode({withImage: true});
            });
        });

        describe('fn: getXml', function () {
            beforeEach(function () {
                sinon.stub(urlUtils, 'urlFor');
            });

            it('get cached xml', function () {
                sinon.spy(generator, 'generateXmlFromNodes');
                generator.siteMapContent = 'something';
                generator.getXml().should.eql('something');
                generator.siteMapContent = null;
                generator.generateXmlFromNodes.called.should.eql(false);
            });

            it('compare content output', function () {
                let idxFirst;
                let idxSecond;
                let idxThird;

                urlUtils.urlFor.withArgs('image', {image: 'post-100.jpg'}, true).returns('http://my-ghost-blog.com/images/post-100.jpg');
                urlUtils.urlFor.withArgs('image', {image: 'post-200.jpg'}, true).returns('http://my-ghost-blog.com/images/post-200.jpg');
                urlUtils.urlFor.withArgs('image', {image: 'post-300.jpg'}, true).returns('http://my-ghost-blog.com/images/post-300.jpg');
                urlUtils.urlFor.withArgs('sitemap_xsl', true).returns('http://my-ghost-blog.com/sitemap.xsl');

                generator.addUrl('http://my-ghost-blog.com/url/100/', testUtils.DataGenerator.forKnex.createPost({
                    feature_image: 'post-100.jpg',
                    created_at: (Date.UTC(2014, 11, 22, 12) - 360000) + 100,
                    updated_at: null,
                    published_at: null,
                    slug: '100'
                }));

                generator.addUrl('http://my-ghost-blog.com/url/200/', testUtils.DataGenerator.forKnex.createPost({
                    created_at: (Date.UTC(2014, 11, 22, 12) - 360000) + 200,
                    updated_at: null,
                    published_at: null,
                    slug: '200'
                }));

                generator.addUrl('http://my-ghost-blog.com/url/300/', testUtils.DataGenerator.forKnex.createPost({
                    created_at: (Date.UTC(2014, 11, 22, 12) - 360000) + 300,
                    feature_image: 'post-300.jpg',
                    updated_at: null,
                    published_at: null,
                    slug: '300'
                }));

                const xml = generator.getXml();

                xml.should.containEql('<loc>http://my-ghost-blog.com/url/100/</loc>');
                xml.should.containEql('<loc>http://my-ghost-blog.com/url/200/</loc>');
                xml.should.containEql('<loc>http://my-ghost-blog.com/url/300/</loc>');

                xml.should.containEql('<image:loc>http://my-ghost-blog.com/images/post-100.jpg</image:loc>');
                // This should NOT be present
                xml.should.not.containEql('<image:loc>http://my-ghost-blog.com/images/post-200.jpg</image:loc>');
                xml.should.containEql('<image:loc>http://my-ghost-blog.com/images/post-300.jpg</image:loc>');

                // Validate order newest to oldest
                idxFirst = xml.indexOf('<loc>http://my-ghost-blog.com/url/300/</loc>');
                idxSecond = xml.indexOf('<loc>http://my-ghost-blog.com/url/200/</loc>');
                idxThird = xml.indexOf('<loc>http://my-ghost-blog.com/url/100/</loc>');

                idxFirst.should.be.below(idxSecond);
                idxSecond.should.be.below(idxThird);
            });
        });

        describe('fn: removeUrl', function () {
            let post;

            beforeEach(function () {
                post = testUtils.DataGenerator.forKnex.createPost();
                generator.nodeLookup[post.id] = 'node';
            });

            afterEach(function () {
                generator.nodeLookup = {};
                generator.nodeTimeLookup = {};
            });

            it('remove none existend url', function () {
                generator.removeUrl('https://myblog.com/blog/podcast/featured/', testUtils.DataGenerator.forKnex.createPost());
                Object.keys(generator.nodeLookup).length.should.eql(1);
            });

            it('remove existing url', function () {
                generator.removeUrl('https://myblog.com/blog/test/', post);
                Object.keys(generator.nodeLookup).length.should.eql(0);
            });
        });
    });

    describe('PageGenerator', function () {
        beforeEach(function () {
            generator = new PageGenerator();
        });

        describe('fn: getXml', function () {
            it('add', function () {
                generator.addUrl('http://my-ghost-blog.com/home/', {id: 'identifier1', staticRoute: true});
                generator.addUrl('http://my-ghost-blog.com/magic/', {id: 'identifier2', staticRoute: false});
                generator.addUrl('http://my-ghost-blog.com/subscribe/', {id: ObjectId.generate(), page: 1});

                generator.getXml();

                generator.siteMapContent.should.containEql('<loc>http://my-ghost-blog.com/home/</loc>');
                generator.siteMapContent.should.containEql('<loc>http://my-ghost-blog.com/magic/</loc>');
                generator.siteMapContent.should.containEql('<loc>http://my-ghost-blog.com/subscribe/</loc>');

                // <loc> should exist exactly one time
                generator.siteMapContent.match(/<loc>/g).length.should.eql(3);
            });
        });
    });

    describe('TagGenerator', function () {
        beforeEach(function () {
            generator = new TagGenerator();
        });
    });

    describe('UserGenerator', function () {
        beforeEach(function () {
            generator = new UserGenerator();
        });

        describe('fn: validateImageUrl', function () {
            it('image url is localhost', function () {
                generator.validateImageUrl('http://localhost:2368/content/images/1.jpg').should.be.true();
            });

            it('image url is https', function () {
                generator.validateImageUrl('https://myblog.com/content/images/1.png').should.be.true();
            });

            it('image url is external', function () {
                generator.validateImageUrl('https://myblog.com/1.jpg').should.be.true();
            });

            it('no host', function () {
                generator.validateImageUrl('/content/images/1.jpg').should.be.false();
            });
        });
    });
});
