"use strict";
var httpProxy = require("http-proxy");
var utils = require("./utils");
var proxyUtils = require("./proxy-utils");
var Immutable = require("immutable");
var Map = require("immutable").Map;
var List = require("immutable").List;
/**
 * Default options that are passed along to http-proxy
 */
var defaultHttpProxyOptions = Map({
    /**
     * This ensures targets are more likely to
     * accept each request
     */
    changeOrigin: true,
    /**
     * This handles redirects
     */
    autoRewrite: true,
    /**
     * This allows our self-signed certs to be used for development
     */
    secure: false,
    ws: true
});
var defaultCookieOptions = Map({
    stripDomain: true
});
var ProxyOption = Immutable.Record({
    route: "",
    target: "",
    rewriteRules: true,
    /**
     * Functions to be called on proxy request
     * with args [proxyReq, req, res, options]
     */
    proxyReq: List([]),
    /**
     * Functions to be called on proxy response
     * with args [proxyRes, req, res]
     */
    proxyRes: List([]),
    /**
     * Functions to be called on proxy response
     * with args [proxyReq, req, socket, options, head]
     */
    proxyReqWs: List([]),
    errHandler: undefined,
    url: Map({}),
    proxyOptions: Map(defaultHttpProxyOptions),
    cookies: Map(defaultCookieOptions),
    ws: false,
    middleware: List([]),
    reqHeaders: undefined
});
/**
 * @param {BrowserSync} bs
 * @param {String} scripts
 * @returns {*}
 */
module.exports = function createProxyServer(bs) {
    var opt = new ProxyOption().mergeDeep(bs.options.get("proxy"));
    var proxy = httpProxy.createProxyServer(opt
        .get("proxyOptions")
        .set("target", opt.get("target"))
        .toJS());
    var target = opt.get("target");
    var proxyReq = getProxyReqFunctions(opt.get("proxyReq"), opt, bs);
    var proxyRes = getProxyResFunctions(opt.get("proxyRes"), opt);
    var proxyResWs = opt.get("proxyReqWs");
    bs.options = bs.options.update("middleware", function (mw) {
        return mw.concat({
            id: "Browsersync Proxy",
            route: opt.get("route"),
            handle: function (req, res) {
                proxy.web(req, res, {
                    target: target
                });
            }
        });
    });
    var app = utils.getBaseApp(bs);
    /**
     * @type {*|{server, app}}
     */
    var browserSyncServer = utils.getServer(app, bs.options);
    browserSyncServer.proxy = proxy;
    if (opt.get("ws")) {
        // debug(`+ ws upgrade for: ${x.get("target")}`);
        browserSyncServer.server.on("upgrade", function (req, socket, head) {
            proxy.ws(req, socket, head);
        });
    }
    /**
     * Add any user provided functions for proxyReq, proxyReqWs and proxyRes
     */
    applyFns("proxyReq", proxyReq);
    applyFns("proxyRes", proxyRes);
    applyFns("proxyReqWs", proxyResWs);
    /**
     * Handle Proxy errors
     */
    proxy.on("error", function (err) {
        if (typeof opt.get("errHandler") === "function") {
            opt.get("errHandler").call(null, err);
        }
    });
    /**
     * Apply functions to proxy events
     * @param {string} name - the name of the http-proxy event
     * @param {Array} fns - functions to call on each event
     */
    function applyFns(name, fns) {
        if (!List.isList(fns))
            fns = [fns];
        proxy.on(name, function () {
            var args = arguments;
            fns.forEach(function (fn) {
                if (typeof fn === "function") {
                    fn.apply(null, args);
                }
            });
        });
    }
    return browserSyncServer;
};
/**
 * @param resFns
 * @returns {*}
 */
function getProxyResFunctions(resFns, opt) {
    if (opt.getIn(["cookies", "stripDomain"])) {
        return resFns.push(proxyUtils.checkCookies);
    }
    return resFns;
}
/**
 * @param reqFns
 * @returns {*}
 */
function getProxyReqFunctions(reqFns, opt, bs) {
    var reqHeaders = opt.getIn(["reqHeaders"]);
    if (!reqHeaders) {
        return reqFns;
    }
    /**
     * Back-compat for old `reqHeaders` option here a
     * function was given that returned an object
     * where key:value was header-name:header-value
     * This didn't really work as it clobbered all other headers,
     * but it remains for the unlucky few who used it.
     */
    if (typeof reqHeaders === "function") {
        var output = reqHeaders.call(bs, opt.toJS());
        if (Object.keys(output).length) {
            return reqFns.concat(function (proxyReq) {
                Object.keys(output).forEach(function (key) {
                    proxyReq.setHeader(key, output[key]);
                });
            });
        }
    }
    /**
     * Now, if {key:value} given, set the each header
     *
     * eg: reqHeaders: {
     *     'is-dev': 'true'
     * }
     */
    if (Map.isMap(reqHeaders)) {
        return reqFns.concat(function (proxyReq) {
            reqHeaders.forEach(function (value, key) {
                proxyReq.setHeader(key, value);
            });
        });
    }
    return reqFns;
}
//# sourceMappingURL=proxy-server.js.map