define([
    'jquery',
    'lodash',
    'Squire',
    'bitbucket/mock/mirroring/nav-builder',
    'test-helper'
], function (
    $,
    _,
    Squire,
    NavBuilder
) {

    'use strict';

    //sinon.stub(obj, name) doesn't work if name is obj[name] is undefined. So we have to do this.
    var jqueryStub = function (name) {
        var old = $.fn[name];
        var result = $.fn[name] = sinon.spy(function () { return this; });
        result.restore = function () {
            if (old) {
                $.fn[name] = old;
            } else {
                delete $.fn[name];
            }
        };
        return result;
    };

    var reload = sinon.spy();
    var poll = sinon.stub();
    var injector = new Squire();

    injector.mock('jquery', $);
    injector.mock('bitbucket/util/server', {poll: poll});
    injector.mock('bitbucket/util/navbuilder', new NavBuilder());
    injector.mock('bitbucket/internal/util/browser-window', {
        location: {
            reload: reload
        }
    });
    injector.mock('aui', {
        I18n: {
            getText: function() {
                return _.toArray(arguments).join('/');
            }
        }
    });
    injector.setDefaultDeps(['bitbucket/internal/mirroring/placeholder']);

    QUnit.module('bitbucket/internal/mirroring/placeholder', {
        beforeEach: function () {
            this.pollDeferred = $.Deferred();

            reload.reset();
            poll.reset().returns(this.pollDeferred.promise());

            this.spin = jqueryStub("spin");
            this.spinStop = jqueryStub("spinStop");
        },
        afterEach: function () {
            this.spin.restore();
            this.spinStop.restore();
        }
    });

    var checkPollCall = function (assert, state, placeholder) {
        placeholder.onReady(state);

        assert.ok(poll.calledOnce, "Poll was called");

        var pollArgs = poll.firstCall.args[0];
        assert.strictEqual(pollArgs.url, "/mirroring/1.0/upstreamServers", "Upstream URL is correct");
        assert.strictEqual(pollArgs.interval, 30000, "Interval set to 30s");
        assert.deepEqual(pollArgs.statusCode, {'*': false}, "Ignoring all errors.");

        //Check that the tick function reacts to the response correctly.
        var tickFunction = pollArgs.tick;
        assert.strictEqual(tickFunction({size: 0}), undefined, "Returns undefined on empty data.");
        assert.strictEqual(tickFunction({size: 1, values:[{state: state}]}), undefined, "Returns undefined still in state.");
        assert.strictEqual(tickFunction({size: 1, values:[{state: "not" + state}]}), true, "Returns true when not in state.");
    };

    injector.test("Spinner loaded when in initializing state.", function(assert, placeholder) {
        placeholder.onReady('initializing');
        sinon.assert.calledWith(this.spin, 'large')
    });

    injector.test("Polls the server for registration of upstream when in initializing state.", function(assert, placeholder) {
        checkPollCall.call(this, assert, 'initializing', placeholder);
    });

    injector.test("Successful registration reloads the page in initializing state.", function(assert, placeholder) {
        placeholder.onReady('initializing');

        assert.ok(poll.calledOnce, "Poll was called.");
        this.pollDeferred.resolve();
        assert.ok(reload.calledOnce, "Reload was called on the window.");
    });

    injector.test("Failed poll updates UI when in initializing state.", function(assert, placeholder) {
        var $heading = $("<div>").attr({id: "mirror-initializing-heading"}).appendTo("#qunit-fixture");

        placeholder.onReady('initializing');
        assert.ok(poll.calledOnce, "Poll was called");

        this.pollDeferred.reject();

        sinon.assert.calledOnce(this.spinStop);
        assert.equal($heading.text(), "bitbucket.web.mirroring.connecting.lost.connection", "Heading changed on error");
    });

    injector.test("Johnson/503 reloads the page when in initializing state.", function(assert, placeholder) {
        placeholder.onReady('initializing');
        assert.ok(poll.calledOnce, "Poll was called");

        this.pollDeferred.reject({status: 503});

        assert.ok(reload.calledOnce, "Reload was called on the window.");
    });

    injector.test("Polls the server when in pending state.", function(assert, placeholder) {
        checkPollCall.call(this, assert, 'pending', placeholder);
    });

    injector.test("Reloads the page when moving from pending state.", function(assert, placeholder) {
        placeholder.onReady('pending');
        assert.ok(poll.calledOnce, "Poll was called");

        this.pollDeferred.resolve();

        assert.ok(reload.calledOnce, "Reload was called on the window.");
    });

    injector.test("Does nothing when connection lost in pending state.", function(assert, placeholder) {
        placeholder.onReady('pending');
        assert.ok(poll.calledOnce, "Poll was called");

        this.pollDeferred.reject();

        assert.ok(!reload.calledOnce, "The page should not be reloaded.");
    });

    injector.test("Reloads the page when moving from unknown state.", function(assert, placeholder) {
        placeholder.onReady('unknown');
        assert.ok(poll.calledOnce, "Poll was called");

        this.pollDeferred.resolve();

        assert.ok(reload.calledOnce, "Reload was called on the window.");
    });

    injector.test("Does nothing when connection lost in unknown state.", function(assert, placeholder) {
        placeholder.onReady('unknown');
        assert.ok(poll.calledOnce, "Poll was called");

        this.pollDeferred.reject();

        assert.ok(!reload.calledOnce, "The page should not be reloaded.");
    });
});
