var Fastly = require('fastly');

/*
 * Fastly library extended to allow configuration for a particular service
 * and some helper methods.
 *
 * @param {string} API key
 * @param {string} Service id
 */
module.exports = function (apiKey, serviceId) {
    var fastly = Fastly(apiKey);
    fastly.serviceId = serviceId;

    /*
     * Helper method for constructing Fastly API urls
     *
     * @param {string} Service id
     * @param {number} Version
     *
     * @return {string}
     */
    fastly.getFastlyAPIPrefix = function (servId, version) {
        return '/service/' + encodeURIComponent(servId) + '/version/' + version;
    };

    /*
     * getLatestActiveVersion: Get the most recent version for the configured service
     *
     * @param {callback} Callback with signature *err, latestVersion)
     */
    fastly.getLatestActiveVersion = function (cb) {
        if (!this.serviceId) {
            return cb('Failed to get latest version. No serviceId configured');
        }
        var url = '/service/' + encodeURIComponent(this.serviceId) + '/version';
        this.request('GET', url, function (err, versions) {
            if (err) {
                return cb('Failed to fetch versions: ' + err);
            }
            var latestVersion = versions.reduce((latestActiveSoFar, cur) => {
                // if one of [latestActiveSoFar, cur] is active and the other isn't,
                // return whichever is active. If both are not active, return
                // latestActiveSoFar.
                if (!cur || !cur.active) return latestActiveSoFar;
                if (!latestActiveSoFar || !latestActiveSoFar.active) return cur;
                // when both are active, prefer whichever has a higher version number.
                return (cur.number > latestActiveSoFar.number) ? cur : latestActiveSoFar;
            }, null);
            return cb(null, latestVersion);
        });
    };

    /*
     * setCondition: Upsert a Fastly condition entry
     * Attempts to PUT and POSTs if the PUT request is a 404
     *
     * @param {number} Version number
     * @param {object} Condition object sent to the API
     * @param {callback} Callback for fastly.request
     */
    fastly.setCondition = function (version, condition, cb) {
        if (!this.serviceId) {
            return cb('Failed to set condition. No serviceId configured');
        }
        var name = condition.name;
        var putUrl = this.getFastlyAPIPrefix(this.serviceId, version) + '/condition/' + encodeURIComponent(name);
        var postUrl = this.getFastlyAPIPrefix(this.serviceId, version) + '/condition';
        return this.request('PUT', putUrl, condition, function (err, response) {
            if (err && err.statusCode === 404) {
                this.request('POST', postUrl, condition, function (e, resp) {
                    if (e) {
                        return cb('Failed while inserting condition "' + condition.statement + '": ' + e);
                    }
                    return cb(null, resp);
                });
                return;
            }
            if (err) {
                return cb('Failed to update condition "' + condition.statement + '": ' + err);
            }
            return cb(null, response);
        }.bind(this));
    };

    /*
     * setFastlyHeader: Upsert a Fastly header entry
     * Attempts to PUT and POSTs if the PUT request is a 404
     *
     * @param {number} Version number
     * @param {object} Header object sent to the API
     * @param {callback} Callback for fastly.request
     */
    fastly.setFastlyHeader = function (version, header, cb) {
        if (!this.serviceId) {
            cb('Failed to set header. No serviceId configured');
        }
        var name = header.name;
        var putUrl = this.getFastlyAPIPrefix(this.serviceId, version) + '/header/' + encodeURIComponent(name);
        var postUrl = this.getFastlyAPIPrefix(this.serviceId, version) + '/header';
        return this.request('PUT', putUrl, header, function (err, response) {
            if (err && err.statusCode === 404) {
                this.request('POST', postUrl, header, function (e, resp) {
                    if (e) {
                        return cb('Failed to insert header: ' + e);
                    }
                    return cb(null, resp);
                });
                return;
            }
            if (err) {
                return cb('Failed to update header: ' + err);
            }
            return cb(null, response);
        }.bind(this));
    };

    /*
     * setResponseObject: Upsert a Fastly response object
     * Attempts to PUT and POSTs if the PUT request is a 404
     *
     * @param {number} Version number
     * @param {object} Response object sent to the API
     * @param {callback} Callback for fastly.request
     */
    fastly.setResponseObject = function (version, responseObj, cb) {
        if (!this.serviceId) {
            cb('Failed to set response object. No serviceId configured');
        }
        var name = responseObj.name;
        var putUrl = this.getFastlyAPIPrefix(this.serviceId, version) + '/response_object/' + encodeURIComponent(name);
        var postUrl = this.getFastlyAPIPrefix(this.serviceId, version) + '/response_object';
        return this.request('PUT', putUrl, responseObj, function (err, response) {
            if (err && err.statusCode === 404) {
                this.request('POST', postUrl, responseObj, function (e, resp) {
                    if (e) {
                        return cb('Failed to insert response object: ' + e);
                    }
                    return cb(null, resp);
                });
                return;
            }
            if (err) {
                return cb('Failed to update response object: ' + err);
            }
            return cb(null, response);
        }.bind(this));
    };

    /*
     * cloneVersion: Clone a version to create a new version
     *
     * @param {number} Version to clone
     * @param {callback} Callback for fastly.request
     */
    fastly.cloneVersion = function (version, cb) {
        if (!this.serviceId) return cb('Failed to clone version. No serviceId configured.');
        var url = this.getFastlyAPIPrefix(this.serviceId, version) + '/clone';
        this.request('PUT', url, cb);
    };

    /*
     * activateVersion: Activate a version
     *
     * @param {number} Version number
     * @param {callback} Callback for fastly.request
     */
    fastly.activateVersion = function (version, cb) {
        if (!this.serviceId) return cb('Failed to activate version. No serviceId configured.');
        var url = this.getFastlyAPIPrefix(this.serviceId, version) + '/activate';
        this.request('PUT', url, cb);
    };

    /*
     * Upsert a custom vcl file. Attempts a PUT, and falls back
     * to POST if not there already.
     *
     * @param {number}   version current version number for fastly service
     * @param {string}   name    name of the custom vcl file to be upserted
     * @param {string}   vcl     stringified custom vcl to be uploaded
     * @param {Function} cb      function that takes in two args: err, response
     */
    fastly.setCustomVCL = function (version, name, vcl, cb) {
        if (!this.serviceId) {
            return cb('Failed to set response object. No serviceId configured');
        }

        var url = this.getFastlyAPIPrefix(this.serviceId, version) + '/vcl/' + name;
        var postUrl = this.getFastlyAPIPrefix(this.serviceId, version) + '/vcl';
        var content = {content: vcl};
        return this.request('PUT', url, content, function (err, response) {
            if (err && err.statusCode === 404) {
                content.name = name;
                this.request('POST', postUrl, content, function (e, resp) {
                    if (e) {
                        return cb('Failed while adding custom vcl "' + name + '": ' + e);
                    }
                    return cb(null, resp);
                });
                return;
            }
            if (err) {
                return cb('Failed to update custom vcl "' + name + '": ' + err);
            }
            return cb(null, response);
        }.bind(this));
    };

    return fastly;
};