import log from 'loglevel';
import { defaultModuleQueriesandMutations } from '../../../support/helpers';
import { instrumentWithLongContent } from './instrument-highlights';

const contentBlockId = window.btoa('ContentBlockNode:1');
const instrumentId = window.btoa('InstrumentNode:2');
const instrumentSlug = 'my-instrument';
const chapterId = window.btoa('ChapterNode:3');
const moduleId = window.btoa('ModuleNode:3');
const moduleSlug = 'my-module';

const getModule = (contents) => {
  return {
    intro: '<p>Introducing this great paragraph!</p>',
    highlights: [],
    slug: moduleSlug,
    chapters: [
      {
        title: 'A Chapter',
        description: 'This is something else',
        highlights: [],
        id: chapterId,
        contentBlocks: [
          {
            title: 'A Content Block',
            highlights: [],
            id: contentBlockId,
            contents,
          },
        ],
      },
    ],
  };
};

const defaultContents = [
  {
    type: 'text_block',
    id: 'some-content-component-id',
    value: {
      text: '<p>Dies ist ein Text mit ein paar Wörtern.</p>',
    },
  },
];

const contentListContents = [
  {
    id: '16ea6f8a-99bc-4366-9f32-d7c3e7e39765',
    type: 'content_list_item',
    value: [
      {
        id: '73f571e9-c049-41e7-8927-c4badbfb9b93',
        type: 'text_block',
        value: {
          text: '<p data-block-key="qykuf">Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo<br/><br/> Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo Hallo</p>',
        },
      },
    ],
  },
];

const subTitleContents = [
  {
    id: 'c4eaf571-de75-436b-b4d7-9178f2a86979',
    type: 'subtitle',
    value: {
      text: 'Wozu dienen Visualisierungen?',
    },
  },
];

const getAddHighlight = (pageType: string = 'ModuleNode') => {
  log.debug(`adding highlight for page ${pageType}`);
  let page: { id: string; __typename: string; slug?: string };
  if (pageType === 'ChapterNode') {
    page = {
      id: chapterId,
      __typename: 'ChapterNode',
    };
  } else if (pageType === 'InstrumentNode') {
    page = {
      slug: instrumentSlug,
      id: instrumentId,
      __typename: 'InstrumentNode',
    };
  } else {
    page = {
      id: moduleId,
      slug: moduleSlug,
      __typename: 'ModuleNode',
    };
  }
  return ({ input: { highlight } }) => {
    lastHighlight = {
      ...highlight,
      id: 'new-highlight-id',
      page,
    };
    return {
      addHighlight: {
        highlight: lastHighlight,
      },
    };
  };
};

const getAddContentHighlight = (pageType: string = 'ContentBlockNode') => {
  log.debug(`adding content highlight for page type ${pageType}`);
  let page: { id: string; __typename: string; slug?: string };
  if (pageType === 'InstrumentNode') {
    page = {
      id: instrumentId,
      slug: instrumentSlug,
      __typename: 'InstrumentNode',
    };
  } else {
    page = {
      id: contentBlockId,
      __typename: 'ContentBlockNode',
    };
  }
  console.log(page);
  return ({ input: { highlight } }) => {
    lastHighlight = {
      ...highlight,
      id: 'new-highlight-id',
      page,
    };
    return {
      addContentHighlight: {
        highlight: lastHighlight,
      },
    };
  };
};

let lastHighlight;
const defaultModule = getModule(defaultContents);
const getOperations = (pageType = 'ModuleNode') => ({
  ...defaultModuleQueriesandMutations,
  ModuleDetailsQuery: {
    module: defaultModule,
  },
  UpdateLastModule: {
    updateLastModule: {
      lastModule: defaultModule,
    },
  },
  AddHighlight: getAddHighlight(pageType),
  AddContentHighlight: getAddContentHighlight(pageType),
  UpdateHighlight: ({ input: { note, color } }) => {
    lastHighlight = {
      ...lastHighlight,
      note,
      color,
    };
    return {
      updateHighlight: {
        highlight: lastHighlight,
      },
    };
  },
  DeleteHighlight: {
    deleteHighlight: {
      success: true,
    },
  },
});

