set_global('$', global.make_zjquery());
const window_stub = $.create('window-stub');
set_global('location', {
    protocol: 'http:',
    host: 'example.com',
});
set_global('to_$', () => window_stub);

zrequire('people');
zrequire('hash_util');
zrequire('hashchange');
zrequire('stream_data');

set_global('document', 'document-stub');
set_global('history', {});

set_global('admin', {});
set_global('drafts', {});
set_global('favicon', {});
set_global('floating_recipient_bar', {});
set_global('info_overlay', {});
set_global('message_viewport', {});
set_global('narrow', {});
set_global('overlays', {});
set_global('settings', {});
set_global('subs', {});
set_global('ui_util', {});

run_test('operators_round_trip', () => {
    let operators;
    let hash;
    let narrow;

    operators = [
        {operator: 'stream', operand: 'devel'},
        {operator: 'topic', operand: 'algol'},
    ];
    hash = hash_util.operators_to_hash(operators);
    assert.equal(hash, '#narrow/stream/devel/topic/algol');

    narrow = hash_util.parse_narrow(hash.split('/'));
    assert.deepEqual(narrow, [
        {operator: 'stream', operand: 'devel', negated: false},
        {operator: 'topic', operand: 'algol', negated: false},
    ]);

    operators = [
        {operator: 'stream', operand: 'devel'},
        {operator: 'topic', operand: 'visual c++', negated: true},
    ];
    hash = hash_util.operators_to_hash(operators);
    assert.equal(hash, '#narrow/stream/devel/-topic/visual.20c.2B.2B');

    narrow = hash_util.parse_narrow(hash.split('/'));
    assert.deepEqual(narrow, [
        {operator: 'stream', operand: 'devel', negated: false},
        {operator: 'topic', operand: 'visual c++', negated: true},
    ]);

    // test new encodings, where we have a stream id
    const florida_stream = {
        name: 'Florida, USA',
        stream_id: 987,
    };
    stream_data.add_sub(florida_stream);
    operators = [
        {operator: 'stream', operand: 'Florida, USA'},
    ];
    hash = hash_util.operators_to_hash(operators);
    assert.equal(hash, '#narrow/stream/987-Florida.2C-USA');
    narrow = hash_util.parse_narrow(hash.split('/'));
    assert.deepEqual(narrow, [
        {operator: 'stream', operand: 'Florida, USA', negated: false},
    ]);
});

run_test('operators_trailing_slash', () => {
    const hash = '#narrow/stream/devel/topic/algol/';
    const narrow = hash_util.parse_narrow(hash.split('/'));
    assert.deepEqual(narrow, [
        {operator: 'stream', operand: 'devel', negated: false},
        {operator: 'topic', operand: 'algol', negated: false},
    ]);
});

run_test('people_slugs', () => {
    let operators;
    let hash;

    const alice = {
        email: 'alice@example.com',
        user_id: 42,
        full_name: 'Alice Smith',
    };

    people.add(alice);
    operators = [
        {operator: 'sender', operand: 'alice@example.com'},
    ];
    hash = hash_util.operators_to_hash(operators);
    assert.equal(hash, '#narrow/sender/42-alice');
    const narrow = hash_util.parse_narrow(hash.split('/'));
    assert.deepEqual(narrow, [
        {operator: 'sender', operand: 'alice@example.com', negated: false},
    ]);

    operators = [
        {operator: 'pm-with', operand: 'alice@example.com'},
    ];
    hash = hash_util.operators_to_hash(operators);
    assert.equal(hash, '#narrow/pm-with/42-alice');
});

function test_helper() {
    let events = [];
    let narrow_terms;

    function stub(module_name, func_name) {
        global[module_name][func_name] = () => {
            events.push(module_name + '.' + func_name);
        };
    }

    stub('admin', 'launch');
    stub('drafts', 'launch');
    stub('favicon', 'reset');
    stub('floating_recipient_bar', 'update');
    stub('message_viewport', 'stop_auto_scrolling');
    stub('narrow', 'deactivate');
    stub('overlays', 'close_for_hash_change');
    stub('settings', 'launch');
    stub('subs', 'launch');
    stub('ui_util', 'blur_active_element');

    ui_util.change_tab_to = (hash) => {
        events.push('change_tab_to ' + hash);
    };

    narrow.activate = (terms) => {
        narrow_terms = terms;
        events.push('narrow.activate');
    };

    info_overlay.show = (name) => {
        events.push('info: ' + name);
    };

    return {
        clear_events: () => {
            events = [];
        },
        assert_events: (expected_events) => {
            assert.deepEqual(expected_events, events);
        },
        get_narrow_terms: () => {
            return narrow_terms;
        },
    };
}

