require([
    'aui',
    'lodash',
    'Squire',
    'test-helper'
], function (
    AJS,
    _,
    Squire,
    testHelper
) {

    var key = {};
    var existingItem = 'deadbeef';
    var mockClientStorage = {
        getSessionItem: sinon.stub().returns(existingItem),
        setSessionItem: sinon.stub(),
        removeSessionItem: sinon.stub(),
        buildKey: sinon.stub().returns(key)
    };

    var mockLocation = {
        location: {
            href: ''
        }
    };

    var injector = new Squire();
    injector.setDefaultDeps(['bitbucket/internal/feature/repository/sticky-branches']);
    injector.mock('bitbucket/internal/util/client-storage', mockClientStorage);
    injector.mock('bitbucket/internal/model/page-state', testHelper.mockPageState({
        projectKey: 'PROJECT',
        repoSlug: 'REPO'
    }));
    injector.mock('bitbucket/internal/util/browser-location', mockLocation);

    QUnit.module('repository-sticky-branches', {
        beforeEach: function () {
            _.forOwn(mockClientStorage, function (prop) {
                if (typeof prop.reset === 'function') {
                    prop.reset();
                }
            });
        }
    });

    function stickyBranchesTest(name, url, beforeFn, testFn) {
        if (!testFn) {
            testFn = beforeFn;
            beforeFn = undefined;
        }
        injector.test(name, function (assert, stickyBranches) {
            if (beforeFn) {
                beforeFn();
            }
            mockLocation.location.href = url;
            stickyBranches.onReady();
            testFn(assert);
        });
    }

    function createMockBranchSelector(type, ref) {
        var $el = $("<div id='repository-layout-revision-selector'></div>")
            .appendTo($('#qunit-fixture'));
        $("<div></div>")
            .attr('data-revision-ref', JSON.stringify({
                id: ref,
                type: {
                    id: type
                }
            }))
            .appendTo($el);
    }

    stickyBranchesTest('Updates sticky ref when at ref is a branch', AJS.contextPath() + '/projects/PROJECT/repos/REPO/browse?at=refs/heads/branch', function () {
        createMockBranchSelector('branch', 'refs/heads/branch');
    }, function (assert) {
        assert.ok(mockClientStorage.setSessionItem.calledWith(key, 'refs/heads/branch'));
        assert.ok(mockClientStorage.removeSessionItem.notCalled, mockClientStorage.removeSessionItem.firstCall);
    });

    stickyBranchesTest('Does not update sticky ref when at ref is a commit hash but matches a branch', AJS.contextPath() + '/projects/PROJECT/repos/REPO/browse?at=deadbeef', function () {
        createMockBranchSelector('branch', 'refs/heads/branch');
    }, function (assert) {
        assert.ok(mockClientStorage.setSessionItem.notCalled, mockClientStorage.setSessionItem.firstCall);
        assert.ok(mockClientStorage.removeSessionItem.notCalled, mockClientStorage.removeSessionItem.firstCall);
    });

    stickyBranchesTest('Does not update sticky ref when at ref is a commit hash but matches a tag', AJS.contextPath() + '/projects/PROJECT/repos/REPO/browse?at=deadbeef', function () {
        createMockBranchSelector('tag', 'refs/tags/tag');
    }, function (assert) {
        assert.ok(mockClientStorage.setSessionItem.notCalled, mockClientStorage.setSessionItem.firstCall);
        assert.ok(mockClientStorage.removeSessionItem.notCalled, mockClientStorage.removeSessionItem.firstCall);
    });

    stickyBranchesTest('Does not update sticky ref when at ref is a commit hash not matching a branch', AJS.contextPath() + '/projects/PROJECT/repos/REPO/browse?at=deadbeef', function () {
        createMockBranchSelector('commit', 'deadbeef');
    }, function (assert) {
        assert.ok(mockClientStorage.setSessionItem.notCalled, mockClientStorage.setSessionItem.firstCall);
        assert.ok(mockClientStorage.removeSessionItem.notCalled, mockClientStorage.removeSessionItem.firstCall);
    });

    stickyBranchesTest('Does not update sticky ref when at ref matches a branch and there is no existing sticky ref', AJS.contextPath() + '/projects/PROJECT/repos/REPO/browse?at=deadbeef', function () {
        existingItem = '';
        createMockBranchSelector('branch', 'refs/heads/branch');
    }, function (assert) {
        assert.ok(mockClientStorage.setSessionItem.notCalled, mockClientStorage.setSessionItem.firstCall);
        assert.ok(mockClientStorage.removeSessionItem.notCalled, mockClientStorage.removeSessionItem.firstCall);
    });

    stickyBranchesTest('Updates sticky ref when at ref is a branch', AJS.contextPath() + '/projects/PROJECT/repos/REPO/browse?at=refs/heads/branch', function () {
        existingItem = '';
        createMockBranchSelector('branch', 'refs/heads/branch');
    }, function (assert) {
        assert.ok(mockClientStorage.setSessionItem.calledWith(key, 'refs/heads/branch'));
        assert.ok(mockClientStorage.removeSessionItem.notCalled);
    });

    stickyBranchesTest('Clears sticky ref when there is no at ref', AJS.contextPath() + '/projects/PROJECT/repos/REPO/browse', function () {
        existingItem = '';
        createMockBranchSelector('branch', 'refs/heads/branch');
    }, function (assert) {
        assert.ok(mockClientStorage.setSessionItem.notCalled, mockClientStorage.setSessionItem.firstCall);
        assert.ok(mockClientStorage.removeSessionItem.calledWith(key));
    });
});