var createDefaultStream = require('./lib/default_stream');
var Test = require('./lib/test');
var createResultStream = require('./lib/results');

var canEmitExit = typeof process !== 'undefined' && process
    && typeof process.on === 'function'
;
var canExit = typeof process !== 'undefined' && process
    && typeof process.exit === 'function'
;

var nextTick = typeof setImmediate !== 'undefined'
    ? setImmediate
    : process.nextTick
;

exports = module.exports = (function () {
    var harness;
    var lazyLoad = function () {
        if (!harness) harness = createExitHarness();

        return harness.apply(this, arguments);
    };

    lazyLoad.only = function () {
        if (!harness) harness = createExitHarness();

        return harness.only.apply(this, arguments);
    }

    return lazyLoad
})();

function createExitHarness (conf) {
    if (!conf) conf = {};
    var harness = createHarness();
    var stream = harness.createStream();
    stream.pipe(createDefaultStream());
    
    var ended = false;
    stream.on('end', function () { ended = true });
    
    if (conf.exit === false) return harness;
    if (!canEmitExit || !canExit) return harness;
    
    var _error;

    process.on('uncaughtException', function (err) {
        _error = err
        
        throw err
    })

    process.on('exit', function (code) {
        if (_error) {
            return
        }

        if (!ended) {
            for (var i = 0; i < harness._tests.length; i++) {
                var t = harness._tests[i];
                t._exit();
            }
        }
        process.exit(code || harness._exitCode);
    });
    return harness;
}

exports.createHarness = createHarness;
exports.Test = Test;
exports.test = exports; // tap compat

var exitInterval;

function createHarness (conf_) {
    var results;
    
    var test = function (name, conf, cb) {
        if (!results) {
            results = createResultStream();
            results.pause();
        }
        
        var t = new Test(name, conf, cb);
        test._tests.push(t);
        
        (function inspectCode (st) {
            st.on('test', function sub (st_) {
                inspectCode(st_);
            });
            st.on('result', function (r) {
                if (!r.ok) test._exitCode = 1
            });
        })(t);
        
        results.push(t);
        return t;
    };
    
    test._tests = [];
    
    test.createStream = function () {
        if (!results) results = createResultStream();
        
        var _pause = results.pause;
        var paused = false;
        results.pause = function () { paused = true };
        
        nextTick(function () {
            if (!paused) results.resume();
        });
        return results;
    };
    
    var only = false;
    test.only = function (name) {
        if (only) throw new Error('there can only be one only test');
        results.only(name);
        only = true;
        return test.apply(null, arguments);
    };
    test._exitCode = 0;
    
    return test;
}
