const should = require('should');
const path = require('path');
const fs = require('fs');
const _ = require('lodash');
const supertest = require('supertest');
const testUtils = require('../../utils');
const config = require('../../../core/shared/config');
const localUtils = require('./utils');

const ghost = testUtils.startGhost;

describe('Themes API', function () {
    let ghostServer;
    let ownerRequest;

    const uploadTheme = (options) => {
        const themePath = options.themePath;
        const fieldName = 'file';
        const request = options.request || ownerRequest;

        return request
            .post(localUtils.API.getApiQuery('themes/upload'))
            .set('Origin', config.get('url'))
            .attach(fieldName, themePath);
    };

    before(function () {
        return ghost()
            .then((_ghostServer) => {
                ghostServer = _ghostServer;
            });
    });

    before(function () {
        ownerRequest = supertest.agent(config.get('url'));
        return localUtils.doAuth(ownerRequest);
    });

    it('Can request all available themes', function () {
        return ownerRequest
            .get(localUtils.API.getApiQuery('themes/'))
            .set('Origin', config.get('url'))
            .expect(200)
            .then((res) => {
                const jsonResponse = res.body;
                should.exist(jsonResponse.themes);
                localUtils.API.checkResponse(jsonResponse, 'themes');
                jsonResponse.themes.length.should.eql(5);

                localUtils.API.checkResponse(jsonResponse.themes[0], 'theme');
                jsonResponse.themes[0].name.should.eql('broken-theme');
                jsonResponse.themes[0].package.should.be.an.Object().with.properties('name', 'version');
                jsonResponse.themes[0].active.should.be.false();

                localUtils.API.checkResponse(jsonResponse.themes[1], 'theme', 'templates');
                jsonResponse.themes[1].name.should.eql('casper');
                jsonResponse.themes[1].package.should.be.an.Object().with.properties('name', 'version');
                jsonResponse.themes[1].active.should.be.true();

                localUtils.API.checkResponse(jsonResponse.themes[2], 'theme');
                jsonResponse.themes[2].name.should.eql('casper-1.4');
                jsonResponse.themes[2].package.should.be.an.Object().with.properties('name', 'version');
                jsonResponse.themes[2].active.should.be.false();

                localUtils.API.checkResponse(jsonResponse.themes[3], 'theme');
                jsonResponse.themes[3].name.should.eql('test-theme');
                jsonResponse.themes[3].package.should.be.an.Object().with.properties('name', 'version');
                jsonResponse.themes[3].active.should.be.false();

                localUtils.API.checkResponse(jsonResponse.themes[4], 'theme');
                jsonResponse.themes[4].name.should.eql('test-theme-channels');
                jsonResponse.themes[4].package.should.be.false();
                jsonResponse.themes[4].active.should.be.false();
            });
    });

    it('Can download a theme', function () {
        return ownerRequest
            .get(localUtils.API.getApiQuery('themes/casper/download/'))
            .set('Origin', config.get('url'))
            .expect('Content-Type', /application\/zip/)
            .expect('Content-Disposition', 'attachment; filename=casper.zip')
            .expect(200);
    });

    it('Can upload a valid theme', function () {
        return uploadTheme({themePath: path.join(__dirname, '..', '..', 'utils', 'fixtures', 'themes', 'valid.zip')})
            .then((res) => {
                const jsonResponse = res.body;

                should.not.exist(res.headers['x-cache-invalidate']);

                should.exist(jsonResponse.themes);
                localUtils.API.checkResponse(jsonResponse, 'themes');
                jsonResponse.themes.length.should.eql(1);
                localUtils.API.checkResponse(jsonResponse.themes[0], 'theme');
                jsonResponse.themes[0].name.should.eql('valid');
                jsonResponse.themes[0].active.should.be.false();

                // upload same theme again to force override
                return uploadTheme({themePath: path.join(__dirname, '..', '..', 'utils', 'fixtures', 'themes', 'valid.zip')});
            })
            .then((res) => {
                const jsonResponse = res.body;

                should.not.exist(res.headers['x-cache-invalidate']);
                should.exist(jsonResponse.themes);
                localUtils.API.checkResponse(jsonResponse, 'themes');
                jsonResponse.themes.length.should.eql(1);
                localUtils.API.checkResponse(jsonResponse.themes[0], 'theme');
                jsonResponse.themes[0].name.should.eql('valid');
                jsonResponse.themes[0].active.should.be.false();

                // ensure tmp theme folder contains two themes now
                const tmpFolderContents = fs.readdirSync(config.getContentPath('themes'));
                tmpFolderContents.forEach((theme, index) => {
                    if (theme.match(/^\./)) {
                        tmpFolderContents.splice(index, 1);
                    }
                });

                // Note: at this point, the tmpFolder can legitimately still contain a valid_34324324 backup
                // As it is deleted asynchronously
                tmpFolderContents.should.containEql('valid');
                tmpFolderContents.should.containEql('valid.zip');

                // Check the Themes API returns the correct result
                return ownerRequest
                    .get(localUtils.API.getApiQuery('themes/'))
                    .set('Origin', config.get('url'))
                    .expect(200);
            })
            .then((res) => {
                const jsonResponse = res.body;

                should.exist(jsonResponse.themes);
                localUtils.API.checkResponse(jsonResponse, 'themes');
                jsonResponse.themes.length.should.eql(6);

                // Casper should be present and still active
                const casperTheme = _.find(jsonResponse.themes, {name: 'casper'});
                should.exist(casperTheme);
                localUtils.API.checkResponse(casperTheme, 'theme', 'templates');
                casperTheme.active.should.be.true();

                // The added theme should be here
                const addedTheme = _.find(jsonResponse.themes, {name: 'valid'});
                should.exist(addedTheme);
                localUtils.API.checkResponse(addedTheme, 'theme');
                addedTheme.active.should.be.false();

                // Note: at this point, the API should not return a valid_34324324 backup folder as a theme
                _.map(jsonResponse.themes, 'name').should.eql([
                    'broken-theme',
                    'casper',
                    'casper-1.4',
                    'test-theme',
                    'test-theme-channels',
                    'valid'
                ]);
            });
    });

    it('Can delete a theme', function () {
        return ownerRequest
            .del(localUtils.API.getApiQuery('themes/valid'))
            .set('Origin', config.get('url'))
            .expect(204)
            .then((res) => {
                const jsonResponse = res.body;
                // Delete requests have empty bodies
                jsonResponse.should.eql({});

                // ensure tmp theme folder contains one theme again now
                const tmpFolderContents = fs.readdirSync(config.getContentPath('themes'));
                tmpFolderContents.forEach((theme, index) => {
                    if (theme.match(/^\./)) {
                        tmpFolderContents.splice(index, 1);
                    }
                });
                tmpFolderContents.should.be.an.Array().with.lengthOf(9);

                tmpFolderContents.should.eql([
                    'broken-theme',
                    'casper',
                    'casper-1.4',
                    'casper.zip',
                    'invalid.zip',
                    'test-theme',
                    'test-theme-channels',
                    'valid.zip',
                    'warnings.zip'
                ]);

                // Check the themes API returns the correct result after deletion
                return ownerRequest
                    .get(localUtils.API.getApiQuery('themes/'))
                    .set('Origin', config.get('url'))
                    .expect(200);
            })
            .then((res) => {
                const jsonResponse = res.body;

                should.exist(jsonResponse.themes);
                localUtils.API.checkResponse(jsonResponse, 'themes');
                jsonResponse.themes.length.should.eql(5);

                // Casper should be present and still active
                const casperTheme = _.find(jsonResponse.themes, {name: 'casper'});
                should.exist(casperTheme);
                localUtils.API.checkResponse(casperTheme, 'theme', 'templates');
                casperTheme.active.should.be.true();

                // The deleted theme should not be here
                const deletedTheme = _.find(jsonResponse.themes, {name: 'valid'});
                should.not.exist(deletedTheme);
            });
    });

    it('Can upload a theme, which has warnings', function () {
        return uploadTheme({themePath: path.join(__dirname, '/../../utils/fixtures/themes/warnings.zip')})
            .then((res) => {
                const jsonResponse = res.body;

                should.exist(jsonResponse.themes);
                localUtils.API.checkResponse(jsonResponse, 'themes');
                jsonResponse.themes.length.should.eql(1);
                localUtils.API.checkResponse(jsonResponse.themes[0], 'theme', ['warnings']);
                jsonResponse.themes[0].name.should.eql('warnings');
                jsonResponse.themes[0].active.should.be.false();
                jsonResponse.themes[0].warnings.should.be.an.Array();

                // Delete the theme to clean up after the test
                return ownerRequest
                    .del(localUtils.API.getApiQuery('themes/warnings'))
                    .set('Origin', config.get('url'))
                    .expect(204);
            });
    });

    it('Can activate a theme', function () {
        return ownerRequest
            .get(localUtils.API.getApiQuery('themes/'))
            .set('Origin', config.get('url'))
            .expect(200)
            .then((res) => {
                const jsonResponse = res.body;

                should.exist(jsonResponse.themes);
                localUtils.API.checkResponse(jsonResponse, 'themes');
                jsonResponse.themes.length.should.eql(5);

                const casperTheme = _.find(jsonResponse.themes, {name: 'casper'});
                should.exist(casperTheme);
                localUtils.API.checkResponse(casperTheme, 'theme', 'templates');
                casperTheme.active.should.be.true();

                const testTheme = _.find(jsonResponse.themes, {name: 'test-theme'});
                should.exist(testTheme);
                localUtils.API.checkResponse(testTheme, 'theme');
                testTheme.active.should.be.false();

                // Finally activate the new theme
                return ownerRequest
                    .put(localUtils.API.getApiQuery('themes/test-theme/activate'))
                    .set('Origin', config.get('url'))
                    .expect(200);
            })
            .then((res) => {
                const jsonResponse = res.body;

                should.exist(res.headers['x-cache-invalidate']);
                should.exist(jsonResponse.themes);
                localUtils.API.checkResponse(jsonResponse, 'themes');
                jsonResponse.themes.length.should.eql(1);

                const casperTheme = _.find(jsonResponse.themes, {name: 'casper'});
                should.not.exist(casperTheme);

                const testTheme = _.find(jsonResponse.themes, {name: 'test-theme'});
                should.exist(testTheme);
                localUtils.API.checkResponse(testTheme, 'theme', ['warnings', 'templates']);
                testTheme.active.should.be.true();
                testTheme.warnings.should.be.an.Array();
            });
    });
});
