import {EditorState, ContentState, SelectionState, RichUtils} from 'draft-js';
import reducer from '..';
import {applyLink} from '../../actions/toolbar';
import {LIMIT_CHARACTERS_OVERFLOW_STYLE} from 'core/editor3/helpers/characters-limit';

/**
 * @description Creates a new store state that contains the editorState and searchTerm.
 * @param {string} txt The text in the editor
 * @param {Object} searchTerm The searchTerm data (index, pattern, caseSensitive)
 * @returns {Object}
 */
function withSearchTerm(txt, searchTerm) {
    const editorState = EditorState.createWithContent(ContentState.createFromText(txt));
    const onChangeValue = function() {
        // noop
    };

    return {editorState, searchTerm, onChangeValue};
}

function fakeTabEvent({shift = false} = {}) {
    return new KeyboardEvent('keydown', {
        key: 'Tab',
        shiftKey: shift,
    });
}

describe('editor3.reducers', () => {
    it('EDITOR_CHANGE_STATE', () => {
        const editorState = EditorState.createEmpty();
        const onChangeValue = jasmine.createSpy();

        reducer({
            onChangeValue: onChangeValue,
            editorState: EditorState.createEmpty(),
        }, {
            type: 'EDITOR_CHANGE_STATE',
            payload: {editorState},
        });

        expect(onChangeValue).toHaveBeenCalled();
    });

    it('EDITOR_DRAG_DROP', () => {
        const data = {a: 1};

        const startState = {
            editorState: EditorState.createEmpty(),
            onChangeValue: () => ({}),
        };

        const {editorState} = reducer(startState, {
            type: 'EDITOR_DRAG_DROP',
            payload: {data: data, blockKey: null},
        });

        const contentState = editorState.getCurrentContent();
        const entityKey = contentState.getLastCreatedEntityKey();
        const entity = contentState.getEntity(entityKey);

        expect(entity.getType()).toBe('MEDIA');
        expect(entity.getMutability()).toBe('MUTABLE');
        expect(entity.getData()).toEqual({media: {a: 1}});
    });

    it('HIGHLIGHTS_RENDER highlight', () => {
        const startState = withSearchTerm(
            'apple banana apple ananas apple prune',
            {index: 1, pattern: 'Apple', caseSensitive: false},
        );

        const state = reducer(startState, {type: 'HIGHLIGHTS_RENDER'});
        const block = state.editorState.getCurrentContent().getFirstBlock();

        expect(block.getInlineStyleAt(0).has('HIGHLIGHT')).toBe(true);
        expect(block.getInlineStyleAt(13).has('HIGHLIGHT_STRONG')).toBe(true);
        expect(block.getInlineStyleAt(22).has('HIGHLIGHT')).toBe(false);
        expect(block.getInlineStyleAt(22).has('HIGHLIGHT_STRONG')).toBe(false);
        expect(block.getInlineStyleAt(26).has('HIGHLIGHT')).toBe(true);
    });

    it('HIGHLIGHTS_RENDER case sensitive', () => {
        const startState = withSearchTerm(
            'apple banana apple ananas apple prune',
            {index: 1, pattern: 'Apple', caseSensitive: true},
        );

        const state = reducer(startState, {type: 'HIGHLIGHTS_RENDER'});
        const block = state.editorState.getCurrentContent().getFirstBlock();

        expect(block.getInlineStyleAt(0).has('HIGHLIGHT')).toBe(false);
        expect(block.getInlineStyleAt(13).has('HIGHLIGHT_STRONG')).toBe(false);
    });

    it('HIGHLIGHTS_RENDER special characters', () => {
        const startState = withSearchTerm(
            '?apple banana ?apple ananas apple prune',
            {index: 1, pattern: '?Apple', caseSensitive: false},
        );

        const state = reducer(startState, {type: 'HIGHLIGHTS_RENDER'});
        const block = state.editorState.getCurrentContent().getFirstBlock();

        expect(block.getInlineStyleAt(0).has('HIGHLIGHT')).toBe(true);
        expect(block.getInlineStyleAt(1).has('HIGHLIGHT')).toBe(true);
        expect(block.getInlineStyleAt(14).has('HIGHLIGHT_STRONG')).toBe(true);
        expect(block.getInlineStyleAt(15).has('HIGHLIGHT_STRONG')).toBe(true);
    });

    it('HIGHLIGHTS_CRITERIA change term', () => {
        const startState = withSearchTerm(
            'apple banana apple ananas apple prune',
            {index: 1, pattern: 'apple', caseSensitive: false},
        );

        const state = reducer(startState, {
            type: 'HIGHLIGHTS_CRITERIA',
            payload: {
                diff: {banana: 'Banana'},
                caseSensitive: true,
            },
        });

        expect(state.searchTerm.index).toBe(-1);
        expect(state.searchTerm.caseSensitive).toBe(true);
        expect(state.searchTerm.pattern).toBe('banana');
    });

    it('HIGHLIGHTS_CRITERIA change sensitivity', () => {
        const startState = withSearchTerm(
            'apple banana apple ananas apple prune',
            {index: 1, pattern: 'apple', caseSensitive: false},
        );

        const state = reducer(startState, {
            type: 'HIGHLIGHTS_CRITERIA',
            payload: {caseSensitive: true, diff: {apple: 'Apple'}},
        });

        expect(state.searchTerm.index).toBe(0);
    });

    it('HIGHLIGHTS_FIND_NEXT', () => {
        const startState = withSearchTerm(
            'apple banana apple ananas apple prune',
            {index: 1, pattern: 'Apple', caseSensitive: false},
        );

        const state = reducer(startState, {type: 'HIGHLIGHTS_FIND_NEXT'});
        const block = state.editorState.getCurrentContent().getFirstBlock();

        expect(state.searchTerm.index).toBe(2);
        expect(block.getInlineStyleAt(0).has('HIGHLIGHT')).toBe(true);
        expect(block.getInlineStyleAt(13).has('HIGHLIGHT')).toBe(true);
        expect(block.getInlineStyleAt(26).has('HIGHLIGHT_STRONG')).toBe(true);
    });

    it('HIGHLIGHTS_FIND_NEXT past last', () => {
        const startState = withSearchTerm(
            'apple banana apple ananas apple prune',
            {index: 2, pattern: 'Apple', caseSensitive: false},
        );

        const state = reducer(startState, {type: 'HIGHLIGHTS_FIND_NEXT'});
        const block = state.editorState.getCurrentContent().getFirstBlock();

        expect(state.searchTerm.index).toBe(0);
        expect(block.getInlineStyleAt(0).has('HIGHLIGHT_STRONG')).toBe(true);
        expect(block.getInlineStyleAt(13).has('HIGHLIGHT')).toBe(true);
        expect(block.getInlineStyleAt(26).has('HIGHLIGHT')).toBe(true);
    });

    it('HIGHLIGHTS_FIND_NEXT wrong index', () => {
        const startState = withSearchTerm(
            'apple banana apple ananas apple prune',
            {index: 5, pattern: 'Apple', caseSensitive: false},
        );

        const state = reducer(startState, {type: 'HIGHLIGHTS_FIND_NEXT'});
        const block = state.editorState.getCurrentContent().getFirstBlock();

        expect(state.searchTerm.index).toBe(0);
        expect(block.getInlineStyleAt(0).has('HIGHLIGHT_STRONG')).toBe(true);
        expect(block.getInlineStyleAt(13).has('HIGHLIGHT')).toBe(true);
        expect(block.getInlineStyleAt(26).has('HIGHLIGHT')).toBe(true);
    });

    it('HIGHLIGHTS_FIND_PREV', () => {
        const startState = withSearchTerm(
            'apple banana apple ananas apple prune',
            {index: 1, pattern: 'Apple', caseSensitive: false, diff: {Apple: 'apple'}},
        );

        const state = reducer(startState, {type: 'HIGHLIGHTS_FIND_PREV'});
        const block = state.editorState.getCurrentContent().getFirstBlock();

        expect(state.searchTerm.index).toBe(0);
        expect(block.getInlineStyleAt(0).has('HIGHLIGHT_STRONG')).toBe(true);
        expect(block.getInlineStyleAt(13).has('HIGHLIGHT')).toBe(true);
        expect(block.getInlineStyleAt(26).has('HIGHLIGHT')).toBe(true);
    });

    it('HIGHLIGHTS_FIND_PREV before first', () => {
        const startState = withSearchTerm(
            'apple banana apple ananas apple prune',
            {index: 0, pattern: 'Apple', caseSensitive: false, diff: {Apple: 'apple'}},
        );

        const state = reducer(startState, {type: 'HIGHLIGHTS_FIND_PREV'});
        const block = state.editorState.getCurrentContent().getFirstBlock();

        expect(state.searchTerm.index).toBe(2);
        expect(block.getInlineStyleAt(0).has('HIGHLIGHT')).toBe(true);
        expect(block.getInlineStyleAt(13).has('HIGHLIGHT')).toBe(true);
        expect(block.getInlineStyleAt(26).has('HIGHLIGHT_STRONG')).toBe(true);
    });

    it('HIGHLIGHTS_FIND_PREV wrong index', () => {
        const startState = withSearchTerm(
            'apple banana apple ananas apple prune',
            {index: -5, pattern: 'Apple', caseSensitive: false, diff: {Apple: 'apple'}},
        );

        const state = reducer(startState, {type: 'HIGHLIGHTS_FIND_PREV'});
        const block = state.editorState.getCurrentContent().getFirstBlock();

        expect(state.searchTerm.index).toBe(2);
        expect(block.getInlineStyleAt(0).has('HIGHLIGHT')).toBe(true);
        expect(block.getInlineStyleAt(13).has('HIGHLIGHT')).toBe(true);
        expect(block.getInlineStyleAt(26).has('HIGHLIGHT_STRONG')).toBe(true);
    });

    it('HIGHLIGHTS_FIND_REPLACE', () => {
        const startState = withSearchTerm(
            'apple banana apple ananas apple prune',
            {index: 1, pattern: 'Apple', caseSensitive: false},
        );

        const state = reducer(startState, {
            type: 'HIGHLIGHTS_REPLACE',
            payload: 'kiwi',
        });

        const text = state.editorState.getCurrentContent().getPlainText('\n');

        expect(text).toBe('apple banana kiwi ananas apple prune');
    });

    it('HIGHLIGHTS_FIND_REPLACE_ALL', () => {
        const startState = withSearchTerm(
            'apple banana apple ananas apple prune',
            {index: 1, pattern: 'Apple', caseSensitive: false},
        );

        const state = reducer(startState, {
            type: 'HIGHLIGHTS_REPLACE_ALL',
            payload: 'kiwi',
        });

        const text = state.editorState.getCurrentContent().getPlainText('\n');

        expect(text).toBe('kiwi banana kiwi ananas kiwi prune');
    });

    it('SPELLCHECKER_REPLACE_WORD', () => {
        const editorState = EditorState.createWithContent(
            ContentState.createFromText('abcd efgh'),
        );

        const state = reducer({
            editorState: editorState,
            onChangeValue: () => { /* no-op */ },
        }, {
            type: 'SPELLCHECKER_REPLACE_WORD',
            payload: {
                word: {text: 'efgh', offset: 5},
                newWord: '1234',
            },
        });

        const text = state.editorState.getCurrentContent().getPlainText();

        expect(text).toBe('abcd 1234');
    });

    it('TOOLBAR_APPLY_LINK', () => {
        const contentState = ContentState.createFromText('some text');

        const blockKey = contentState.getFirstBlock().getKey();
        const selectionState = SelectionState.createEmpty(blockKey);
        const updatedSelection = selectionState.merge({
            focusKey: blockKey,
            focusOffset: 4,
        }) as SelectionState;

        const editorState = EditorState.createWithContent(contentState);
        const selectedEditorState = EditorState.forceSelection(editorState, updatedSelection);

        const state = reducer(
            {
                editorState: selectedEditorState,
                onChangeValue: jasmine.createSpy('onChangeValue'),
            },
            applyLink({link: 'http://example.com'}),
        );

        let updatedContent = state.editorState.getCurrentContent();
        let entity = updatedContent.getEntity(updatedContent.getLastCreatedEntityKey());

        expect(entity.type).toBe('LINK');
        expect(entity.data.link.link).toBe('http://example.com');
        expect(state.onChangeValue).toHaveBeenCalled();

        const nextState = reducer(
            {
                editorState: state.editorState,
                onChangeValue: jasmine.createSpy('onChangeValue'),
            },
            applyLink({link: 'http://foo.com'}, entity),
        );

        updatedContent = nextState.editorState.getCurrentContent();
        entity = updatedContent.getEntity(updatedContent.getLastCreatedEntityKey());

        expect(entity.data.link.link).toBe('http://foo.com');
        expect(nextState.onChangeValue).toHaveBeenCalled();
    });

    it('EDITOR_PUSH_STATE', () => {
        const contentState = ContentState.createFromText('some text');
        const nextContentState = ContentState.createFromText('some other text');

        const nextState = reducer({
            editorState: EditorState.createWithContent(contentState),
            onChangeValue: jasmine.createSpy('onChangeValue'),
        }, {
            type: 'EDITOR_PUSH_STATE',
            payload: {contentState: nextContentState},
        });

        expect(nextState.editorState.getCurrentContent().getPlainText()).toBe(nextContentState.getPlainText());
    });

    it('EDITOR_TAB insert tab character', () => {
        const contentState = ContentState.createFromText('foo');
        let editorState = EditorState.createWithContent(contentState);

        editorState = EditorState.moveFocusToEnd(editorState);

        const nextState = reducer({
            editorState: editorState,
            editorFormat: ['tab', 'tab as spaces'],
            onChangeValue: jasmine.createSpy('onChangeValue'),
        }, {
            type: 'EDITOR_TAB',
            payload: fakeTabEvent({shift: false}),
        });

        expect(nextState.editorState.getCurrentContent().getPlainText()).toBe('foo\t');
    });

    it('EDITOR_TAB insert tab as spaces when pressing shift', () => {
        const contentState = ContentState.createFromText('foo');
        let editorState = EditorState.createWithContent(contentState);

        editorState = EditorState.moveFocusToEnd(editorState);

        const nextState = reducer({
            editorState: editorState,
            editorFormat: ['tab', 'tab as spaces'],
            onChangeValue: jasmine.createSpy('onChangeValue'),
        }, {
            type: 'EDITOR_TAB',
            payload: fakeTabEvent({shift: true}),
        });

        expect(nextState.editorState.getCurrentContent().getPlainText()).toBe('foo        ');
    });

    it('EDITOR_TAB does not insert anything without formatting options', () => {
        const contentState = ContentState.createFromText('foo');
        let editorState = EditorState.createWithContent(contentState);

        editorState = EditorState.moveFocusToEnd(editorState);

        const nextState = reducer({
            editorState: editorState,
            onChangeValue: jasmine.createSpy('onChangeValue'),
        }, {
            type: 'EDITOR_TAB',
            payload: fakeTabEvent({shift: false}),
        });

        expect(nextState.editorState.getCurrentContent().getPlainText()).toBe('foo');
    });

    it('EDITOR_TAB on lists does not change text', () => {
        const contentState = ContentState.createFromText('list item');
        let editorState = EditorState.createWithContent(contentState);

        editorState = RichUtils.toggleBlockType(editorState, 'ordered-list-item');

        // indent right
        let nextState = reducer({
            editorState,
            editorFormat: ['tab', 'tab as spaces'],
            onChangeValue: jasmine.createSpy('onChangeValue'),
        }, {
            type: 'EDITOR_TAB',
            payload: fakeTabEvent(),
        });

        editorState = nextState.editorState as EditorState;

        expect(editorState.getCurrentContent().getFirstBlock().getText()).toBe('list item');

        // indent left
        nextState = reducer({
            editorState,
            editorFormat: ['tab', 'tab as spaces'],
            onChangeValue: jasmine.createSpy('onChangeValue'),
        }, {
            type: 'EDITOR_TAB',
            payload: fakeTabEvent({shift: true}),
        });

        editorState = nextState.editorState as EditorState;

        expect(editorState.getCurrentContent().getFirstBlock().getText()).toBe('list item');
    });

    it('EDITOR_CHANGE_LIMIT_CONFIG changes the config', () => {
        const nextState = reducer({
            limitConfig: {ui: 'limit', chars: 5},
        }, {
            type: 'EDITOR_CHANGE_LIMIT_CONFIG',
            payload: {ui: 'highlight', chars: 10},
        });

        expect(nextState.limitConfig.ui).toBe('highlight');
        expect(nextState.limitConfig.chars).toBe(10);
    });
});
