// LICENSE_CODE ZON
'use strict'; /*zlint node*/
const zconf = require('./config.js');
const conf = require('./conf.js');
const path = require('path');
const slow = require('./slow.js');
const zutil = require('./util.js');
const zerr = require('./zerr.js');
const etask = require('./etask.js');
const agentkeepalive = require('agentkeepalive');
const request = require('request');
const E = module.exports = wget;
const assign = Object.assign;

let opt_parse = opt=>{
    opt = zutil.clone(opt);
    if (opt.expect!==undefined && !Array.isArray(opt.expect))
        opt.expect = [opt.expect];
    if (opt.keepalive===undefined && zutil.is_mocha())
        opt.keepalive = false;
    if (opt.keepalive!=false)
    {
        let is_https = opt.url&&opt.url.startsWith('https://');
        opt.agentClass = is_https ? agentkeepalive.HttpsAgent : agentkeepalive;
        opt.agentOptions = assign({maxKeepAliveTime: 10000},
            opt.maxSockets ? {maxSockets: opt.maxSockets} : {});
    }
    if (opt.url!==undefined && !/^\w+:\/\//.test(opt.url))
        opt.url = 'http://'+opt.url; // default to http
    if (opt.pool===undefined && opt.maxSockets===undefined)
    {
        if (opt.keepalive!=false)
            opt.maxSockets = 1024;
        else
            opt.pool = false;
    }
    if (opt.timeout===undefined)
        opt.timeout = 60000;
    if (opt.slow===undefined)
        opt.slow = 1000;
    if (opt.method===undefined)
        opt.method = 'GET';
    if (opt.retry===undefined)
        opt.retry = 1;
    opt.headers = opt.headers ? zutil.clone(opt.headers) : {};
    if (conf.app && !('user-agent' in opt.headers)
        && !('User-Agent' in opt.headers))
    {
        opt.headers['User-Agent'] = `Hola ${conf.app}/${zconf.ZON_VERSION}`;
    }
    return opt;
};

// wget(opt)
// wget(url [,opt])
// Opts are as per https://github.com/request/request, plus:
// - expect: integer or array of integers -- raise an error if statusCode does
//   not match. Defaults to not to check the status code.
// - slow: number -- warn if request is not finished for N ms.
//   Defaults to 1s.
// - retry: number -- number of retries on connection failure
// - retry_interval: number -- wait N ms between retries
// Defaults changed:
// - pool: defaults to false
// - timeout: defaults to 60sec
// - headers['User-Agent']: defaults to `Hola ${conf.app}/${ZON_VERSION}`
function wget(url, opt){
    opt = opt||{};
    if (typeof url=='object')
        opt = url;
    else if (typeof url=='string')
    {
        opt = zutil.clone(opt);
        opt.url = url;
    }
    else
        throw 'invalid args';
    return E._wget(opt);
}

E._wget = opt=>etask({cancel: opt.cancel}, function*wget(){
    opt = opt_parse(opt);
    let url_s = zerr.json(opt.url).slice(0, 256), attempt = 0;
    this.info.method = opt.method;
    this.info.url = url_s;
    do {
        let req = {};
        try {
            zerr.debug('wget '+url_s);
            if (opt.slow)
            {
                slow.etask(this, assign({timeout: opt.slow},
                    slow.slow_opt('wget', ()=>'wget '+url_s.slice(0, 128))));
            }
            attempt++;
            if (attempt>1)
                this.info.attempt = `(attempt ${attempt})`;
            let r = yield etask.nfn_apply({ret_sync: [req, 'req']}, request,
                [opt]);
            zerr.debug('wget ok '+r.statusCode+' '+url_s);
            if (opt.expect!==undefined && !opt.expect.includes(r.statusCode))
            {
                throw new Error(`Unexpected status code: ${r.statusCode} `
                    +`(${r.statusMessage})`);
            }
            return {resp: r, body: r.body};
        } catch(e){
            (opt.quiet ? zerr.debug : zerr)('wget err '+e+' '+url_s);
            if (req&&req.req)
                req.req.abort();
            if (attempt>=opt.retry)
                throw e;
            if (!opt.quiet)
            {
                zerr.warn('wget failed '+url_s+', will retry '
                    +(opt.retry-attempt)+' more times');
            }
            if (opt.retry_interval)
                yield etask.sleep(opt.retry_interval);
        } finally { // works only on node>=6.x.x
            // XXX romank: check if we need socket destroy on non-keep-alive
            // mode.
            if (req&&req.req)
            {
                try { req.req.end(); }
                catch(e){
                    zerr.err(`Failed req.end. Opt: ${JSON.stringify(opt)}.`
                        +zerr.e2s(e));
                }
            }
        }
    } while (attempt<opt.retry);
});

E.stream = opt=>request(opt_parse(opt));
