set_global('$', global.make_zjquery());
zrequire('input_pill');

set_global('Handlebars', global.make_handlebars());
zrequire('templates');

set_global('document', {});

const noop = function () {};
const example_img_link = 'http://example.com/example.png';

set_global('ui_util', {
    place_caret_at_end: noop,
});

set_global('getSelection', () => {
    return {
        anchorOffset: 0,
    };
});

let id_seq = 0;
run_test('set_up_ids', () => {
    // just get coverage on a simple one-liner:
    input_pill.random_id();

    input_pill.random_id = function () {
        id_seq += 1;
        return 'some_id' + id_seq;
    };
});

function pill_html(value, data_id, img_src) {
    const has_image = img_src !== undefined;

    const opts = {
        id: data_id,
        display_value: value,
        has_image: has_image,
    };

    if (has_image) {
        opts.img_src = img_src;
    }

    return require('../../static/templates/input_pill.hbs')(opts);
}

run_test('basics', () => {
    const config = {};

    blueslip.expect('error', 'Pill needs container.');
    input_pill.create(config);

    const pill_input = $.create('pill_input');
    const container = $.create('container');
    container.set_find_results('.input', pill_input);

    blueslip.expect('error', 'Pill needs create_item_from_text');
    config.container = container;
    input_pill.create(config);

    blueslip.expect('error', 'Pill needs get_text_from_item');
    config.create_item_from_text = noop;
    input_pill.create(config);

    config.get_text_from_item = noop;
    const widget = input_pill.create(config);

    const item = {
        display_value: 'JavaScript',
        language: 'js',
        img_src: example_img_link,
    };

    let inserted_before;
    const expected_html = pill_html('JavaScript', 'some_id1', example_img_link);

    pill_input.before = function (elem) {
        inserted_before = true;
        assert.equal(elem.html(), expected_html);
    };

    widget.appendValidatedData(item);
    assert(inserted_before);

    assert.deepEqual(widget.items(), [item]);
});

function set_up() {
    set_global('$', global.make_zjquery());
    const items = {
        blue: {
            display_value: 'BLUE',
            description: 'color of the sky',
            img_src: example_img_link,
        },

        red: {
            display_value: 'RED',
            description: 'color of stop signs',
        },

        yellow: {
            display_value: 'YELLOW',
            description: 'color of bananas',
        },
    };

    const pill_input = $.create('pill_input');

    pill_input.before = () => {};

    const create_item_from_text = function (text) {
        return items[text];
    };

    const container = $.create('container');
    container.set_find_results('.input', pill_input);

    const config = {
        container: container,
        create_item_from_text: create_item_from_text,
        get_text_from_item: (item) => item.display_value,
    };

    id_seq = 0;

    return {
        config: config,
        pill_input: pill_input,
        items: items,
        container: container,
    };
}

run_test('copy from pill', () => {
    const info = set_up();
    const config = info.config;
    const container = info.container;

    const widget = input_pill.create(config);
    widget.appendValue('blue,red');

    const copy_handler = container.get_on_handler('copy', '.pill');

    let copied_text;

    const pill_stub = {
        data: (field) => {
            assert.equal(field, 'id');
            return 'some_id2';
        },
    };

    const e = {
        originalEvent: {
            clipboardData: {
                setData: (format, text) => {
                    assert.equal(format, 'text/plain');
                    copied_text = text;
                },
            },
        },
        preventDefault: noop,
    };

    container.set_find_results(':focus', pill_stub);

    copy_handler(e);

    assert.equal(copied_text, 'RED');
});

run_test('paste to input', () => {
    const info = set_up();
    const config = info.config;
    const container = info.container;
    const items = info.items;

    const widget = input_pill.create(config);

    const paste_handler = container.get_on_handler('paste', '.input');

    const paste_text = 'blue,yellow';

    const e = {
        originalEvent: {
            clipboardData: {
                getData: (format) => {
                    assert.equal(format, 'text/plain');
                    return paste_text;
                },
            },
        },
        preventDefault: noop,
    };

    document.execCommand = (cmd, _, text) => {
        assert.equal(cmd, 'insertText');
        container.find('.input').text(text);
    };

    paste_handler(e);

    assert.deepEqual(widget.items(), [
        items.blue,
        items.yellow,
    ]);
});

run_test('arrows on pills', () => {
    const info = set_up();
    const config = info.config;
    const container = info.container;

    const widget = input_pill.create(config);
    widget.appendValue('blue,red');

    const key_handler = container.get_on_handler('keydown', '.pill');

    function test_key(c) {
        key_handler({
            charCode: c,
        });
    }

    const LEFT_ARROW = 37;
    const RIGHT_ARROW = 39;

    let prev_focused = false;
    let next_focused = false;

    const pill_stub = {
        prev: () => {
            return {
                focus: () => {
                    prev_focused = true;
                },
            };
        },
        next: () => {
            return {
                focus: () => {
                    next_focused = true;
                },
            };
        },
    };

    container.set_find_results('.pill:focus', pill_stub);

    // We use the same stub to test both arrows, since we don't
    // actually cause any real state changes here.  We stub out
    // the only interaction, which is to move the focus.
    test_key(LEFT_ARROW);
    assert(prev_focused);

    test_key(RIGHT_ARROW);
    assert(next_focused);
});

run_test('left arrow on input', () => {
    const info = set_up();
    const config = info.config;
    const container = info.container;

    const widget = input_pill.create(config);
    widget.appendValue('blue,red');

    const LEFT_ARROW = 37;
    const key_handler = container.get_on_handler('keydown', '.input');

    let last_pill_focused = false;

    container.set_find_results('.pill', {
        last: () => {
            return {
                focus: () => {
                    last_pill_focused = true;
                },
            };
        },
    });

    key_handler({
        keyCode: LEFT_ARROW,
    });

    assert(last_pill_focused);
});

