import buildHTMLToMarkdownRenderer from '~/vue_shared/components/rich_content_editor/services/build_html_to_markdown_renderer';
import { attributeDefinition } from './renderers/mock_data';

describe('rich_content_editor/services/html_to_markdown_renderer', () => {
  let baseRenderer;
  let htmlToMarkdownRenderer;
  let fakeNode;

  beforeEach(() => {
    baseRenderer = {
      trim: jest.fn(input => `trimmed ${input}`),
      getSpaceCollapsedText: jest.fn(input => `space collapsed ${input}`),
      getSpaceControlled: jest.fn(input => `space controlled ${input}`),
      convert: jest.fn(),
    };

    fakeNode = { nodeValue: 'mock_node', dataset: {} };
  });

  afterEach(() => {
    htmlToMarkdownRenderer = null;
  });

  describe('TEXT_NODE visitor', () => {
    it('composes getSpaceControlled, getSpaceCollapsedText, and trim services', () => {
      htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer);

      expect(htmlToMarkdownRenderer.TEXT_NODE(fakeNode)).toBe(
        `space controlled trimmed space collapsed ${fakeNode.nodeValue}`,
      );
    });
  });

  describe('LI OL, LI UL visitor', () => {
    const oneLevelNestedList = '\n    * List item 1\n    * List item 2';
    const twoLevelNestedList = '\n  * List item 1\n    * List item 2';
    const spaceInContentList = '\n  * List    item 1\n  * List item 2';

    it.each`
      list                  | indentSpaces | result
      ${oneLevelNestedList} | ${2}         | ${'\n  * List item 1\n  * List item 2'}
      ${oneLevelNestedList} | ${3}         | ${'\n   * List item 1\n   * List item 2'}
      ${oneLevelNestedList} | ${6}         | ${'\n      * List item 1\n      * List item 2'}
      ${twoLevelNestedList} | ${4}         | ${'\n    * List item 1\n        * List item 2'}
      ${spaceInContentList} | ${1}         | ${'\n * List    item 1\n * List item 2'}
    `('changes the list indentation to $indentSpaces spaces', ({ list, indentSpaces, result }) => {
      htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer, {
        subListIndentSpaces: indentSpaces,
      });

      baseRenderer.convert.mockReturnValueOnce(list);

      expect(htmlToMarkdownRenderer['LI OL, LI UL'](fakeNode, list)).toBe(result);
      expect(baseRenderer.convert).toHaveBeenCalledWith(fakeNode, list);
    });
  });

  describe('UL LI visitor', () => {
    it.each`
      listItem           | unorderedListBulletChar | result             | bulletChar
      ${'* list item'}   | ${undefined}            | ${'- list item'}   | ${'default'}
      ${'  - list item'} | ${'*'}                  | ${'  * list item'} | ${'*'}
      ${'  * list item'} | ${'-'}                  | ${'  - list item'} | ${'-'}
    `(
      'uses $bulletChar bullet char in unordered list items when $unorderedListBulletChar is set in config',
      ({ listItem, unorderedListBulletChar, result }) => {
        htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer, {
          unorderedListBulletChar,
        });
        baseRenderer.convert.mockReturnValueOnce(listItem);

        expect(htmlToMarkdownRenderer['UL LI'](fakeNode, listItem)).toBe(result);
        expect(baseRenderer.convert).toHaveBeenCalledWith(fakeNode, listItem);
      },
    );

    it('detects attribute definitions and attaches them to the list item', () => {
      const listItem = '- list item';
      const result = `${listItem}\n${attributeDefinition}\n`;

      fakeNode.dataset.attributeDefinition = attributeDefinition;
      htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer);
      baseRenderer.convert.mockReturnValueOnce(`${listItem}\n`);

      expect(htmlToMarkdownRenderer['UL LI'](fakeNode, listItem)).toBe(result);
    });
  });

  describe('OL LI visitor', () => {
    it.each`
      listItem              | result              | incrementListMarker | action
      ${'2. list item'}     | ${'1. list item'}   | ${false}            | ${'increments'}
      ${'  3. list item'}   | ${'  1. list item'} | ${false}            | ${'increments'}
      ${'  123. list item'} | ${'  1. list item'} | ${false}            | ${'increments'}
      ${'3. list item'}     | ${'3. list item'}   | ${true}             | ${'does not increment'}
    `(
      '$action a list item counter when incrementListMaker is $incrementListMarker',
      ({ listItem, result, incrementListMarker }) => {
        const subContent = null;

        htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer, {
          incrementListMarker,
        });
        baseRenderer.convert.mockReturnValueOnce(listItem);

        expect(htmlToMarkdownRenderer['OL LI'](fakeNode, subContent)).toBe(result);
        expect(baseRenderer.convert).toHaveBeenCalledWith(fakeNode, subContent);
      },
    );
  });

  describe('STRONG, B visitor', () => {
    it.each`
      input                | strongCharacter | result
      ${'**strong text**'} | ${'_'}          | ${'__strong text__'}
      ${'__strong text__'} | ${'*'}          | ${'**strong text**'}
    `(
      'converts $input to $result when strong character is $strongCharacter',
      ({ input, strongCharacter, result }) => {
        htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer, {
          strong: strongCharacter,
        });

        baseRenderer.convert.mockReturnValueOnce(input);

        expect(htmlToMarkdownRenderer['STRONG, B'](fakeNode, input)).toBe(result);
        expect(baseRenderer.convert).toHaveBeenCalledWith(fakeNode, input);
      },
    );
  });

  describe('EM, I visitor', () => {
    it.each`
      input              | emphasisCharacter | result
      ${'*strong text*'} | ${'_'}            | ${'_strong text_'}
      ${'_strong text_'} | ${'*'}            | ${'*strong text*'}
    `(
      'converts $input to $result when emphasis character is $emphasisCharacter',
      ({ input, emphasisCharacter, result }) => {
        htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer, {
          emphasis: emphasisCharacter,
        });

        baseRenderer.convert.mockReturnValueOnce(input);

        expect(htmlToMarkdownRenderer['EM, I'](fakeNode, input)).toBe(result);
        expect(baseRenderer.convert).toHaveBeenCalledWith(fakeNode, input);
      },
    );
  });

  describe('H1, H2, H3, H4, H5, H6 visitor', () => {
    it('detects attribute definitions and attaches them to the heading', () => {
      const heading = 'heading text';
      const result = `${heading.trimRight()}\n${attributeDefinition}\n\n`;

      fakeNode.dataset.attributeDefinition = attributeDefinition;
      htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer);
      baseRenderer.convert.mockReturnValueOnce(`${heading}\n\n`);

      expect(htmlToMarkdownRenderer['H1, H2, H3, H4, H5, H6'](fakeNode, heading)).toBe(result);
    });
  });

  describe('PRE CODE', () => {
    let node;
    const subContent = 'sub content';
    const originalConverterResult = 'base result';

    beforeEach(() => {
      node = document.createElement('PRE');

      node.innerText = 'reference definition content';
      node.dataset.sseReferenceDefinition = true;

      baseRenderer.convert.mockReturnValueOnce(originalConverterResult);
      htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer);
    });

    it('returns raw text when pre node has sse-reference-definitions class', () => {
      expect(htmlToMarkdownRenderer['PRE CODE'](node, subContent)).toBe(
        `\n\n${node.innerText}\n\n`,
      );
    });

    it('returns base result when pre node does not have sse-reference-definitions class', () => {
      delete node.dataset.sseReferenceDefinition;

      expect(htmlToMarkdownRenderer['PRE CODE'](node, subContent)).toBe(originalConverterResult);
    });
  });

  describe('IMG', () => {
    const originalSrc = 'path/to/image.png';
    const alt = 'alt text';
    let node;

    beforeEach(() => {
      node = document.createElement('img');
      node.alt = alt;
      node.src = originalSrc;
    });

    it('returns an image with its original src of the `original-src` attribute is preset', () => {
      node.dataset.originalSrc = originalSrc;
      node.src = 'modified/path/to/image.png';

      htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer);

      expect(htmlToMarkdownRenderer.IMG(node)).toBe(`![${alt}](${originalSrc})`);
    });

    it('fallback to `src` if no `original-src` is specified on the image', () => {
      htmlToMarkdownRenderer = buildHTMLToMarkdownRenderer(baseRenderer);
      expect(htmlToMarkdownRenderer.IMG(node)).toBe(`![${alt}](${originalSrc})`);
    });
  });
});