const operations = getOperations();

const selectText = (elm: Node, start: number, end: number) => {
  const range = document.createRange();
  range.setStart(elm, start);
  range.setEnd(elm, end);
  cy.window().then((win) => win.getSelection().addRange(range));
};

const makeSelection = (dataCy, tag = null) => {
  cy.getByDataCy(dataCy).then(($elm) => {
    const textBlock = $elm[0];
    let paragraph;
    if (tag) {
      paragraph = textBlock.querySelector(tag);
    } else {
      paragraph = textBlock;
    }
    selectText(paragraph.childNodes[0], 2, 12);
    /*
     * Also trigger the events manually
     */
    cy.document().trigger('selectionchange');
    cy.wrap(paragraph.parentNode).trigger('mouseup');
  });
};

const removeSelection = () => {
  cy.window().then((win) => {
    const selection = win.getSelection();
    selection.removeAllRanges();
  });
};

const markText = (dataCy = 'text-block', tag = 'p') => {
  /*
   * Mark the text (programmatically, as Cypress has no API for this)
   */
  makeSelection(dataCy, tag);
  cy.getByDataCy('highlight-popover').should('be.visible');
  cy.getByDataCy('highlight-popover').should('have.length', 1); // there should only be one popover

  /*
   * manually remove the selection, because cypress does not do that on a click, unlike a real browser
   */
  removeSelection();
};

const createHighlight = (text: string, content = false, numHighlights = 1) => {
  // delete doesn't make sense before the highlight exists
  cy.getByDataCy('highlight-delete').should('not.exist');
  // mark the text with yellow and check the text
  cy.getByDataCy('highlight-alpha').click();
  if (content) {
    cy.wait('@AddContentHighlight');
  } else {
    cy.wait('@AddHighlight');
  }
  cy.getByDataCy('highlight-mark').should('contain', text);

  // we only want to have one of each element and not accidentally create multiple
  cy.getByDataCy('highlight-popover').should('have.length', 1); // there should only be one popover
  cy.getByDataCy('highlight-mark').should('have.length', numHighlights);
};

const updateHighlight = (text, color = 'beta') => {
  cy.getByDataCy(`highlight-${color}`).click();
  cy.wait('@UpdateHighlight');
  cy.getByDataCy('highlight-mark').should('contain', text);
  cy.getByDataCy('highlight-mark').should('have.class', `highlight--${color}`);
  //
  // we only want to have one of each element and not accidentally create multiple
  cy.getByDataCy('highlight-popover').should('have.length', 1); // there should only be one popover
  cy.getByDataCy('highlight-mark').should('have.length', 1);
};

const openSidebar = (text) => {
  // display the sidebar and popover and check them
  cy.getByDataCy('highlight-note').click();
  cy.getByDataCy('highlight-popover').should('be.visible');
  cy.getByDataCy('highlight-sidebar').should('be.visible');
  cy.getByDataCy('highlight-in-sidebar').should('contain', text);

  cy.getByDataCy('highlight-popover').should('have.length', 1); // there should only be one popover
  cy.getByDataCy('highlight-mark').should('have.length', 1);
};

const deleteHighlight = () => {
  // delete the highlight
  cy.getByDataCy('highlight-mark').click();
  cy.getByDataCy('highlight-delete').click();
  cy.getByDataCy('confirm-dialog').should('be.visible');
  cy.getByDataCy('modal-save-button').click();
  cy.wait('@DeleteHighlight');

  cy.getByDataCy('highlight-popover').should('not.exist');
  cy.getByDataCy('highlight-sidebar').should('not.exist');
  cy.getByDataCy('highlight-mark').should('not.exist');
};

const addNote = () => {
  const textPart = 'Some noteworthy stuff with a link to ';
  const urlPart = 'https://hep.ch';
  const note = `${textPart}${urlPart}`;
  cy.getByDataCy('highlight-note-input').should('have.value', '').type(note);
  cy.getByDataCy('highlight-note-save').click();
  cy.wait('@UpdateHighlight');

  cy.getByDataCy('highlight-note-save').should('not.exist');
  cy.getByDataCy('highlight-note-text').should('contain.text', textPart);
  cy.get(`[href="${urlPart}"]`).should('exist');
};

