import { FocusTools, Waiter } from '@ephox/agar';
import { describe, it, beforeEach, afterEach } from '@ephox/bedrock-client';
import { PlatformDetection } from '@ephox/sand';
import { SugarDocument } from '@ephox/sugar';
import { TinyAssertions, TinyHooks, TinyUiActions, TinySelections, TinyContentActions } from '@ephox/wrap-mcagar';
import { assert } from 'chai';

import type Editor from 'tinymce/core/api/Editor';
import type { ExecCommandEvent } from 'tinymce/core/api/EventTypes';
import type { EditorEvent } from 'tinymce/core/api/util/EventDispatcher';
import Plugin from 'tinymce/plugins/link/Plugin';

describe('browser.tinymce.plugins.link.DialogOpenTest', () => {
  const metaKey = PlatformDetection.detect().os.isMacOS() ? { metaKey: true } : { ctrlKey: true };
  let dispatched: boolean;

  const hook = TinyHooks.bddSetup<Editor>({
    plugins: 'link',
    toolbar: 'link',
    base_url: '/project/tinymce/js/tinymce',
  }, [ Plugin ]);

  beforeEach(() => {
    hook.editor().on('ExecCommand', checkDispatch);
    dispatched = false;
  });

  afterEach(() => {
    hook.editor().off('ExecCommand', checkDispatch);
  });

  const checkDispatch = (evt: EditorEvent<ExecCommandEvent>) => {
    if (evt.command.toLowerCase() === 'mcelink') {
      dispatched = true;
    }
  };

  const pDialogCheck = async (editor: Editor) => {
    await TinyUiActions.pWaitForDialog(editor);
    TinyUiActions.closeDialog(editor);
    assert.isTrue(dispatched);
    dispatched = false;
  };

  const pTestOpenBothDialog = async (editor: Editor) => {
    TinyUiActions.clickOnMenu(editor, 'button:contains("Insert")');
    await TinyUiActions.pWaitForUi(editor, '[role="menu"]');
    TinyUiActions.clickOnUi(editor, `[role="menu"] [aria-label="Link…"]`);
    await pDialogCheck(editor);
    TinyUiActions.clickOnToolbar(editor, 'div.tox-toolbar__group > button');
    await pDialogCheck(editor);
  };

  it('TINY-8057: Checking dialog opens from toolbar and menu', async () => {
    const editor = hook.editor();
    await pTestOpenBothDialog(editor);
  });

  it('TINY-8057: Checking dialog opens from toolbar and menu with quicklink enabled', async () => {
    const editor = hook.editor();
    editor.options.set('link_quicklink', true);
    await pTestOpenBothDialog(editor);
    editor.options.unset('link_quicklink');
  });

  it('TINY-8057: Checking dialog opens from keyboard shortcut', async () => {
    const editor = hook.editor();
    TinyContentActions.keystroke(editor, 'K'.charCodeAt(0), metaKey);
    await pDialogCheck(editor);
  });

  const pTestDialogOnSelection = async (
    startPath: number[],
    soffset: number,
    finishPath: number[],
    foffset: number,
    result: string
  ) => {
    const editor = hook.editor();
    editor.setContent('<p>aaa bbb ccc</p><p>ddd</p>');
    TinySelections.setSelection(editor, startPath, soffset, finishPath, foffset);
    editor.execCommand('mceLink');
    await TinyUiActions.pWaitForDialog(editor);
    FocusTools.setActiveValue(SugarDocument.getDocument(), 'http://tiny.cloud');
    TinyUiActions.submitDialog(editor);
    await Waiter.pTryUntil('Wait for content to change', () =>
      TinyAssertions.assertContent(editor, result));
    // TINY-11593: Set cursor back into link to verify change in dialog
    TinySelections.select(editor, 'a', []);
    editor.execCommand('mceLink');
    await TinyUiActions.pWaitForDialog(editor);
    assert.equal(FocusTools.getActiveValue(SugarDocument.getDocument()), 'http://tiny.cloud', 'Should be equal to link\'s href');
    await pDialogCheck(editor);
  };

  it(`TINY-7993: Dialog shows correctly right after inserting a link: selection at the beginning`, async () => {
    await pTestDialogOnSelection(
      [ 0, 0 ],
      0,
      [ 0, 0 ],
      'aaa'.length,
      `<p><a href="http://tiny.cloud">aaa</a> bbb ccc</p>\n<p>ddd</p>`
    );
  });

  it(`TINY-7993: Dialog shows correctly right after inserting a link: selection in the middle`, async () => {
    await pTestDialogOnSelection(
      [ 0, 0 ],
      'aaa '.length,
      [ 0, 0 ],
      'aaa bbb'.length,
      `<p>aaa <a href="http://tiny.cloud">bbb</a> ccc</p>\n<p>ddd</p>`
    );
  });

  it(`TINY-7993: Dialog shows correctly right after inserting a link: selection at the end`, async () => {
    await pTestDialogOnSelection(
      [ 0, 0 ],
      'aaa bbb '.length,
      [ 0, 0 ],
      'aaa bbb ccc'.length,
      `<p>aaa bbb <a href="http://tiny.cloud">ccc</a></p>\n<p>ddd</p>`
    );
  });

  it(`TINY-7993: Dialog shows correctly right after inserting a link: whole paragraph selection`, async () => {
    await pTestDialogOnSelection(
      [ 1, 0 ],
      0,
      [ 1, 0 ],
      'ddd'.length,
      `<p>aaa bbb ccc</p>\n<p><a href="http://tiny.cloud">ddd</a></p>`
    );
  });

  it(`TINY-7993: Dialog shows correctly right after inserting a link: selection splitted between paragraphs`, async () => {
    await pTestDialogOnSelection(
      [ 0, 0 ],
      'aaa bbb '.length,
      [ 1, 0 ],
      'ddd'.length,
      `<p>aaa bbb <a href="http://tiny.cloud">ccc</a></p>\n<p><a href="http://tiny.cloud">ddd</a></p>`
    );
  });
});
