var NewDashboard = function() {
    var _this = this;
    var ga = {};
    var cache = "client"; //client, false
    var userId = null;
    var subSiteId = null;
    var ready = new signals.Signal();
    var rtTTL = 45;
    var gaTTL = 84600;
    var gaShortTTL = 3600;

    var configure = function(ids) {
        ga = Object.assign(ga, {ids: ids});
        ready.dispatch(NewDashboard);
    };

    var validateInstance = function() {
        if (!ga.ids || !userId) {
            throw "Profile ids and user id must be set properly. Maybe not call init before?"
        }
    };

    var handleGaTTL = function (params) {
        var startDate = params['start-date'];
        var endDate   = params['end-date'];

        if (
            (startDate === "today" && endDate === "today") ||
            (startDate === "yesterday" && endDate === "today") ||
            (startDate === "yesterday" && endDate === "yesterday") ||
            (startDate === "1daysAgo" && endDate === "today") ||
            (startDate === "1daysAgo" && endDate === "1daysAgo")
        ) {
            return gaShortTTL;
        }

        return gaTTL;
    };

    var handleRtTTL = function (params) {
        return rtTTL;
    };

    var subSiteFilter = function(add) {
        var c = 'ga:dimension13==' + subSiteId;

        if (add === true) {
            return ',' + c;
        }

        return c;
    };

    var gaCall = function(params, cacheTTL) {

        validateInstance();

        params = Object.assign(params, { quotaUser: userId });

        return new Promise(function(resolve, reject){

            var cacheKey = generateKey(params);

            fromCache(cacheKey, params)
                .then(function(response){
                    if (!response) {
                        gapi.client.analytics.data.ga.get(params)
                            .then(function(response) {
                                saveInCache(cacheKey, response, (cacheTTL || handleGaTTL(params)))
                                    .then(function(response) {
                                        resolve(response.result);
                                    })
                            })
                            .then(null, function(err) {
                                reject(err);
                            });
                    } else {
                        resolve(response.result);
                    }
                })
                .catch(function(error) {});
        });
    };

    var rtCall = function(params, cacheTTL) {

        validateInstance();

        params = Object.assign(params, { quotaUser: userId });

        return new Promise(function(resolve, reject){

            var cacheKey = generateKey(params);

            fromCache(cacheKey, params)
                .then(function(response){
                    if (!response) {
                        gapi.client.analytics.data.realtime.get(params)
                            .then(function(response) {
                                saveInCache(cacheKey, response, (cacheTTL || handleRtTTL(params)))
                                    .then(function(response) {
                                        resolve(response.result);
                                    })
                            })
                            .then(null, function(err) {
                                reject(err);
                            });
                    } else {
                        resolve(response.result);
                    }
                })
                .catch(function(error) {});
        });
    };

    var fromCache = function(cacheKey) {
        return new Promise(function(resolve, reject){
            if (cache === false) {
                resolve(null);
            } else if (cache === "client") {
                var cacheData = localStorage.getItem(cacheKey);

                if (cacheData) {
                    var data = JSON.parse(cacheData);

                    // Check if should be expires
                    if (data.timestamp < getTime()) {
                        localStorage.removeItem(cacheKey);
                        resolve(null);
                    } else {
                        resolve(data);
                    }
                } else {
                    resolve(null);
                }
            } else {
                $.ajax({
                    url: Routing.generate("cs_common_base_dashboard_cache_fetch"),
                    data: {key: cacheKey},
                    dataType: "json",
                    success: function(response) {
                        if (response.data) {
                            resolve(JSON.parse(response.data));
                        } else {
                            resolve(null);
                        }
                    }
                });
            }
        });
    };

    var getTime = function(increment, incrementType) {
        var now = increment && incrementType ? moment().add(increment, incrementType) : moment();
        return now._d.getTime();
    };

    var saveInCache = function(cacheKey, data, cacheTTL) {
        return new Promise(function(resolve, reject){
            if (cache === "server") {
                $.ajax({
                    url: Routing.generate("cs_common_base_dashboard_cache_persist"),
                    data: {
                        data: JSON.stringify(data),
                        key: cacheKey,
                        ttl: cacheTTL
                    },
                    dataType: "json",
                    method: "post"
                });

            } else if (cache === "client") {
                localStorage.setItem(cacheKey, JSON.stringify(setCacheDate(data, cacheTTL)));
            }

            resolve(data);
        });
    };

    var setCacheDate = function(data, cacheTTL) {
        return Object.assign(data, { timestamp : getTime(cacheTTL, "seconds")})
    };

    var generateKey = function(params) {
        return JSON.stringify(params)
            .split("")
            .reduce(function(a,b){
                a = ((a<<5)-a)+b.charCodeAt(0);
                return a&a
            }, 0);
    };

    var executeFromCache = function(key, onSuccess, onEmpty){
        $.ajax({
            url: Routing.generate("cs_common_base_dashboard_cache_fetch"),
            data: {key:key},
            dataType: "json",
            success: function(response) {
                if (response.data) {
                    onSuccess.call(this, response.data);
                } else {
                    onEmpty.call();
                }
            }
        });
    };

    var persistInCache = function(key, data, ttl) {
        $.ajax({
            url: Routing.generate("cs_common_base_dashboard_cache_persist"),
            data: {data: data, key:key, ttl: ttl},
            dataType: "json",
            method: "post"
        });
    };

    var parseTerm = function(term) {
        return term.replace(/(\,|\;|\'|:)/g,"\\$1");
    };

    return {
        init: function(accessToken, userIdParam, subSiteParam, callBack) {
            GoogleAnalyticsEmbedAPI.initStatistics('add', configure, _this);
            GoogleAnalyticsEmbedAPI.init(accessToken);
            userId    = userIdParam;
            subSiteId = subSiteParam;
            ready.add(callBack);
        },
        realTimeActiveUsers: function() {
            return rtCall({
                "ids": ga.ids,
                "metrics": "rt:activeUsers"
            });
        },
        realTimePageWithTraffic: function() {
            return rtCall({
                "ids": ga.ids,
                "metrics": "rt:activeUsers",
                "dimensions": "ga:pagePath",
                "sort": "-rt:activeUsers",
                "max-results": 10
            });
        },
        realTimeTrafficOrigin: function() {
            return rtCall({
                'ids': ga.ids,
                "metrics": "rt:activeUsers",
                "dimensions": "ga:source,rt:medium",
                "sort": "-rt:activeUsers"
            });
        },
        realTimeDevices: function() {
            return rtCall({
                'ids': ga.ids,
                "metrics": "rt:activeUsers",
                "dimensions": "ga:deviceCategory",
                "sort": "-rt:activeUsers"
            });
        },
        gaActiveUsers: function(startDate, endDate) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                'metrics': 'ga:users'
            });
        },

         gaActiveSessions: function(startDate, endDate) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                'metrics': 'ga:sessions'
            });
        },
        gaPageViews: function(startDate, endDate) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                'metrics': 'ga:pageViews',
                'dimensions': 'ga:date'
            });
        },
        gaAvgTimeOnPage: function(startDate, endDate) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                "metrics": "ga:avgTimeOnPage",
                "dimensions": "ga:date" //ga:pagePath
            });
        },
        gaAvgSessionDuration: function(startDate, endDate) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                "metrics": "ga:avgSessionDuration",
                "dimensions": "ga:date" //ga:pagePath
            });
        },
        gaPageViewGraphForRealTime: function(startDate, endDate) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                "metrics": "ga:pageViews",
                "dimensions": "ga:date,ga:hour"
            });
        },
        gaPageViewGraph: function(startDate, endDate, metric) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                "metrics": metric || "ga:pageViews",
                "dimensions": "ga:date"
            });
        },
        gaTrafficOrigin: function(startDate, endDate) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                "metrics": "ga:sessions",
                "dimensions": "ga:channelGrouping",
                "max-results": 5,
                "sort": "-ga:sessions"
            });
        },
        gaDevices: function(startDate, endDate) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                "metrics": "ga:sessions",
                "dimensions": "ga:deviceCategory"
            });
        },
        gaTotalUsers: function(startDate, endDate) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                "metrics": "ga:users"
            });
        },
        gaTotalSessions: function(startDate, endDate) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                'dimensions': 'ga:date',
                'metrics': 'ga:sessions'
            });
        },
        gaBounceRate: function(startDate, endDate) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                "metrics": "ga:bounceRate"
            });
        },
        gaNewSessions: function(startDate, endDate) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                "metrics": "ga:percentNewSessions"
            });
        },
        gaSessionsOrigin: function(startDate, endDate) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                "metrics": "ga:sessions",
                "dimensions": "ga:userType"
            });
        },
        gaUserGender: function(startDate, endDate) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                "metrics": "ga:users",
                "dimensions": "ga:userGender"
            });
        },
        gaUserAgeBucket: function(startDate, endDate) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                "metrics": "ga:users",
                "dimensions": "ga:userAgeBracket"
            });
        },
        gaCities: function(startDate, endDate) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                "metrics": "ga:users",
                "dimensions": "ga:city,ga:region",
                "sort": "-ga:users",
                "max-results": 10
            });
        },
        gaAccessFrequency: function(startDate, endDate) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                "metrics": "ga:sessions, ga:pageviews",
                "dimensions": "ga:sessionCount",
                'sort': 'ga:sessionCount',
                "max-results": 20
            });
        },
        gaInteraction: function(startDate, endDate) {
            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                "metrics": "ga:sessions, ga:pageviews",
                "dimensions": "ga:sessionDurationBucket"
            });
        },
        gaContent: function(startDate, page, dimensions, term, limit, endDate) {

            var filters = "ga:dimension3==102,ga:dimension13==" + subSiteId;

            if (term) {
                term = parseTerm(term);
                filters += ";ga:pageTitle=@"+ term;

                if (isFinite(term)) {
                    filters += ",ga:dimension6=="+ term;
                }
            }

            return gaCall({
                'ids': ga.ids,
                'start-date': startDate,
                'end-date': endDate || 'today',
                "metrics": "ga:pageviews, ga:metric1,ga:avgTimeOnPage,ga:metric2,ga:metric3, ga:bounceRate,ga:metric5",
                "dimensions": dimensions,
                "filters": filters,
                "start-index": page > 1 ? ((page - 1) * limit) + 1 : page,
                "max-results": limit,
                "sort" : "-ga:pageviews"
            });
        },
        ga: ga,
        setCache: function(useCache) {
            cache = useCache;
        },
        setRtTTL: function (ttl) {
            rtTTL = ttl;
        },
        setGaTTL: function (ttl) {
            gaTTL = ttl;
        },
        setShortGaTTL: function (ttl) {
            gaShortTTL = ttl;
        }
    };
}();
