'use strict';

const dateWrapper = require('./date-wrapper');
const querystring = require('querystring');

module.exports = class LayergroupMetadata {
    constructor (resourceLocator) {
        this.resourceLocator = resourceLocator;
    }

    // TODO this should take into account several URL patterns
    addDataviewsAndWidgetsUrls (username, layergroup, mapConfig) {
        this._addDataviewsUrls(username, layergroup, mapConfig);
        this._addWidgetsUrl(username, layergroup, mapConfig);
    }

    _addDataviewsUrls (username, layergroup, mapConfig) {
        layergroup.metadata.dataviews = layergroup.metadata.dataviews || {};
        var dataviews = mapConfig.dataviews || {};

        Object.keys(dataviews).forEach((dataviewName) => {
            var resource = layergroup.layergroupid + '/dataview/' + dataviewName;
            layergroup.metadata.dataviews[dataviewName] = {
                url: this.resourceLocator.getUrls(username, resource)
            };
        });
    }

    _addWidgetsUrl (username, layergroup, mapConfig) {
        if (layergroup.metadata && Array.isArray(layergroup.metadata.layers) && Array.isArray(mapConfig.layers)) {
            layergroup.metadata.layers = layergroup.metadata.layers.map((layer, layerIndex) => {
                var mapConfigLayer = mapConfig.layers[layerIndex];
                if (mapConfigLayer.options && mapConfigLayer.options.widgets) {
                    layer.widgets = layer.widgets || {};
                    Object.keys(mapConfigLayer.options.widgets).forEach((widgetName) => {
                        var resource = layergroup.layergroupid + '/' + layerIndex + '/widget/' + widgetName;
                        layer.widgets[widgetName] = {
                            type: mapConfigLayer.options.widgets[widgetName].type,
                            url: this.resourceLocator.getUrls(username, resource)
                        };
                    });
                }

                return layer;
            });
        }
    }

    addAnalysesMetadata (username, layergroup, analysesResults, includeQuery) {
        includeQuery = includeQuery || false;
        analysesResults = analysesResults || [];
        layergroup.metadata.analyses = [];

        analysesResults.forEach((analysis) => {
            var nodes = analysis.getNodes();
            layergroup.metadata.analyses.push({
                nodes: nodes.reduce((nodesIdMap, node) => {
                    if (node.params.id) {
                        var nodeResource = layergroup.layergroupid + '/analysis/node/' + node.id();
                        var nodeRepr = {
                            status: node.getStatus(),
                            url: this.resourceLocator.getUrls(username, nodeResource)
                        };
                        if (includeQuery) {
                            nodeRepr.query = node.getQuery();
                        }
                        if (node.getStatus() === 'failed') {
                            nodeRepr.error_message = node.getErrorMessage();
                        }
                        nodesIdMap[node.params.id] = nodeRepr;
                    }

                    return nodesIdMap;
                }, {})
            });
        });
    }

    addAggregationContextMetadata (layergroup, mapConfig, context) {
        if (layergroup.metadata && Array.isArray(layergroup.metadata.layers) && Array.isArray(mapConfig.layers)) {
            layergroup.metadata.layers = layergroup.metadata.layers.map(function(layer, layerIndex) {
                if (context.aggregation && Array.isArray(context.aggregation.layers)) {
                    layer.meta.aggregation = context.aggregation.layers[layerIndex];
                }
                return layer;
            });
        }
    }

    addTileJsonMetadata (layergroup, user, mapconfig, userApiKey = null) {
        const isVectorOnlyMapConfig = mapconfig.isVectorOnlyMapConfig();
        let hasMapnikLayers = false;
        const apiKey = userApiKey ? '?' + querystring.stringify({api_key: userApiKey}) : '';

        layergroup.metadata.layers.forEach((layerMetadata, index) => {
            const layerId = mapconfig.getLayerId(index);
            const rasterResource = `${layergroup.layergroupid}/${layerId}/{z}/{x}/{y}.png${apiKey}`;

            if (mapconfig.layerType(index) === 'mapnik') {
                hasMapnikLayers = true;
                const vectorResource = `${layergroup.layergroupid}/${layerId}/{z}/{x}/{y}.mvt${apiKey}`;
                const layerTilejson = {
                    vector: this._getTilejson(this.resourceLocator.getTileUrls(user, vectorResource))
                };
                if (!isVectorOnlyMapConfig) {
                    let grids = null;
                    const layer = mapconfig.getLayer(index);
                    if (layer.options.interactivity) {
                        const gridResource = `${layergroup.layergroupid}/${layerId}/{z}/{x}/{y}.grid.json${apiKey}`;
                        grids = this.resourceLocator.getTileUrls(user, gridResource);
                    }
                    layerTilejson.raster = this._getTilejson(
                        this.resourceLocator.getTileUrls(user, rasterResource),
                        grids
                    );
                }
                layerMetadata.tilejson = layerTilejson;
            } else {
                layerMetadata.tilejson = {
                    raster: this._getTilejson(this.resourceLocator.getTileUrls(user, rasterResource))
                };
            }
        });

        const tilejson = {};
        const url = {};

        if (hasMapnikLayers) {
            const vectorResource = `${layergroup.layergroupid}/{z}/{x}/{y}.mvt${apiKey}`;
            tilejson.vector = this._getTilejson(
                this.resourceLocator.getTileUrls(user, vectorResource)
            );
            url.vector = this._getTemplateUrl(this.resourceLocator.getTemplateUrls(user, vectorResource));

            if (!isVectorOnlyMapConfig) {
                const rasterResource = `${layergroup.layergroupid}/{z}/{x}/{y}.png${apiKey}`;
                tilejson.raster = this._getTilejson(
                    this.resourceLocator.getTileUrls(user, rasterResource)
                );
                url.raster = this._getTemplateUrl(this.resourceLocator.getTemplateUrls(user, rasterResource));
            }
        }

        layergroup.metadata.tilejson = tilejson;
        layergroup.metadata.url = url;
    }

    _getTilejson(tiles, grids) {
        const tilejson = {
            tilejson: '2.2.0',
            tiles: tiles.https || tiles.http
        };

        if (grids) {
            tilejson.grids = grids.https || grids.http;
        }

        return tilejson;
    }

    _getTemplateUrl(url) {
        return url.https || url.http;
    }

    addTurboCartoContextMetadata(layergroup, mapConfig, context) {
        if (layergroup.metadata && Array.isArray(layergroup.metadata.layers) && Array.isArray(mapConfig.layers)) {
            layergroup.metadata.layers = layergroup.metadata.layers.map(function(layer, layerIndex) {
                if (context.turboCarto && Array.isArray(context.turboCarto.layers)) {
                    layer.meta.cartocss_meta = context.turboCarto.layers[layerIndex];
                }
                return layer;
            });
        }
    }

    addDateWrappingMetadata (layergroup, mapConfig) {
        if (layergroup.metadata && Array.isArray(layergroup.metadata.layers) && Array.isArray(mapConfig.layers)) {
            layergroup.metadata.layers = layergroup.metadata.layers.map(function(layer, layerIndex) {
                const mapConfigLayer = mapConfig.layers[layerIndex];
                const layerOptions = mapConfigLayer.options;
                if (layerOptions.dates_as_numbers && layerOptions.sql) {
                    const wrappedColumns = dateWrapper.getColumnsWithWrappedDates(layerOptions.sql);
                    if (wrappedColumns) {
                        layer.meta.dates_as_numbers = wrappedColumns;
                    }
                }
                return layer;
            });
        }
    }

};