run_test('hash_interactions', () => {
    const helper = test_helper();

    window.location.hash = '#';

    helper.clear_events();
    hashchange.initialize();
    helper.assert_events([
        'overlays.close_for_hash_change',
        'message_viewport.stop_auto_scrolling',
        'change_tab_to #home',
        'narrow.deactivate',
        'floating_recipient_bar.update',
    ]);

    helper.clear_events();
    $(window).trigger($.Event('hashchange', {}));
    helper.assert_events([
        'overlays.close_for_hash_change',
        'message_viewport.stop_auto_scrolling',
        'change_tab_to #home',
        'narrow.deactivate',
        'floating_recipient_bar.update',
    ]);

    window.location.hash = '#narrow/stream/Denmark';

    helper.clear_events();
    $(window).trigger($.Event('hashchange', {}));
    helper.assert_events([
        'overlays.close_for_hash_change',
        'message_viewport.stop_auto_scrolling',
        'change_tab_to #home',
        'narrow.activate',
        'floating_recipient_bar.update',
    ]);
    let terms = helper.get_narrow_terms();
    assert.equal(terms[0].operand, 'Denmark');

    window.location.hash = '#narrow';

    helper.clear_events();
    $(window).trigger($.Event('hashchange', {}));
    helper.assert_events([
        'overlays.close_for_hash_change',
        'message_viewport.stop_auto_scrolling',
        'change_tab_to #home',
        'narrow.activate',
        'floating_recipient_bar.update',
    ]);
    terms = helper.get_narrow_terms();
    assert.equal(terms.length, 0);

    window.location.hash = '#streams/whatever';

    helper.clear_events();
    $(window).trigger($.Event('hashchange', {}));
    helper.assert_events([
        'overlays.close_for_hash_change',
        'subs.launch',
    ]);

    window.location.hash = '#keyboard-shortcuts/whatever';

    helper.clear_events();
    $(window).trigger($.Event('hashchange', {}));
    helper.assert_events([
        'overlays.close_for_hash_change',
        'message_viewport.stop_auto_scrolling',
        'info: keyboard-shortcuts',
    ]);

    window.location.hash = '#message-formatting/whatever';

    helper.clear_events();
    $(window).trigger($.Event('hashchange', {}));
    helper.assert_events([
        'overlays.close_for_hash_change',
        'message_viewport.stop_auto_scrolling',
        'info: message-formatting',
    ]);

    window.location.hash = '#search-operators/whatever';

    helper.clear_events();
    $(window).trigger($.Event('hashchange', {}));
    helper.assert_events([
        'overlays.close_for_hash_change',
        'message_viewport.stop_auto_scrolling',
        'info: search-operators',
    ]);

    window.location.hash = '#drafts';

    helper.clear_events();
    $(window).trigger($.Event('hashchange', {}));
    helper.assert_events([
        'overlays.close_for_hash_change',
        'drafts.launch',
    ]);

    window.location.hash = '#settings/alert-words';

    helper.clear_events();
    $(window).trigger($.Event('hashchange', {}));
    helper.assert_events([
        'overlays.close_for_hash_change',
        'settings.launch',
    ]);

    window.location.hash = '#organization/user-list-admin';

    helper.clear_events();
    $(window).trigger($.Event('hashchange', {}));
    helper.assert_events([
        'overlays.close_for_hash_change',
        'admin.launch',
    ]);

    let called_back;

    helper.clear_events();
    hashchange.exit_overlay(() => {
        called_back = true;
    });

    helper.assert_events([
        'ui_util.blur_active_element',
    ]);
    assert(called_back);

});

run_test('save_narrow', () => {
    const helper = test_helper();

    let operators = [
        {operator: 'is', operand: 'private'},
    ];

    blueslip.expect('warn', 'browser does not support pushState');
    hashchange.save_narrow(operators);

    helper.assert_events([
        'message_viewport.stop_auto_scrolling',
        'favicon.reset',
    ]);
    assert.equal(window.location.hash, '#narrow/is/private');

    let url_pushed;
    global.history.pushState = (state, title, url) => {
        url_pushed = url;
    };

    operators = [
        {operator: 'is', operand: 'starred'},
    ];

    helper.clear_events();
    hashchange.save_narrow(operators);
    helper.assert_events([
        'message_viewport.stop_auto_scrolling',
        'favicon.reset',
    ]);
    assert.equal(url_pushed, 'http://example.com/#narrow/is/starred');
});
