/* eslint global-require: 0 */
'use strict';

// Force NODE_ENV (and thus 'env' in express)
process.env.NODE_ENV = 'test';

const assert     = require('assert');
const fs         = require('fs');
const path       = require('path');
const htmlEncode = require('htmlencode').htmlEncode;
const request    = require('request');
const validator  = require('html-validator');
const yaml       = require('js-yaml');

let response = {};

// For array of types, the first one will be chosen when testing strictly
const CONTENT_TYPE_MAP = {
    css: 'text/css; charset=utf-8',
    js: 'application/javascript; charset=utf-8',

    eot: 'application/vnd.ms-fontobject',
    otf: 'application/x-font-otf',
    svg: 'image/svg+xml',
    ttf: [
        'application/x-font-ttf',
        'font/ttf'
    ],
    woff: 'application/font-woff',
    woff2: 'application/font-woff2',

    map: 'application/json; charset=utf-8'
};

function getExtension(str) {
    // use two enclosing parts; one for the dot (.)
    // and one for the extension itself.
    // So, the result we want is the third Array element,
    // since the first one is the whole match, the second one
    // returns the first captured match, etc.
    const re = /(\.)([a-zA-Z0-9]+)$/;

    return str.match(re)[2];
}

function assertContentType(uri, contentType) {
    const ext = getExtension(uri);
    let expectedType  = CONTENT_TYPE_MAP[ext];

    // Making TEST_STRICT=true default, pass TEST_STRICT=false to disable
    // strict checking.

    if (process.env.TEST_STRICT === 'false' && Array.isArray(expectedType)) {
        assert(contentType.includes(expectedType),
            `Invalid "content-type" for "${ext}", expects one of "${expectedType.join('", "')}" but got ${contentType}`);
    } else {
        expectedType = Array.isArray(expectedType) ? expectedType[0] : expectedType;

        assert.equal(contentType, expectedType,
            `Invalid "content-type" for "${ext}", expects "${expectedType}" but got "${contentType}"`);
    }
}

function getConfig() {
    const CONFIG_FILE = path.join(__dirname, '..', 'config', '_config.yml');

    return yaml.safeLoad(fs.readFileSync(CONFIG_FILE, 'utf8'));
}

function cleanEndpoint(endpoint = '/') {
    endpoint = endpoint.startsWith('/') ? endpoint : `/${endpoint}`;
    endpoint = endpoint.endsWith('/') ? endpoint : `${endpoint}/`;

    return endpoint;
}

function runApp(cfg, endpoint) {
    const endp = cleanEndpoint(endpoint);
    const port = cfg.port < 3000 ? cfg.port + 3000 : cfg.port + 1;

    // don't use configured port
    process.env.PORT = port;

    // load app
    require('../app.js');

    return `http://localhost:${port}${endp}`;
}

function assertValidHTML(res, done) {
    const options = {
        data: res.body,
        format: 'text'
    };

    validator(options, (err, data) => {
        if (err) {
            return done(err);
        }

        // Return when successful.
        if (data.includes('The document validates')) {
            return done();
        }

        // Formatting output for readability.
        const errStr = `HTML Validation for '${res.request.path}' failed with:\n\t${data.replace('Error: ', '').split('\n').join('\n\t')}\n`;

        return done(new Error(errStr));
    });
}

function assertItWorks(res, done) {
    try {
        assert.equal(200, res, 'file missing or forbidden');
        done();
    } catch (err) {
        done(err);
    }
}

function preFetch(uri, cb) {
    const reqOpts = {
        uri,
        forever: true, // for 'connection: Keep-Alive'
        gzip: true
    };

    request.get(reqOpts, (err, res, body) => {
        if (err) {
            console.log(err);
        }

        response = res;
        response.body = body;
    })
    .on('complete', () => cb(response));
}

function cssHTML(uri, sri) {
    return htmlEncode(`<link href="${uri}" rel="stylesheet" integrity="${sri}" crossorigin="anonymous">`);
}

function cssJade(uri, sri) {
    return htmlEncode(`link(href="${uri}", rel="stylesheet", integrity="${sri}", crossorigin="anonymous")`);
}

function cssHAML(uri, sri) {
    return htmlEncode(`%link{href: "${uri}", rel: "stylesheet", integrity: "${sri}", crossorigin: "anonymous"}`);
}

function jsHTML(uri, sri) {
    return htmlEncode(`<script src="${uri}" integrity="${sri}" crossorigin="anonymous"></script>`);
}

function jsJade(uri, sri) {
    return htmlEncode(`script(src="${uri}", integrity="${sri}", crossorigin="anonymous")`);
}

function jsHAML(uri, sri) {
    return htmlEncode(`%script{src: "${uri}", integrity: "${sri}", crossorigin: "anonymous"}`);
}

function domainCheck(uri) {
    if (typeof process.env.TEST_S3 === 'undefined') {
        return uri;
    }

    return uri.replace('https://stackpath.bootstrapcdn.com/', process.env.TEST_S3);
}

module.exports = {
    getConfig,
    runApp,
    assert: {
        contentType: assertContentType,
        itWorks: assertItWorks,
        validHTML: assertValidHTML
    },
    preFetch,
    getExtension,
    css: {
        pug: cssJade,
        html: cssHTML,
        haml: cssHAML
    },
    javascript: {
        pug: jsJade,
        html: jsHTML,
        haml: jsHAML
    },
    CONTENT_TYPE_MAP,
    domainCheck
};

@@ -427,82 +427,8 @@
 %7D;%0A%0A
-// For array of types, the first one will be chosen when testing strictly%0A
 cons
@@ -651,18 +651,8 @@
 ttf:
- %5B%0A       
  'ap
@@ -678,34 +678,8 @@
 f',%0A
-        'font/ttf'%0A    %5D,%0A
     
@@ -1239,18 +1239,20 @@
 i);%0A    
-le
+cons
 t expect
@@ -1258,17 +1258,16 @@
 tedType 
- 
 = CONTEN
@@ -1292,505 +1292,48 @@
     
-// Making TEST_STRICT=true default, pass TEST_STRICT=false to disable%0A    // strict checking.%0A%0A    if (process.env.TEST_STRICT === 'false' && Array.isArray(expectedType)) %7B%0A        assert(contentType.includes(expectedType),%0A            %60Invalid %22content-type%22 for %22$%7Bext%7D%22, expects one of %22$%7BexpectedType.join('%22, %22')%7D%22 but got $%7BcontentType%7D%60);%0A    %7D else %7B%0A        expectedType = Array.isArray(expectedType) ? expectedType%5B0%5D : expectedType;%0A%0A        assert.equal(contentType, expectedType,%0A    
+assert.equal(expectedType, contentType,%0A
     
@@ -1428,22 +1428,16 @@
 pe%7D%22%60);%0A
-    %7D%0A
 %7D%0A%0Afunct

Clean up `CONTENT_TYPE_MAP`.