var _         = require('lodash'),
    xml       = require('xml'),
    moment    = require('moment'),
    config    = require('../../../config'),
    events    = require('../../../events'),
    utils     = require('./utils'),
    Promise   = require('bluebird'),
    path      = require('path'),
    CHANGE_FREQ = 'weekly',
    XMLNS_DECLS;

// Sitemap specific xml namespace declarations that should not change
XMLNS_DECLS = {
    _attr: {
        xmlns: 'http://www.sitemaps.org/schemas/sitemap/0.9',
        'xmlns:image': 'http://www.google.com/schemas/sitemap-image/1.1',
        'xmlns:xhtml': 'http://www.w3.org/1999/xhtml'
    }
};

function BaseSiteMapGenerator() {
    this.lastModified = 0;
    this.nodeLookup = {};
    this.nodeTimeLookup = {};
    this.siteMapContent = '';
    this.dataEvents = events;
}

_.extend(BaseSiteMapGenerator.prototype, {
    init: function () {
        var self = this;

        return this.refreshAll().then(function () {
            return self.bindEvents();
        });
    },

    bindEvents: _.noop,

    getData: function () {
        return Promise.resolve([]);
    },

    refreshAll: function () {
        var self = this;

        // Load all data
        return this.getData().then(function (data) {
            // Generate SiteMap from data
            return self.generateXmlFromData(data);
        }).then(function (generatedXml) {
            self.siteMapContent = generatedXml;
        });
    },

    generateXmlFromData: function (data) {
        // Create all the url elements in JSON
        var self = this,
            nodes;
        nodes = _.map(data, function (datum, index) {
            var node = self.createUrlNodeFromDatum(datum, index);
            self.updateLastModified(datum);
            self.updateLookups(datum, node);

            return node;
        });

        return this.generateXmlFromNodes(nodes);
    },

    generateXmlFromNodes: function () {
        var self = this,
            // Get a mapping of node to timestamp
            timedNodes = _.map(this.nodeLookup, function (node, id) {
                return {
                    id: id,
                    // Using negative here to sort newest to oldest
                    ts: -(self.nodeTimeLookup[id] || 0),
                    node: node
                };
            }, []),
            // Sort nodes by timestamp
            sortedNodes = _.sortBy(timedNodes, 'ts'),
            // Grab just the nodes
            urlElements = _.pluck(sortedNodes, 'node'),
            data = {
                // Concat the elements to the _attr declaration
                urlset: [XMLNS_DECLS].concat(urlElements)
            };

        // Return the xml
        return utils.getDeclarations() + xml(data);
    },

    updateXmlFromNodes: function (urlElements) {
        var content = this.generateXmlFromNodes(urlElements);

        this.setSiteMapContent(content);

        return content;
    },

    addOrUpdateUrl: function (model) {
        var datum = model.toJSON(),
            node = this.createUrlNodeFromDatum(datum);

        this.updateLastModified(datum);
        // TODO: Check if the node values changed, and if not don't regenerate
        this.updateLookups(datum, node);

        return this.updateXmlFromNodes();
    },

    removeUrl: function (model) {
        var datum = model.toJSON();
        // When the model is destroyed we need to fetch previousAttributes
        if (!datum.id) {
            datum = model.previousAttributes();
        }
        this.removeFromLookups(datum);

        this.lastModified = Date.now();

        return this.updateXmlFromNodes();
    },

    getUrlForDatum: function () {
        return config.urlFor('home', true);
    },

    getUrlForImage: function (image) {
        return config.urlFor('image', {image: image}, true);
    },

    getPriorityForDatum: function () {
        return 1.0;
    },

    getLastModifiedForDatum: function (datum) {
        return datum.updated_at || datum.published_at || datum.created_at;
    },

    createUrlNodeFromDatum: function (datum) {
        var url = this.getUrlForDatum(datum),
            priority = this.getPriorityForDatum(datum),
            node,
            imgNode;

        node = {
            url: [
                {loc: url},
                {lastmod: moment(this.getLastModifiedForDatum(datum)).toISOString()},
                {changefreq: CHANGE_FREQ},
                {priority: priority}
            ]
        };

        imgNode = this.createImageNodeFromDatum(datum);

        if (imgNode) {
            node.url.push(imgNode);
        }

        return node;
    },

    createImageNodeFromDatum: function (datum) {
        // Check for cover first because user has cover but the rest only have image
        var image = datum.cover || datum.image,
            imageUrl,
            imageEl;

        if (!image) {
            return;
        }

        // Grab the image url
        imageUrl = this.getUrlForImage(image);

        // Verify the url structure
        if (!this.validateImageUrl(imageUrl)) {
            return;
        }

        // Create the weird xml node syntax structure that is expected
        imageEl = [
            {'image:loc': imageUrl},
            {'image:caption': path.basename(imageUrl)}
        ];

        // Return the node to be added to the url xml node
        return {
            'image:image': imageEl
        };
    },

    validateImageUrl: function (imageUrl) {
        return !!imageUrl;
    },

    setSiteMapContent: function (content) {
        this.siteMapContent = content;
    },

    updateLastModified: function (datum) {
        var lastModified = this.getLastModifiedForDatum(datum);

        if (lastModified > this.lastModified) {
            this.lastModified = lastModified;
        }
    },

    updateLookups: function (datum, node) {
        this.nodeLookup[datum.id] = node;
        this.nodeTimeLookup[datum.id] = this.getLastModifiedForDatum(datum);
    },

    removeFromLookups: function (datum) {
        var lookup = this.nodeLookup;
        delete lookup[datum.id];

        lookup = this.nodeTimeLookup;
        delete lookup[datum.id];
    }
});

module.exports = BaseSiteMapGenerator;

@@ -1627,23 +1627,16 @@
 n (datum
-, index
 ) %7B%0A    
@@ -1691,15 +1691,8 @@
 atum
-, index
 );%0A 

remove attempt to index sitemap nodes