run_test('comma', () => {
    const info = set_up();
    const config = info.config;
    const items = info.items;
    const pill_input = info.pill_input;
    const container = info.container;

    const widget = input_pill.create(config);
    widget.appendValue('blue,red');

    assert.deepEqual(widget.items(), [
        items.blue,
        items.red,
    ]);

    const COMMA = 188;
    const key_handler = container.get_on_handler('keydown', '.input');

    pill_input.text = () => ' yel';

    key_handler({
        keyCode: COMMA,
        preventDefault: noop,
    });

    assert.deepEqual(widget.items(), [
        items.blue,
        items.red,
    ]);

    pill_input.text = () => ' yellow';

    key_handler({
        keyCode: COMMA,
    });

    assert.deepEqual(widget.items(), [
        items.blue,
        items.red,
        items.yellow,
    ]);
});

run_test('enter key with text', () => {
    const info = set_up();
    const config = info.config;
    const items = info.items;
    const container = info.container;

    const widget = input_pill.create(config);
    widget.appendValue('blue,red');

    assert.deepEqual(widget.items(), [
        items.blue,
        items.red,
    ]);

    const ENTER = 13;
    const key_handler = container.get_on_handler('keydown', '.input');

    key_handler({
        keyCode: ENTER,
        preventDefault: noop,
        stopPropagation: noop,
        target: {
            innerText: ' yellow ',
        },
    });

    assert.deepEqual(widget.items(), [
        items.blue,
        items.red,
        items.yellow,
    ]);
});

run_test('insert_remove', () => {
    const info = set_up();

    const config = info.config;
    const pill_input = info.pill_input;
    const items = info.items;
    const container = info.container;

    const inserted_html = [];
    pill_input.before = function (elem) {
        inserted_html.push(elem.html());
    };

    const widget = input_pill.create(config);

    let created;
    let removed;

    widget.onPillCreate(function () {
        created = true;
    });

    widget.onPillRemove(function () {
        removed = true;
    });

    widget.appendValue('blue,chartreuse,red,yellow,mauve');

    assert(created);
    assert(!removed);

    assert.deepEqual(inserted_html, [
        pill_html('BLUE', 'some_id1', example_img_link),
        pill_html('RED', 'some_id2'),
        pill_html('YELLOW', 'some_id3'),
    ]);

    assert.deepEqual(widget.items(), [
        items.blue,
        items.red,
        items.yellow,
    ]);

    assert.equal(pill_input.text(), 'chartreuse, mauve');

    assert.equal(widget.is_pending(), true);
    widget.clear_text();
    assert.equal(pill_input.text(), '');
    assert.equal(widget.is_pending(), false);

    const BACKSPACE = 8;
    let key_handler = container.get_on_handler('keydown', '.input');

    key_handler({
        keyCode: BACKSPACE,
        target: {
            innerText: '',
        },
        preventDefault: noop,
    });

    assert(removed);

    assert.deepEqual(widget.items(), [
        items.blue,
        items.red,
    ]);

    let next_pill_focused = false;

    const next_pill_stub = {
        focus: () => {
            next_pill_focused = true;
        },
    };

    const focus_pill_stub = {
        next: () => next_pill_stub,
        data: (field) => {
            assert.equal(field, 'id');
            return 'some_id1';
        },
    };

    container.set_find_results('.pill:focus', focus_pill_stub);

    key_handler = container.get_on_handler('keydown', '.pill');
    key_handler({
        keyCode: BACKSPACE,
        preventDefault: noop,
    });

    assert(next_pill_focused);
});

run_test('exit button on pill', () => {
    const info = set_up();

    const config = info.config;
    const items = info.items;
    const container = info.container;

    const widget = input_pill.create(config);

    widget.appendValue('blue,red');

    let next_pill_focused = false;

    const next_pill_stub = {
        focus: () => {
            next_pill_focused = true;
        },
    };

    const curr_pill_stub = {
        next: () => next_pill_stub,
        data: (field) => {
            assert.equal(field, 'id');
            return 'some_id1';
        },
    };

    const exit_button_stub = {
        to_$: () => {
            return {
                closest: (sel) => {
                    assert.equal(sel, '.pill');
                    return curr_pill_stub;
                },
            };
        },
    };

    const e = {
        stopPropagation: noop,
    };
    const exit_click_handler = container.get_on_handler('click', '.exit');

    exit_click_handler.call(exit_button_stub, e);

    assert(next_pill_focused);

    assert.deepEqual(widget.items(), [
        items.red,
    ]);

});

run_test('misc things', () => {
    const info = set_up();

    const config = info.config;
    const container = info.container;
    const pill_input = info.pill_input;

    const widget = input_pill.create(config);

    // animation
    const animation_end_handler = container.get_on_handler('animationend', '.input');

    let shake_class_removed = false;

    const input_stub = {
        to_$: () => {
            return {
                removeClass: (cls) => {
                    assert.equal(cls, 'shake');
                    shake_class_removed = true;
                },
            };
        },
    };

    animation_end_handler.call(input_stub);
    assert(shake_class_removed);

    // bad data
    blueslip.expect('error', 'no display_value returned');
    widget.appendValidatedData('this-has-no-item-attribute');

    // click on container
    const container_click_handler = container.get_on_handler('click');

    const stub = $.create('the-pill-container');
    stub.set_find_results('.input', pill_input);
    stub.is = (sel) => {
        assert.equal(sel, '.pill-container');
        return true;
    };

    const this_ = {
        to_$: () => stub,
    };

    container_click_handler.call(this_, {target: this_});
});
