define([
    'jquery',
    'bitbucket/internal/util/css'
], function(
    $,
    css
) {

    'use strict';

    var unset = (function() {
        var div = document.createElement('div');
        document.body.appendChild(div);
        var val = getComputedStyle(div).backgroundColor;
        document.body.removeChild(div);
        return val;
    })();
    var red = 'rgb(255, 0, 0)';
    var green = 'rgb(0, 255, 0)';
    var blue = 'rgb(0, 0, 255)';

    module('util-css', {
        setup : function() {
            this.$foo = $('<div class="foo">Foo</div>').appendTo('#qunit-fixture');
            this.$bar = $('<div class="bar">Bar</div>').appendTo('#qunit-fixture');
            this.$baz = $('<div class="baz">Baz</div>').appendTo('#qunit-fixture');
        },
        teardown : function(){
            delete this.$foo;
            delete this.$bar;
            if (css.__sheet.cssRules.length) {
                while(css.__sheet.cssRules.length) { // try to clean up to avoid failing other tests
                    css.__sheet.removeRule(0);
                }
                ok(false, 'The test did not clean up the added rules.');
            }
        },
        expectBgColors : function(colorByClass) {
            //Safari 8 doesn't trigger recalculation without this for some reason
            this.$foo[0].offsetHeight;
            this.$bar[0].offsetHeight;
            this.$baz[0].offsetHeight;

            equal(getComputedStyle(this.$foo[0]).backgroundColor, colorByClass.foo);
            equal(getComputedStyle(this.$bar[0]).backgroundColor, colorByClass.bar);
            equal(getComputedStyle(this.$baz[0]).backgroundColor, colorByClass.baz);
        }
    });

    test('chain', function() {
        this.expectBgColors({ foo: unset, bar: unset, baz: unset });

        var chain = css.chain();
        equal(typeof chain.destroy, 'function', 'has destroy');
        equal(typeof chain.appendRule, 'function', 'has appendRule');

        equal(chain.appendRule('.foo { background-color: ' + red + '; }'), chain, 'returns this');
        this.expectBgColors({ foo: red, bar: unset, baz: unset });

        equal(chain.appendRule('.bar { background-color: ' + blue + '; }'), chain, 'returns this');
        this.expectBgColors({ foo: red, bar: blue, baz: unset });

        chain.destroy();
        this.expectBgColors({ foo: unset, bar: unset, baz: unset });

        chain.destroy(); // does nothing, no throw
        this.expectBgColors({ foo: unset, bar: unset, baz: unset });
    });

    function setupAllRules(thenFn) {
        this.expectBgColors({ foo: unset, bar: unset, baz: unset });

        var destroyFoo = css.appendRule('.foo { background-color: ' + red + '; }');
        equal(typeof destroyFoo, 'function', 'returns destroy function');
        this.expectBgColors({ foo: red, bar: unset, baz: unset });

        var destroyBar = css.appendRule('.bar { background-color: ' + blue + '; }');
        equal(typeof destroyBar, 'function', 'returns destroy function');
        this.expectBgColors({ foo: red, bar: blue, baz: unset });

        var destroyBaz = css.appendRule('.baz { background-color: ' + green + '; }');
        equal(typeof destroyBaz, 'function', 'returns destroy function');
        this.expectBgColors({ foo: red, bar: blue, baz: green });

        thenFn.call(this, destroyFoo, destroyBar, destroyBaz, this.expectBgColors.bind(this));
    }

    test('appendRule - FILO', function() {
        setupAllRules.call(this, function(destroyFoo, destroyBar, destroyBaz, expectBgColors) {
            destroyBaz();
            expectBgColors({ foo: red, bar: blue, baz: unset });

            destroyBar();
            expectBgColors({ foo: red, bar: unset, baz: unset });

            destroyFoo();
            expectBgColors({ foo: unset, bar: unset, baz: unset });
        });
    });

    test('appendRule - FIFO', function() {
        setupAllRules.call(this, function(destroyFoo, destroyBar, destroyBaz, expectBgColors) {
            destroyFoo();
            expectBgColors({ foo: unset, bar: blue, baz: green });

            destroyFoo(); // does nothing, no throw
            expectBgColors({ foo: unset, bar: blue, baz: green });

            destroyBar();
            expectBgColors({ foo: unset, bar: unset, baz: green });

            destroyBar(); // does nothing, no throw
            expectBgColors({ foo: unset, bar: unset, baz: green });

            destroyBaz();
            expectBgColors({ foo: unset, bar: unset, baz: unset });

            destroyBaz(); // does nothing, no throw
            expectBgColors({ foo: unset, bar: unset, baz: unset });
        });
    });

    test('appendRule - mixed', function() {
        setupAllRules.call(this, function(destroyFoo, destroyBar, destroyBaz, expectBgColors) {
            destroyBar();
            expectBgColors({ foo: red, bar: unset, baz: green });

            destroyFoo();
            expectBgColors({ foo: unset, bar: unset, baz: green });

            destroyBaz();
            expectBgColors({ foo: unset, bar: unset, baz: unset });
        });
    });

    test('appendRule - serial', function() {
        this.expectBgColors({ foo: unset, bar: unset, baz: unset });

        var destroyFoo = css.appendRule('.foo { background-color: ' + red + '; }');
        equal(typeof destroyFoo, 'function', 'returns destroy function');
        this.expectBgColors({ foo: red, bar: unset, baz: unset });

        destroyFoo();
        this.expectBgColors({ foo: unset, bar: unset, baz: unset });

        var destroyBar = css.appendRule('.bar { background-color: ' + blue + '; }');
        equal(typeof destroyBar, 'function', 'returns destroy function');
        this.expectBgColors({ foo: unset, bar: blue, baz : unset });

        destroyBar();
        this.expectBgColors({ foo: unset, bar: unset, baz: unset });
    });

    test('appendRule - interleaved', function() {
        this.expectBgColors({ foo: unset, bar: unset, baz: unset });

        var destroyFoo = css.appendRule('.foo { background-color: ' + red + '; }');
        equal(typeof destroyFoo, 'function', 'returns destroy function');
        this.expectBgColors({ foo: red, bar: unset, baz: unset });

        var destroyBar = css.appendRule('.bar { background-color: ' + blue + '; }');
        equal(typeof destroyBar, 'function', 'returns destroy function');
        this.expectBgColors({ foo: red, bar: blue, baz: unset });

        destroyFoo();
        this.expectBgColors({ foo: unset, bar: blue, baz: unset });

        var destroyBaz = css.appendRule('.baz { background-color: ' + green + '; }');
        equal(typeof destroyBaz, 'function', 'returns destroy function');
        this.expectBgColors({ foo: unset, bar: blue, baz: green });

        destroyBar();
        this.expectBgColors({ foo: unset, bar: unset, baz: green });

        destroyBaz();
        this.expectBgColors({ foo: unset, bar: unset, baz: unset });
    });
});