var testHelper = require('../../support/test_helper');

var assert      = require('../../support/assert');
var step        = require('step');
var FastlyPurge = require('fastly-purge');
var _ = require('underscore');

var NamedMapsCacheEntry = require(__dirname + '/../../../lib/cartodb/cache/model/named_maps_entry');
var CartodbWindshaft = require(__dirname + '/../../../lib/cartodb/server');


describe('templates surrogate keys', function() {

    var serverOptions = require('../../../lib/cartodb/server_options');

    // Enable Varnish purge for tests
    var varnishHost = serverOptions.varnish_host;
    serverOptions.varnish_host = '127.0.0.1';
    var varnishPurgeEnabled = serverOptions.varnish_purge_enabled;
    serverOptions.varnish_purge_enabled = true;

    var fastlyConfig = serverOptions.fastly;
    var FAKE_FASTLY_API_KEY = 'fastly-api-key';
    var FAKE_FASTLY_SERVICE_ID = 'fake-service-id';
    serverOptions.fastly = {
        enabled: true,
        // the fastly api key
        apiKey: FAKE_FASTLY_API_KEY,
        // the service that will get surrogate key invalidation
        serviceId: FAKE_FASTLY_SERVICE_ID
    };

    var server = new CartodbWindshaft(serverOptions);

    var templateOwner = 'localhost';
    var templateName = 'acceptance';
    var expectedTemplateId = templateName;
    var template = {
        version: '0.0.1',
        name: templateName,
        auth: {
            method: 'open'
        },
        layergroup:  {
            version: '1.2.0',
            layers: [
                {
                    options: {
                        sql: 'select 1 cartodb_id, null::geometry as the_geom_webmercator',
                        cartocss: '#layer { marker-fill:blue; }',
                        cartocss_version: '2.3.0'
                    }
                }
            ]
        }
    };
    var templateUpdated = _.extend({}, template, {layergroup: {layers: [{
        type: 'plain',
        options: {
            color: 'red'
        }
    }]} });
    var expectedBody = { template_id: expectedTemplateId };

    var varnishHttpUrl = [
        'http://', serverOptions.varnish_host, ':', serverOptions.varnish_http_port
    ].join('');

    var cacheEntryKey = new NamedMapsCacheEntry(templateOwner, templateName).key();
    var invalidationMatchHeader = '\\b' + cacheEntryKey + '\\b';
    var fastlyPurgePath = '/service/' + FAKE_FASTLY_SERVICE_ID + '/purge/' + cacheEntryKey;

    var nock = require('nock');
    nock.enableNetConnect(/(127.0.0.1:5555|cartocdn.com)/);

    after(function(done) {
        serverOptions.varnish_purge_enabled = false;
        serverOptions.varnish_host = varnishHost;
        serverOptions.varnish_purge_enabled = varnishPurgeEnabled;

        serverOptions.fastly = fastlyConfig;

        nock.restore();
        done();
    });

    function createTemplate(callback) {
        var postTemplateRequest = {
            url: '/api/v1/map/named?api_key=1234',
            method: 'POST',
            headers: {
                host: templateOwner,
                'Content-Type': 'application/json'
            },
            data: JSON.stringify(template)
        };

        step(
            function postTemplate() {
                var next = this;
                assert.response(server,
                    postTemplateRequest,
                    {
                        status: 200
                    },
                    function(res) {
                        next(null, res);
                    }
                );
            },
            function rePostTemplate(err, res) {
                if (err) {
                    throw err;
                }
                var parsedBody = JSON.parse(res.body);
                assert.deepEqual(parsedBody, expectedBody);
                return true;
            },
            function finish(err) {
                callback(err);
            }
        );
    }

    it("invalidates surrogate keys on template update", function(done) {

        var scope = nock(varnishHttpUrl)
            .intercept('/key', 'PURGE')
            .matchHeader('Invalidation-Match', invalidationMatchHeader)
            .reply(204, '');

        var fastlyScope = nock(FastlyPurge.FASTLY_API_ENDPOINT)
            .post(fastlyPurgePath)
            .matchHeader('Fastly-Key', FAKE_FASTLY_API_KEY)
            .matchHeader('Accept', 'application/json')
            .reply(200, {
                status:'ok'
            });

        step(
            function createTemplateToUpdate() {
                createTemplate(this);
            },
            function putValidTemplate(err) {
                if (err) {
                    throw err;
                }
                var updateTemplateRequest = {
                    url: '/api/v1/map/named/' + expectedTemplateId + '/?api_key=1234',
                    method: 'PUT',
                    headers: {
                        host: templateOwner,
                        'Content-Type': 'application/json'
                    },
                    data: JSON.stringify(templateUpdated)
                };
                var next = this;
                assert.response(server,
                    updateTemplateRequest,
                    {
                        status: 200
                    },
                    function(res) {
                        setTimeout(function() {
                            next(null, res);
                        }, 50);
                    }
                );
            },
            function checkValidUpdate(err, res) {
                if (err) {
                    throw err;
                }
                var parsedBody = JSON.parse(res.body);
                assert.deepEqual(parsedBody, expectedBody);

                assert.equal(scope.pendingMocks().length, 0);
                assert.equal(fastlyScope.pendingMocks().length, 0);

                return null;
            },
            function finish(err) {
                if ( err ) {
                    return done(err);
                }
                testHelper.deleteRedisKeys({'map_tpl|localhost': 0}, done);
            }
        );
    });

    it("invalidates surrogate on template deletion", function(done) {

        var scope = nock(varnishHttpUrl)
            .intercept('/key', 'PURGE')
            .matchHeader('Invalidation-Match', invalidationMatchHeader)
            .reply(204, '');

        var fastlyScope = nock(FastlyPurge.FASTLY_API_ENDPOINT)
            .post(fastlyPurgePath)
            .matchHeader('Fastly-Key', FAKE_FASTLY_API_KEY)
            .matchHeader('Accept', 'application/json')
            .reply(200, {
                status:'ok'
            });

        step(
            function createTemplateToDelete() {
                createTemplate(this);
            },
            function deleteValidTemplate(err) {
                if (err) {
                    throw err;
                }
                var deleteTemplateRequest = {
                    url: '/api/v1/map/named/' + expectedTemplateId + '/?api_key=1234',
                    method: 'DELETE',
                    headers: {
                        host: templateOwner,
                        'Content-Type': 'application/json'
                    }
                };
                var next = this;
                assert.response(server,
                    deleteTemplateRequest,
                    {
                        status: 204
                    },
                    function(res) {
                        setTimeout(function() {
                            next(null, res);
                        }, 50);
                    }
                );
            },
            function checkValidUpdate(err) {
                if (err) {
                    throw err;
                }

                assert.equal(scope.pendingMocks().length, 0);
                assert.equal(fastlyScope.pendingMocks().length, 0);

                return null;
            },
            function finish(err) {
                done(err);
            }
        );
    });

    it("should update template even if surrogate key invalidation fails", function(done) {

        var scope = nock(varnishHttpUrl)
            .intercept('/key', 'PURGE')
            .matchHeader('Invalidation-Match', invalidationMatchHeader)
            .reply(503, '');

        var fastlyScope = nock(FastlyPurge.FASTLY_API_ENDPOINT)
            .post(fastlyPurgePath)
            .matchHeader('Fastly-Key', FAKE_FASTLY_API_KEY)
            .matchHeader('Accept', 'application/json')
            .reply(200, {
                status:'ok'
            });

        step(
            function createTemplateToUpdate() {
                createTemplate(this);
            },
            function putValidTemplate(err) {
                if (err) {
                    throw err;
                }
                var updateTemplateRequest = {
                    url: '/api/v1/map/named/' + expectedTemplateId + '/?api_key=1234',
                    method: 'PUT',
                    headers: {
                        host: templateOwner,
                        'Content-Type': 'application/json'
                    },
                    data: JSON.stringify(templateUpdated)
                };
                var next = this;
                assert.response(server,
                    updateTemplateRequest,
                    {
                        status: 200
                    },
                    function(res) {
                        setTimeout(function() {
                            next(null, res);
                        }, 50);
                    }
                );
            },
            function checkValidUpdate(err, res) {
                if (err) {
                    throw err;
                }
                var parsedBody = JSON.parse(res.body);
                assert.deepEqual(parsedBody, expectedBody);

                assert.equal(scope.pendingMocks().length, 0);
                assert.equal(fastlyScope.pendingMocks().length, 0);

                return null;
            },
            function finish(err) {
                if ( err ) {
                    return done(err);
                }
                testHelper.deleteRedisKeys({'map_tpl|localhost': 0}, done);
            }
        );
    });

});