describe('Highlights', () => {
  beforeEach(() => {
    cy.setup();
  });

  it('visits a module and highlights a paragraph', () => {
    cy.mockGraphqlOps({
      operations,
    });
    cy.visit('/module/my-module');

    markText();

    const highlightedText = 'es ist ein';
    createHighlight(highlightedText, true);
    updateHighlight(highlightedText);

    openSidebar(highlightedText);

    // click outside the created components to make them disappear
    cy.getByDataCy('module-title').click();
    cy.getByDataCy('highlight-popover').should('not.exist');
    cy.getByDataCy('highlight-sidebar').should('not.exist');

    cy.getByDataCy('highlight-mark').should('have.length', 1);

    // click on the highlighted text to make the menus appear again
    cy.getByDataCy('highlight-mark').click();
    cy.getByDataCy('highlight-popover').should('be.visible');
    cy.getByDataCy('highlight-sidebar').should('be.visible');

    updateHighlight(highlightedText, 'gamma');
    cy.getByDataCy('highlight-in-sidebar').should('have.class', 'highlight--gamma');
    // todo: write a note
    // todo: click the note icon without first setting a color

    deleteHighlight();
  });

  it('visits a module with a ContentListItem and highlights some text', () => {
    cy.mockGraphqlOps({
      operations: {
        ...operations,
        ModuleDetailsQuery: {
          module: getModule(contentListContents),
        },
      },
    });

    cy.visit('/module/my-module');

    markText();
  });

  it('visits a module and highlights some text, then adds a note', () => {
    cy.mockGraphqlOps({
      operations: {
        ...operations,
        ModuleDetailsQuery: {
          module: getModule(defaultContents),
        },
      },
    });

    cy.visit('/module/my-module');

    markText();
    cy.getByDataCy('highlight-note').click();
    cy.wait('@AddContentHighlight');

    addNote();
  });

  it('visits a module and highlights some text, clicks on the Note icon, then changes the color', () => {
    cy.mockGraphqlOps({
      operations,
    });
    cy.visit('/module/my-module');

    markText();
    const highlightedText = 'es ist ein';

    // delete doesn't make sense before the highlight exists
    cy.getByDataCy('highlight-delete').should('not.exist');
    openSidebar(highlightedText);
    cy.wait('@AddContentHighlight');
    updateHighlight(highlightedText);
    cy.getByDataCy('highlight-mark').should('have.length', 1);
  });

  it('visits a module and highlights the chapter description', () => {
    cy.mockGraphqlOps({
      operations: getOperations('ChapterNode'),
    });
    cy.visit('/module/my-module');

    markText('chapter-intro');
    const highlightedText = 'is is some';

    // delete doesn't make sense before the highlight exists
    cy.getByDataCy('highlight-delete').should('not.exist');
    openSidebar(highlightedText);
    cy.wait('@AddHighlight');
    updateHighlight(highlightedText);
    cy.getByDataCy('highlight-mark').should('have.length', 1);

    deleteHighlight();
  });
  it('visits a module and highlights the module description', () => {
    cy.mockGraphqlOps({
      operations,
    });
    cy.visit('/module/my-module');

    markText('module-intro');
    const highlightedText = 'troducing';

    // delete doesn't make sense before the highlight exists
    cy.getByDataCy('highlight-delete').should('not.exist');
    openSidebar(highlightedText);
    cy.wait('@AddHighlight');
    updateHighlight(highlightedText);
    cy.getByDataCy('highlight-mark').should('have.length', 1);
    deleteHighlight();
  });

  it('visits an instrument and highlights some text', () => {
    cy.mockGraphqlOps({
      operations: {
        ...operations,
        AddContentHighlight: getAddContentHighlight('InstrumentNode'),
        InstrumentQuery: {
          instrument: {
            title: 'My Instrument',
            id: instrumentId,
            slug: instrumentSlug,
            highlights: [],
            contents: [
              {
                type: 'text_block',
                id: 'some-other-content-component-id',
                value: {
                  text: '<p>Hello my beautiful World!</p>',
                },
              },
            ],
          },
        },
      },
    });
    cy.visit(`/instrument/${instrumentSlug}`);
    cy.wait('@InstrumentQuery');
    markText();
    const highlightedText = 'llo my bea';
    createHighlight(highlightedText, true);
    updateHighlight(highlightedText);
    addNote();
    deleteHighlight();
  });

  it('visits an instrument and writes a note', () => {
    cy.mockGraphqlOps({
      operations: {
        ...operations,
        AddContentHighlight: getAddContentHighlight('InstrumentNode'),
        InstrumentQuery: {
          instrument: {
            title: 'My Instrument',
            id: instrumentId,
            slug: instrumentSlug,
            highlights: [],
            contents: [
              {
                type: 'text_block',
                id: 'some-other-content-component-id',
                value: {
                  text: '<p>Hello my beautiful World!</p>',
                },
              },
            ],
          },
        },
      },
    });
    cy.visit(`/instrument/${instrumentSlug}`);
    cy.wait('@InstrumentQuery');
    markText();
    const highlightedText = 'llo my bea';
    openSidebar(highlightedText);
    cy.wait('@AddContentHighlight');
    updateHighlight(highlightedText);
    deleteHighlight();
  });

  it('visits an instrument with long content and highlights some text', () => {
    cy.mockGraphqlOps({
      operations: {
        ...operations,
        AddContentHighlight: getAddContentHighlight('InstrumentNode'),
        InstrumentQuery: {
          instrument: instrumentWithLongContent,
        },
      },
    });
    cy.visit(`/instrument/${instrumentSlug}`);
    cy.wait('@InstrumentQuery');
    markText();
    const highlightedText = 'nn eine Pe';
    createHighlight(highlightedText, true);
    updateHighlight(highlightedText);
    addNote();
    deleteHighlight();
  });

  it('visits an instrument and highlights the intro', () => {
    const intro = '<p>Something here in the intro, right?</p>';
    cy.mockGraphqlOps({
      operations: {
        ...operations,
        AddHighlight: getAddHighlight('InstrumentNode'),
        InstrumentQuery: {
          instrument: {
            title: 'My Instrument',
            id: instrumentId,
            slug: instrumentSlug,
            intro,
            highlights: [],
            contents: [
              {
                type: 'text_block',
                id: 'some-other-content-component-id',
                value: {
                  text: '<p>Hello my beautiful World!</p>',
                },
              },
            ],
          },
        },
      },
    });
    cy.visit(`/instrument/${instrumentSlug}`);
    cy.wait('@InstrumentQuery');
    cy.getByDataCy('instrument-intro').should('have.html', intro);
    markText('instrument-intro');
    const highlightedText = 'mething he';
    createHighlight(highlightedText, false);
    updateHighlight(highlightedText);
    addNote();
    deleteHighlight();
  });

  it('visits an instrument with a subtitle and does not display the popover', () => {
    cy.mockGraphqlOps({
      operations: {
        ...operations,
        InstrumentQuery: {
          instrument: {
            title: 'some instrument',
            id: instrumentId,
            slug: instrumentSlug,
            contents: subTitleContents,
          },
        },
      },
    });

    cy.visit(`/instrument/${instrumentSlug}`);
    cy.wait('@InstrumentQuery');

    // markText('subtitle-block');
    cy.getByDataCy('subtitle-block').should('have.text', 'Wozu dienen Visualisierungen?');
    makeSelection('subtitle-block');
    cy.getByDataCy('highlight-popover').should('not.exist');
  });

  describe('with paragraphs and lists', () => {
    beforeEach(() => {
      const contents = [
        {
          id: 'eaf8016b-ba47-4690-8e3b-21296cc095fa',
          type: 'assignment',
          value: {
            title: 'A LF1 A8: Ziele',
            assignment:
              '<p data-block-key="hed7c">Ihr Produkt und Ihre Erkenntnisse aus dem folgenden Lernauftrag können Sie in Ihr persönliches Portfolio aufnehmen.</p><p data-block-key="d739u">Formulieren Sie je drei bis fünf persönliche Ziele …</p><ul><li data-block-key="9ske7">für den schulischen Bereich.</li><li data-block-key="71vi0">für den Bereich des Lehrbetriebs.</li></ul><p data-block-key="849i"></p><p data-block-key="6qgb7">Wenden Sie dabei Ihre Kenntnisse aus dem Studium des Sachtextes an. Tauschen Sie sich in Gruppen über Ihre Ziele aus. Wer hat gleiche oder ähnliche Ziele? Wo gibt es Unterschiede? Was möchte ich von wem noch wissen?</p>',
            id: 'QXNzaWdubWVudE5vZGU6MzU=',
          },
        },
      ];
      //       const contents = [
      //         {
      //           type: 'text_block',
      //           id: 'some-other-content-component-id',
      //           value: {
      //             text: `
      // <p>First, there is a paragraph</p>
      // <ul>
      //   <li>Then, there is a list</li>
      //   <li>With multiple list items</li>
      // </ul>
      // <p>Then, there is another paragrah</p>
      // `,
      //           },
      //         },
      //       ];
      cy.mockGraphqlOps({
        operations: {
          ...operations,
          AddHighlight: getAddHighlight('InstrumentNode'),
          ModuleDetailsQuery: {
            module: getModule(contents),
          },
          AssignmentQuery: {
            assignment: {
              id: 'QXNzaWdubWVudE5vZGU6Mzk=',
              title: 'A LF1 A13: Ziele und Vorhaben',
              assignment: `<p data-block-key="6fgwz">Ihr Produkt und Ihre Erkenntnisse aus dem folgenden Lernauftrag können Sie in Ihr persönliches Portfolio aufnehmen.</p><p data-block-key="1l7np">Notieren Sie Ihre Ziele und Vorhaben für Ihre berufliche und private Zukunft. Gestalten Sie anschliessend ein Vision Board als Plakat oder in digitaler Form.</p>
<ul>
<li>Ein ListItem</li>
<li>Noch eins</li>
</ul>
<p data-block-key="a0fqk">Zusätzliche Möglichkeit: Sie wählen Ziele und Vorhaben für ein gesellschaftliches Thema wie zum Beispiel Zusammenleben, Weltfrieden, Naturschutz usw.</p><p data-block-key="6kj6a">Hinweis: Im Internet finden Sie zahlreiche Anleitungen zum Erstellen eines Vision Boards. Lassen Sie sich dadurch inspirieren, doch verwenden Sie keine Vorlagen. Vertrauen Sie auf Ihre eigene Kreativität und kreieren Sie Ihr ganz persönliches Vision Board.</p>`,
              solution: null,
            },
          },
        },
      });

      cy.visit(`/module/${moduleSlug}`);
      cy.wait('@ModuleDetailsQuery');
      cy.wait('@AssignmentQuery');
    });
    it('highlights a paragraph', () => {
      markText('assignment-main-text');
      const highlightedText = 'r Produkt';
      createHighlight(highlightedText, true);
      updateHighlight(highlightedText);
      addNote();
      deleteHighlight();
    });
    it('highlights a list item', () => {
      cy.getByDataCy('assignment-main-text').should('contain.text', 'Ihr Produkt');
      markText('assignment-main-text', 'li');
      const highlightedText = 'n ListItem';
      createHighlight(highlightedText, true);
      updateHighlight(highlightedText);
      addNote();
      deleteHighlight();
    });
    // todo: this test does not handle the cache update correctly when having 2 highlights. This is a test problem, not a code problem
    it.skip('highlights a paragraph and a list item', () => {
      const highlightedText = 'r Produkt';
      markText('assignment-main-text', 'p');
      createHighlight(highlightedText, true);

      // click outside the created components to make them disappear
      cy.getByDataCy('module-title').click();

      const secondHighlightedText = 'n ListItem';
      markText('assignment-main-text', 'li');
      createHighlight(secondHighlightedText, true, 2);
      updateHighlight(secondHighlightedText);
      addNote();
      deleteHighlight();
    });
  });
});
