import { Assert, UnitTest } from '@ephox/bedrock-client';
import { Arr } from '@ephox/katamari';
import { Compare, Hierarchy, Html, Insert, InsertAll, Remove, SugarBody, SugarElement } from '@ephox/sugar';

import * as DomClumps from 'ephox/robin/api/dom/DomClumps';

UnitTest.test('DomClumpsTest', () => {
  const body = SugarBody.body();

  const container = SugarElement.fromTag('div');

  Insert.append(body, container);

  const find = (path: number[]) => {
    return Hierarchy.follow(container, path).getOrDie('Could not find the path: ' + path.join(','));
  };

  const isRoot = (elem: SugarElement) => {
    return Compare.eq(elem, container);
  };

  const mark = (res: SugarElement[]) => {
    if (res.length > 0) {
      const strong = SugarElement.fromTag('strong');
      Insert.before(res[0], strong);
      InsertAll.append(strong, res);
    }
  };

  const checkFracture = (expected: string, start: number[], soffset: number, finish: number[], foffset: number) => {
    DomClumps.fracture(isRoot, find(start), soffset, find(finish), foffset).each(mark);
    Assert.eq('', expected, Html.get(container));
  };

  const checkFractures = (expected: string, start: number[], soffset: number, finish: number[], foffset: number) => {
    const sections = DomClumps.fractures(isRoot, find(start), soffset, find(finish), foffset);
    Arr.each(sections, mark);
    Assert.eq('', expected, Html.get(container));
  };

  container.dom.innerHTML =
    '<p>One</p>' +
    '<table><tbody><tr><th>Heading</th></tr><tr><td>Data</td></tr></tbody></table>' +
    '<p>Two</p>';
  // Highlighting over a table from O|ne -> T|wo
  checkFractures(
    '<p>O<strong>ne</strong></p>' +
    '<table><tbody><tr><th><strong>Heading</strong></th></tr><tr><td><strong>Data</strong></td></tr></tbody></table>' +
    '<p><strong>T</strong>wo</p>',
    [ 0, 0 ], 'O'.length, [ 2, 0 ], 'T'.length
  );

  container.dom.innerHTML =
    '<p>This is the second line.</p>' +
    '<h3>This is the heading line.</h3>' +
    '<ol><li>apples</li><li>oranges</li>  </ol>' +
    'and more' +
    '<br>' +
    '<p>the</p>' +
    'something one' +
    '<br>';
  // Highlighting from This is the s|econd line -> someth|ing
  checkFractures(
    '<p>This is the s<strong>econd line.</strong></p>' +
    '<h3><strong>This is the heading line.</strong></h3>' +
    '<ol><li><strong>apples</strong></li><li><strong>oranges</strong></li>  </ol>' +
    '<strong>and more<br></strong>' +
    '<p><strong>the</strong></p>' +
    '<strong>someth</strong>ing one' +
    '<br>',
    [ 0, 0 ], 'This is the s'.length, [ 6 ], 'someth'.length
  );

  container.dom.innerHTML =
    '<ol><li>apples</li><li>oranges</li>  </ol><p>And breaking it:</p><ol><li>here</li></ol>and more<br>and more<br>and m<b>or</b>e<br>';
  // Highlighting from And bre|aking -> and mo|re
  checkFractures(
    '<ol><li>apples</li><li>oranges</li>  </ol><p>And bre<strong>aking it:</strong></p><ol><li><strong>here</strong></li></ol><strong>and more<br>and mo</strong>re<br>and m<b>or</b>e<br>',
    [ 1, 0 ], 7, [ 5 ], 6
  );

  container.dom.innerHTML =
    'Text n<span style="font-weight: bold;">ode <br> Another text node <br></span>' +
    '<h3><span style="font-weight: bold;">This is the heading line.</span></h3>';
  // Highlighting from Anoth|er text node -> Thi|is is the heading line
  checkFractures(
    'Text n<span style="font-weight: bold;">ode <br> Anot<strong>her text node <br></strong></span>' +
    '<h3><span style="font-weight: bold;"><strong>Thi</strong>s is the heading line.</span></h3>',
    [ 1, 2 ], 5, [ 2, 0, 0 ], 3
  );

  container.dom.innerHTML =
    'Text node <br> Another text node <br><p>This is the first line of what I am typing</p><p>This is the second line.</p>';
  // Highlighting from Text n|ode -> This is th|e second line
  checkFractures(
    'Text n<strong>ode <br> Another text node <br></strong><p><strong>This is the first line of what I am typing</strong></p>' +
    '<p><strong>This is th</strong>e second line.</p>',
    [ 0 ], 'Text n'.length, [ 5, 0 ], 'This is th'.length);

  container.dom.innerHTML = '<p>This is <b>bold text</b> and <i>italic text</i> here.</p>';
  // Highlighting from This| -> an|d
  checkFracture(
    '<p>This<strong> is <b>bold text</b> an</strong>d <i>italic text</i> here.</p>',
    [ 0, 0 ], 'This'.length, [ 0, 2 ], ' an'.length
  );

  container.dom.innerHTML = '<p>This is <b>bold text</b> and <i>italic text</i> here.</p>';
  // Highlighting from <b> -> </b>
  checkFracture(
    '<p>This is <strong><b>bold text</b></strong> and <i>italic text</i> here.</p>',
    [ 0 ], 1, [ 0 ], 2
  );

  container.dom.innerHTML = '<p>This is <b>bold text</b> and <i>italic text</i> here.</p>';
  // Highlighting from <b> to text|</b>
  checkFracture(
    '<p>This is <strong><b>bold text</b></strong> and <i>italic text</i> here.</p>',
    [ 0 ], 1, [ 0, 1 ], 1
  );

  container.dom.innerHTML = '<p>This is <b>bold text</b> and <i>italic text</i> here.</p>';
  checkFracture(
    '<p>This is <b><strong>bold</strong> text</b> and <i>italic text</i> here.</p>',
    [ 0 ], 1, [ 0, 1, 0 ], 'bold'.length
  );

  container.dom.innerHTML = '<p>This is <b>bold text</b> and <i>italic text</i> here.</p>';
  checkFracture(
    '<p>T<strong>his is <b>bold</b></strong><b> text</b> and <i>italic text</i> here.</p>',
    [ 0, 0 ], 'T'.length, [ 0, 1, 0 ], 'bold'.length
  );

  container.dom.innerHTML = '<p>This is <b>bold text</b> and <i>italic text</i> here.</p>';
  checkFracture(
    '<p>This is <b>bold text</b><strong> and </strong><i>italic text</i> here.</p>',
    [ 0 ], 2, [ 0 ], 3
  );

  container.dom.innerHTML = '<p>This is <b>bold text</b> and <i>italic text</i> here.</p>';
  checkFracture(
    '<p>This is <b>bold text</b> and <strong><i>italic text</i></strong> here.</p>',
    [ 0 ], 3, [ 0 ], 4
  );

  container.dom.innerHTML = '<p>This is <b>bold text</b> and <i>italic text</i> here.</p>';
  checkFracture(
    '<p>This is <b>bold text</b> and <i>italic text</i><strong> here.</strong></p>',
    [ 0 ], 4, [ 0 ], 5
  );

  container.dom.innerHTML = '<p>This is <b>bold text</b> and <i>italic text</i> here.</p>';
  checkFracture(
    '<p>This is <b>bold text</b> and <i>italic text</i><strong> he</strong>re.</p>',
    [ 0 ], 4, [ 0, 4 ], ' he'.length
  );

  container.dom.innerHTML = '<p>This is <b>bold text</b> and <i>italic text</i> here.</p>';
  checkFracture(
    '<p>This is <b>b</b><strong><b>old text</b> and <i>italic text</i> he</strong>re.</p>',
    [ 0, 1, 0 ], 'b'.length, [ 0, 4 ], ' he'.length
  );

  container.dom.innerHTML = '<p>This is <b>bold text</b> and <i>italic text</i> here.</p>';
  checkFracture(
    '<p>This is <b>b<strong>o</strong>ld text</b> and <i>italic text</i> here.</p>',
    [ 0, 1, 0 ], 'b'.length, [ 0, 1, 0 ], 'bo'.length
  );

  container.dom.innerHTML = '<p>This is <b>bold text</b> and <i>italic text</i> here.</p>';
  checkFracture(
    '<p>This is <b>bold text</b><strong> and </strong><i>italic text</i> here.</p>',
    [ 0, 1 ], 1, [ 0, 3 ], 0
  );

  container.dom.innerHTML = '<p>This is <b>bold text</b> and <i>italic text</i> here.</p>';
  checkFracture(
    // Remove the empty formatting element.
    '<p>This is <b></b><strong><b>bold text</b> a</strong>nd <i>italic text</i> here.</p>',
    [ 0, 1, 0 ], ''.length, [ 0, 2 ], ' a'.length
  );

  container.dom.innerHTML = '<p>This is <b>bold text</b> and <i>italic text</i> here.</p>';
  checkFracture(
    '<p>This is <b><strong>bold text</strong></b> and <i>italic text</i> here.</p>',
    [ 0, 1, 0 ], ''.length, [ 0, 1, 0 ], 'bold text'.length
  );

  container.dom.innerHTML = '<p>This <span>new <u>words</u></span> is <b>bold text</b> and <i>italic text</i> here.</p>';
  checkFracture(
    '<p>This <span>new <u>words</u></span><strong> is <b>bo</b></strong><b>ld text</b> and <i>italic text</i> here.</p>',
    [ 0, 1 ], 2, [ 0, 3, 0 ], 'bo'.length
  );

  container.dom.innerHTML =
    '<p>This is <b>the word</b> that I can understand, even if <i>it</i> is not the same as before.</p>' +
    '<p>And another <u>paragraph</u></p>' +
    '<p>Plus one more.</p>' +
    '<p>Last one, I promise</p>';
  checkFractures(
    '<p>This is <b>the</b><strong><b> word</b> that I can understand, even if <i>it</i> is not the same as before.</strong></p>' +
    '<p><strong>And another <u>paragraph</u></strong></p>' +
    '<p><strong>Plus one more.</strong></p>' +
    '<p><strong>Last</strong> one, I promise</p>',
    [ 0, 1, 0 ], 'the'.length, [ 3, 0 ], 'Last'.length
  );

  container.dom.innerHTML =
    '<p>This is <span>completely <i>different <b>from</b> </i>what you would<span>_expect_</span></span></p>' +
    '<p>And more <u>of this is <span>here</span> again</u>.</p>';
  checkFractures(
    '<p>This is <span>completely <i>different <b>f</b></i><strong><i><b>rom</b> </i>what you would<span>_expect_</span></strong></span></p>' +
    '<p><strong>And more <u>of this is <span>h</span></u></strong><u><span>ere</span> again</u>.</p>',
    [ 0, 1, 1, 1, 0 ], 't'.length, [ 1, 1, 1, 0 ], 'h'.length
  );

  container.dom.innerHTML = '<table><tbody><tr><td>One</td>\n\n\n \n<td>Two</td></tr>\n</tbody></table>';
  checkFractures(
    '<table><tbody>' +
    '<tr>' +
    '<td><strong>One</strong></td>' +
    '\n\n\n \n' +
    '<td><strong>Two</strong></td>' +
    '</tr>' +
    '\n' +
    '</tbody></table>', [ 0, 0, 0, 0, 0 ], 0, [ 0, 0, 0 ], 2);

  container.dom.innerHTML = '<table><tbody>\n<tr>\n<td>One</td>\n\n\n \n<td>Two</td></tr>\n</tbody></table>';
  checkFractures(
    '<table><tbody>\n' +
    '<tr>\n' +
    '<td><strong>One</strong></td>' +
    '\n\n\n \n' +
    '<td><strong>Two</strong></td>' +
    '</tr>' +
    '\n' +
    '</tbody></table>', [ 0 ], 0, [ 0 ], 1);

  container.dom.innerHTML = '<table><caption>Heading</caption><tbody>\n<tr>\n<td>One</td>\n\n\n \n<td>Two</td></tr>\n</tbody></table>';
  checkFractures(
    '<table><caption><strong>Heading</strong></caption><tbody>\n' +
    '<tr>\n' +
    '<td><strong>One</strong></td>' +
    '\n\n\n \n' +
    '<td><strong>Two</strong></td>' +
    '</tr>' +
    '\n' +
    '</tbody></table>', [ 0 ], 0, [ 0 ], 2);

  container.dom.innerHTML = '<ol><li>One</li><li>Two</li><li>Three</li></ol>';
  checkFractures(
    '<ol><li><strong>One</strong></li><li><strong>Two</strong></li><li><strong>Three</strong></li></ol>', [ 0 ], 0, [ 0 ], 2);

  container.dom.innerHTML = '<ol><li>One</li><li>Two</li><li>Three</li></ol>';
  checkFractures(
    '<ol><li><strong>One</strong></li><li><strong>Two</strong></li><li><strong>Three</strong></li></ol>', [ 0, 0 ], 0, [ 0, 2 ], 0);

  Remove.remove(container);
});
