var wd = require('wd');
var http = require('http');
var https = require('https');
var url = require('url');
var path = require('path');
var Promise = require('bluebird');
var _ = require('lodash');
var humanizeDuration = require('humanize-duration');
var utils = require('../utils');
var colors = utils.colors;
var port = 8080;

function runTestWithRetries(browser, test, retries) {
    retries = retries || 0;
    return runTest(browser, test).timeout(30000).catch(Promise.TimeoutError, function() {
        if (retries < 3) {
            console.log(colors.violet, 'Retry', retries + 1, test);
            return runTestWithRetries(browser, test, retries + 1);
        } else {
            throw new Error("Couldn't run test after 3 retries");
        }
    });
}

function getResults(browser) {
    return function() {
        return Promise.props({
            dataUrl: browser
                .waitForElementByCss("body[data-complete='true']", 90000)
                .then(function() {
                    return browser.elementsByCssSelector('.test.fail');
                })
                .then(function(nodes) {
                    return Array.isArray(nodes)
                        ? Promise.map(nodes, function(node) {
                              return browser.text(node).then(function(error) {
                                  return Promise.reject(error);
                              });
                          })
                        : Promise.resolve([]);
                })
        });
    };
}

function runTest(browser, test) {
    return Promise.resolve(
        browser.then(utils.loadTestPage(browser, test, port)).then(getResults(browser))
    ).cancellable();
}

exports.tests = function(browsers, singleTest) {
    var path = 'tests/mocha';
    return (singleTest ? Promise.resolve([singleTest]) : utils.getTests(path)).then(function(
        tests
    ) {
        return Promise.map(
            browsers,
            function(settings) {
                var name = [settings.browserName, settings.version, settings.platform].join('-');
                var count = 0;
                var browser = utils.initBrowser(settings);
                return Promise.using(browser, function() {
                    return Promise.map(
                        tests,
                        function(test, index, total) {
                            console.log(
                                colors.green,
                                'STARTING',
                                '(' + ++count + '/' + total + ')',
                                name,
                                test,
                                colors.clear
                            );
                            var start = Date.now();
                            return runTestWithRetries(browser, test).then(function() {
                                console.log(
                                    colors.green,
                                    'COMPLETE',
                                    humanizeDuration(Date.now() - start),
                                    '(' + count + '/' + total + ')',
                                    name,
                                    colors.clear
                                );
                            });
                        },
                        {concurrency: 1}
                    )
                        .settle()
                        .catch(function(error) {
                            console.error(colors.red, 'ERROR', name, error);
                            throw error;
                        });
                });
            },
            {concurrency: 3}
        );
    });
};