/* eslint-disable no-console */
const test = require('tape');
const path = require('path');
const fs = require('fs-extra');
const { request, Agent } = require('http');
const async = require('async');
const _ = require('lodash');
const { fork } = require('child_process');
const oboe = require('oboe');

const keepAliveAgent = new Agent({ keepAlive: true });

const SAMPLES_DIR = path.resolve(__dirname, '../samples/exiftool');
const TMP_DIR = path.resolve(__dirname, '../../temp');
const UPLOADS_DIR = path.resolve(__dirname, '../../temp/uploads');

let serverProcess;
let port;
let size = 0;
let expectedSize = 0;
const samples = [];

test('setup', (t) => {
    serverProcess = fork(path.resolve(__dirname, '../util/server-fork.js'));
    serverProcess.on('message', (msg) => {
        if (msg.event === 'listening') {
            port = msg.port;
            t.end();
        }
    });
    serverProcess.send({
        action: 'start',
        tmpDir: TMP_DIR,
        uploadsDir: UPLOADS_DIR,
        omitPlugins: [
            'fastTransform',
            'slowTransform',
        ],
    });
});

test('collect samples', (t) => {
    fs.walk(SAMPLES_DIR)
        .on('data', (item) => {
            if (item.stats.isDirectory()) return;
            samples.push(item.path);
            expectedSize += fs.statSync(item.path).size;
        })
        .once('end', () => {
            t.pass(`${samples.length} sample images found`);
            t.pass(`total size ${(expectedSize / 1024 / 1024).toFixed(2)} Mb`);
            t.end();
        })
        .once('error', (err) => {
            throw err;
        });
});

function uploadSeries(files, callback) {
    const boundary = '96187754517167571559282392224318';
    const rq = request({
        method: 'post',
        port,
        headers: { 'content-type': `multipart/form-data; boundary=${boundary}` },
        agent: keepAliveAgent,
    }, (res) => {
        if (res.statusCode !== 200) {
            throw new Error(`HTTP ${res.statusCode} ${res.statusMessage}\n`);
        }
        oboe(res)
            .node('!*', (item) => {
                size += item.size;
            })
            .done(callback);
    });

    async.eachSeries(files, (filePath, next) => {
        const file = fs.createReadStream(filePath);
        let needDrain = false;
        rq.write([
            `--${boundary}`,
            `Content-Disposition: form-data; name="file"; filename=${filePath}`,
            'Content-Type: image/jpeg',
            '',
        ].join('\r\n').concat('\r\n'));
        const onData = (data) => {
            needDrain = !rq.write(data);
            if (needDrain) {
                file.removeListener('data', onData);
                rq.once('drain', () => {
                    needDrain = false;
                    file.on('data', onData);
                });
            }
        };
        file.on('data', onData);
        file.once('end', () => {
            rq.write('\r\n');
            if (needDrain) {
                rq.once('drain', () => {
                    next();
                });
            } else {
                next();
            }
        });
    }, () => {
        rq.write(`--${boundary}--\r\n`);
        rq.end();
    });
}

test('upload files', (t) => {
    const LIMIT = Infinity;
    const FILES_PER_REQUEST = LIMIT;
    const CONCURRENCY = 1;
    const OFFSET = 0;
    const slice = samples.slice(OFFSET, OFFSET + Math.min(LIMIT, samples.length - OFFSET));
    const chunks = _.chunk(slice, FILES_PER_REQUEST);

    const timeStart = Date.now();

    async.eachLimit(chunks, CONCURRENCY, uploadSeries, () => {
        t.equal(size, expectedSize, 'total size match');
        t.pass(`done in ${Date.now() - timeStart} ms`);

        serverProcess.removeAllListeners('message');
        t.end();
    });
});

test('test uploaded files', (t) => {
    let uploadsSize = 0;
    let counter = 0;
    fs.walk(UPLOADS_DIR)
        .on('data', (item) => {
            if (item.stats.isDirectory()) return;
            uploadsSize += fs.statSync(item.path).size;
            counter++;
        })
        .once('end', () => {
            t.equal(counter, samples.length, 'uploaded files count size match');
            t.equal(uploadsSize, expectedSize, 'total size match');
            t.end();
        })
        .once('error', (err) => {
            throw err;
        });
});

test('cleanup', (t) => {
    async.series([
        cb => fs.remove(UPLOADS_DIR, cb),
        cb => fs.remove(TMP_DIR, cb),
    ], (err) => {
        if (err) throw err;
        t.pass('done');
        t.end();
    });
});

test('teardown', (t) => {
    keepAliveAgent.destroy();
    serverProcess.send({
        action: 'stop',
    });
    process.nextTick(() => {
        t.pass('done');
        t.end();
    });
});

@@ -261,24 +261,63 @@
 _process');%0A
+const FormData = require('form-data');%0A
 const oboe =
@@ -334,16 +334,16 @@
 oboe');%0A
-
 %0Aconst k
@@ -1709,53 +1709,29 @@
 nst 
-boundary = '96187754517167571559282392224318'
+form = new FormData()
 ;%0A  
@@ -1812,71 +1812,25 @@
 rs: 
-%7B 'content-type': %60multipart/form-data; boundary=$%7Bboundary%7D%60 %7D
+form.getHeaders()
 ,%0A  
@@ -2147,1059 +2147,107 @@
 %7D);%0A
-%0A
     
-async.eachSeries(files, (filePath, next) =%3E %7B%0A        const file = fs.createReadStream(filePath);%0A        let needDrain
+files.forEach(filePath
  =
+%3E
  f
-alse;%0A        rq.write(%5B%0A            %60--$%7Bboundary%7D%60,%0A            %60Content-Disposition: form-data; name=%22file%22; filename=$%7BfilePath%7D%60,%0A            'Content-Type: image/jpeg',%0A            '',%0A        %5D.join('%5Cr%5Cn').concat('%5Cr%5Cn'));%0A        const onData = (data) =%3E %7B%0A            needDrain = !rq.write(data);%0A            if (needDrain) %7B%0A                file.removeListener('data', onData);%0A                rq.once('drain', () =%3E %7B%0A                    needDrain = false;%0A                    file.on('data', onData);%0A                %7D);%0A            %7D%0A        %7D;%0A        file.on('data', onData);%0A        file.once('end', () =%3E %7B%0A            rq.write('%5Cr%5Cn');%0A            if (needDrain) %7B%0A                rq.once('drain', () =%3E %7B%0A                    next();%0A                %7D);%0A            %7D else %7B%0A                next();%0A            %7D%0A        %7D);%0A    %7D, () =%3E %7B%0A        rq.write(%60--$%7Bboundary%7D--%5Cr%5Cn%60);%0A        rq.end();%0A    %7D
+orm.append('file', fs.createReadStream(filePath)));%0A    form.pipe(rq
 );%0A%7D

test refactoring