require([
    'jquery',
    'lodash',
    'bitbucket/internal/util/horizontal-keyboard-scrolling'
], function ($,
             _,
             horizontalKeyboardScrolling) {

    'use strict';

    var LEFT_KEYDOWN = _.extend($.Event('keydown'), {
        keyCode: 37
    });

    var RIGHT_KEYDOWN = _.extend($.Event('keydown'), {
        keyCode: 39
    });

    var INITIAL_LEFT = 100;
    var WIDTH_NORMAL = 600;
    var WIDTH_NO_SCROLL = 300;
    var WIDTH_RIGHT_EDGE = 420;
    var CLIENT_WIDTH = 300;
    var SCROLL_STEP = 50;

    module('util-horizontal-keyboard-scrolling', {
        setup: function () {
            horizontalKeyboardScrolling.init();
            this.codeMirror = getCodeMirrorMock();
            horizontalKeyboardScrolling.setEditors([this.codeMirror]);
        },
        teardown: function () {
        }
    });

    test('should minus scroll step on left arrow keydown', function () {
        assertScrollToCalls.call(this, getScrollInfoNormal(), LEFT_KEYDOWN, INITIAL_LEFT - SCROLL_STEP);
    });

    test('should plus scroll step on right arrow keydown', function () {
        horizontalKeyboardScrolling.init();
        assertScrollToCalls.call(this, getScrollInfoNormal(), RIGHT_KEYDOWN, INITIAL_LEFT + SCROLL_STEP);
    });

    test('should avoid scrolling on keydowns when the content is small', function () {
        horizontalKeyboardScrolling.init();
        assertNoScrolls.call(this, getScrollInfoNoScroll(), LEFT_KEYDOWN);
        assertNoScrolls.call(this, getScrollInfoNoScroll(), RIGHT_KEYDOWN);
    });

    test('should stop at the edge when reaching the right edge', function () {
        horizontalKeyboardScrolling.init();
        assertScrollToCalls.call(this, getScrollInfoRightEdge(), RIGHT_KEYDOWN, WIDTH_RIGHT_EDGE - CLIENT_WIDTH);
    });

    test('should pause scrolling when paused', function () {
        horizontalKeyboardScrolling.init();
        horizontalKeyboardScrolling.pause();

        assertNoScrolls.call(this, getScrollInfoNormal(), LEFT_KEYDOWN);
        assertNoScrolls.call(this, getScrollInfoNormal(), RIGHT_KEYDOWN);
    });

    test('should resume scrolling after paused and resumed', function () {
        horizontalKeyboardScrolling.init();
        horizontalKeyboardScrolling.pause();
        horizontalKeyboardScrolling.resume();

        assertScrollToCalls.call(this, getScrollInfoNormal(), LEFT_KEYDOWN, INITIAL_LEFT - SCROLL_STEP);
    });

    test('should switch editor after invalidate code mirror', function () {
        horizontalKeyboardScrolling.init();

        var firstEditor = this.codeMirror;
        assertScrollToCalls.call(this, getScrollInfoNormal(), LEFT_KEYDOWN, INITIAL_LEFT - SCROLL_STEP);

        firstEditor.scrollTo.reset();
        var secondEditor = getCodeMirrorMock();
        secondEditor.getScrollInfo.returns(getScrollInfoNormal());

        horizontalKeyboardScrolling.setEditors([secondEditor]);
        assertNoScrolls.call(this, getScrollInfoNormal(), LEFT_KEYDOWN);
        ok(secondEditor.scrollTo.withArgs(INITIAL_LEFT - SCROLL_STEP, null).called);
    });

    test('should have different scroll distance when editors have different width', function() {
        var editorShort = getCodeMirrorMock();
        var editorLong = getCodeMirrorMock();

        editorShort.getScrollInfo.returns(getScrollInfoRightEdge());
        editorLong.getScrollInfo.returns(getScrollInfoNormal());

        horizontalKeyboardScrolling.setEditors([editorShort, editorLong]);
        assertNoScrolls.call(this, getScrollInfoNormal(), RIGHT_KEYDOWN);
        ok(editorShort.scrollTo.withArgs(WIDTH_RIGHT_EDGE - CLIENT_WIDTH).called);
        ok(editorLong.scrollTo.withArgs(INITIAL_LEFT + SCROLL_STEP).called);
    });

    function assertScrollToCalls(scrollInfo, event, scrollLeft) {
        var $document = $(document);
        var codeMirror = this.codeMirror;

        codeMirror.getScrollInfo.returns(scrollInfo);
        $document.trigger(event);
        ok(codeMirror.scrollTo.withArgs(scrollLeft, null).called);
    }

    function assertNoScrolls(scrollInfo, event) {
        var $document = $(document);
        var codeMirror = this.codeMirror;

        codeMirror.getScrollInfo.returns(scrollInfo);
        $document.trigger(event);
        ok(!codeMirror.scrollTo.called);
    }

    function getScrollInfoNormal() {
        return {
            left: INITIAL_LEFT,
            clientWidth: CLIENT_WIDTH,
            width: WIDTH_NORMAL
        };
    }

    function getScrollInfoNoScroll() {
        return {
            left: 0,
            clientWidth: CLIENT_WIDTH,
            width: WIDTH_NO_SCROLL
        };
    }

    function getScrollInfoRightEdge() {
        return {
            left: 100,
            clientWidth: CLIENT_WIDTH,
            width: WIDTH_RIGHT_EDGE
        };
    }

    function getCodeMirrorMock() {
        return {
            getScrollInfo: sinon.stub(),
            scrollTo: sinon.spy()
        };
    }
});
