// LICENSE_CODE ZON
'use strict'; /*jslint node:true*/
require('../../util/config.js');

const zurl = require('../../util/url.js');
const zerr = require('../../util/zerr.js');
const http = require('http');
const net = require('net');
const E = exports;

const PROXY_HOST = 'zproxy.lum-superproxy.io';
const PROXY_PORT = 22225;

let proxies = {};
E.get = (country, user, zone, password)=>{
    let hash = [country, user, zone, password].join('|');
    let proxy = proxies[hash]||(proxies[hash] = new LumProxy());
    // close other servers
    for (let h in proxies)
    {
        if (h==hash)
            continue;
        let server = proxies[h].server;
        delete proxies[h];
        server.close();
        setImmediate(function(){ server.emit('close'); });
    }
    proxy.init(country, user, zone, password);
    return proxy;
};

class LumProxy {
    constructor(){
        this.server = http.createServer();
        this.server._listen2('127.0.0.1', undefined, -1, -1);
        let addr = {};
        this.server._handle.getsockname(addr);
        this.port = addr.port;
        zerr.notice('Opened lum proxy port '+this.port);
        this.server.on('connect', this.onconnect.bind(this));
        this.server.on('request', this.onrequest.bind(this));
    }

    init(country, user, zone, password){
        this.country = country;
        this.user = user;
        this.zone = zone;
        this.password = password;
        this.session = 1000+Math.random()*89999;
        this.auth = 'Basic '+Buffer.from(`lum-customer-${this.user}-`
            +`zone-${this.zone}-country-${this.country}`
            +`-session-${this.session}:${this.password}`)
            .toString('base64');
    }
    // https
    onconnect(req, client_socket, head){
        let target = zurl.parse(req.url);
        let lum_socket = net.connect(PROXY_PORT, PROXY_HOST, ()=>{
            lum_socket.write(
                `CONNECT ${target.host}:${target.port} HTTP/1.1\r\n`
                        );
            lum_socket.write(`Proxy-Authorization: ${this.auth}\r\n\r\n`);
            lum_socket.write(head);
            lum_socket.pipe(client_socket);
            client_socket.pipe(lum_socket);
        });
    }
    // http
    onrequest(req, res){
        let target = zurl.parse(req.url);
        let opts = {
            method: req.method,
            host: PROXY_HOST,
            port: PROXY_PORT,
            path: req.url,
            headers: Object.assign({}, req.headers, {
                host: target.host,
                'proxy-authorization': this.auth,
            }),
        };
        let proxy_req = http.request(opts, proxy_res=>{
            let h_len = proxy_res.rawHeaders.length>>1;
            for (let i=0; i<h_len; i+=2)
            {
                res.setHeader(proxy_res.rawHeaders[i],
                    proxy_res.rawHeaders[i+1]);
            }
            res.statusCode = proxy_res.statusCode;
            proxy_res.pipe(res);
        });
        req.pipe(proxy_req);
        proxy_req.on('error', err=>zerr(err));
        req.on('close', ()=>{
            proxy_req.abort();
        });
        req.on('end', ()=>{
            proxy_req.end();
        });
        // if client closed socket unexpectedly
        res.on('close', ()=>proxy_req.abort());
    }
}
