define([
    'Squire',
    'test-helper',
    'bitbucket/util/events',
    'bitbucket/internal/util/promise'
], function(
    Squire,
    testHelper,
    events,
    promise
) {
    'use strict';

    var setGutterEnabled = sinon.spy(function() {
        return $.Deferred();
    });

    var getBlame;

    var MockBlameSource = sinon.spy(function _MockBlameSource() {
        return {
            get : function() {
                return getBlame.apply(this, arguments);
            }
        };
    });

    var MockBlameDiff = sinon.spy(function _MockBlameDiff() {
        return {
            get : function() {
                return getBlame.apply(this, arguments);
            }
        };
    });

    var MockBlameGutter = sinon.spy(function _MockBlameGutter() {
        return {
            setEnabled : function() {
                return setGutterEnabled.apply(this, arguments);
            }
        };
    });

    function getInjector() {
        var injector = new Squire();
        injector.setDefaultDeps(['bitbucket/internal/feature/file-content/file-blame']);
        injector.mock('bitbucket/internal/feature/file-content/file-blame/blame-gutter', MockBlameGutter);
        injector.mock('bitbucket/internal/feature/file-content/file-blame/blame-source', MockBlameSource);
        injector.mock('bitbucket/internal/feature/file-content/file-blame/blame-diff', MockBlameDiff);
        injector.mock('bitbucket/internal/model/page-state', testHelper.mockPageState());
        injector.mock('bitbucket/util/events', events);
        injector.mock('bitbucket/internal/util/promise', promise);
        return injector;
    }

    var injector = getInjector(true);


    module('file-blame', {
        setup : function() {
            this.$fixture = $('#qunit-fixture');
            this.$button = $('<button class="aui-button file-blame"/>');
            this.$container = $('<div class="blame-container"/>');

            this.$fixture
                .append(this.$button)
                .append(this.$container);

            this.textView = {
                on: function(event, fn){
                    this[event] = fn;
                }
            };
            getBlame = sinon.spy(function(options) {
                return $.Deferred().resolve([{
                    author : {"name": 'Yoda'},
                    authorTimestamp : (+new Date(2012, 1, 1, 0, 0, 0)),
                    commitHash : 'deadbeefcabbac99d00dd00d31337157',
                    spannedLines : options.data.limit
                }]);
            });
            setGutterEnabled.reset();
            sinon.stub(promise, 'spinner');
        },
        teardown: function () {
            this.$fixture.empty();
            this.textView.destroy && this.textView.destroy();
            MockBlameGutter.reset();
            MockBlameSource.reset();
            promise.spinner.restore();
        }
    });

    var INIT_EVENT = 'bitbucket.internal.feature.fileContent.textViewInitializing';

    function withBlameInited(injector, fn) {
        var self = this;
        injector.injectedCall(function (fileBlame) {
            var blameObj = fileBlame.init();
            try {
                fn.call(self, blameObj);
            } finally {
                blameObj.destroy();
            }
            start();
        });
    }

    asyncTest('listener inits on text-views for source', function() {
        withBlameInited.call(this, injector, function() {
            events.trigger(INIT_EVENT, null, this.textView, { contentMode : 'source', $toolbar: this.$fixture });
            equal(MockBlameGutter.callCount, 1, 'Gutter created');
            equal(MockBlameSource.callCount, 1, 'Source created');
            equal(getBlame.callCount, 0, 'No blame retrieved upfront');
            equal(setGutterEnabled.callCount, 0, 'No gutter enabled upfront');
        });
    });

    asyncTest('listener inits on text-views for diff', function() {
        withBlameInited.call(this, injector, function() {
            events.trigger(INIT_EVENT, null, this.textView, { contentMode : 'diff', $toolbar: this.$fixture });
            equal(MockBlameGutter.callCount, 1, 'Gutter created');
            equal(MockBlameDiff.callCount, 1, 'Source created');
            equal(getBlame.callCount, 0, 'No blame retrieved upfront');
            equal(setGutterEnabled.callCount, 0, 'No gutter enabled upfront');
        });
    });

    asyncTest('enables/disables blame gutter on button click', function() {
        withBlameInited.call(this, injector, function() {
            events.trigger(INIT_EVENT, null, this.textView, { contentMode : 'source', $toolbar: this.$fixture });
            equal(setGutterEnabled.callCount, 0, 'No gutter enabled upfront');

            this.$button.click();
            equal(setGutterEnabled.callCount, 1, 'Gutter enabled');
            equal(setGutterEnabled.args[0][0], true, 'Gutter enabled (true)');
            ok(this.$button.prev().hasClass('blame-spinner'), 'While Gutter is being enabled, we spin');
            setGutterEnabled.returnValues[0].resolve();
            ok(this.$button.prev().hasClass('blame-spinner'), 'We stop spinning on resolve');

            this.$button.click();
            equal(setGutterEnabled.callCount, 2, 'Gutter disabled');
            equal(setGutterEnabled.args[1][0], false, 'Gutter disabled (false)');
            ok(this.$button.prev().hasClass('blame-spinner'), 'While Gutter is being disabled, we spin');
            setGutterEnabled.returnValues[1].resolve();
            ok(this.$button.prev().hasClass('blame-spinner'), 'We stop spinning on resolve');

            this.$button.attr('aria-disabled', 'true');
            this.$button.click();
            equal(setGutterEnabled.callCount, 2, 'Gutter state unchanged when button is disabled');
        });
    });
});