import { expect, test } from '@playwright/test';

import {
  createDoc,
  getGridRow,
  goToGridDoc,
  mockedDocument,
  verifyDocName,
} from './utils-common';
import { writeInEditor } from './utils-editor';
import {
  connectOtherUserToDoc,
  mockedAccesses,
  mockedInvitations,
  updateShareLink,
} from './utils-share';
import { createRootSubPage, getTreeRow } from './utils-sub-pages';

test.beforeEach(async ({ page }) => {
  await page.goto('/');
});

test.describe('Doc Header', () => {
  test('toggles panel collapse from floating bar button', async ({
    page,
    browserName,
  }) => {
    const [docTitle] = await createDoc(
      page,
      'doc-floating-bar',
      browserName,
      1,
    );

    const cardCollapse = page.locator('.--docs--left-panel-collapse-button');
    const collapseButton = cardCollapse.getByTestId(
      'floating-bar-toggle-left-panel',
    );
    await expect(collapseButton).toBeVisible();

    // Panel open
    await expect(collapseButton).toHaveAttribute('aria-expanded', 'true');
    await expect(collapseButton.getByText(docTitle)).toBeHidden();

    // Collapse panel
    await collapseButton.click();
    await expect(collapseButton).toHaveAttribute('aria-expanded', 'false');
    await expect(cardCollapse.getByText(docTitle)).toBeHidden();

    // When the title is not visible in the viewport, the button should show the title
    const editor = await writeInEditor({ page, text: 'Lorem ipsum' });
    for (let i = 0; i < 25; i++) {
      await editor.press('Enter');
    }
    await writeInEditor({ page, text: 'Lorem ipsum 2' });
    await expect(cardCollapse.getByText(docTitle)).toBeVisible();

    // Expand panel and check the title is hidden again
    await collapseButton.click();
    await expect(collapseButton).toHaveAttribute('aria-expanded', 'true');
    await expect(cardCollapse.getByText(docTitle)).toBeHidden();
  });

  test('it checks the element are correctly displayed', async ({
    page,
    browserName,
  }) => {
    await createDoc(page, 'doc-update', browserName, 1);

    const card = page.getByLabel(
      'It is the card information about the document.',
    );

    const docTitle = card.getByRole('textbox', { name: 'Document title' });
    await expect(docTitle).toBeVisible();

    await page.getByRole('button', { name: 'Share' }).click();

    await page.getByTestId('doc-visibility').click();

    await page
      .getByRole('menuitem', {
        name: 'Public',
      })
      .click();

    await page.getByRole('button', { name: 'close' }).first().click();

    await expect(card.getByText('Public document')).toBeVisible();

    await expect(card.getByText('Owner ·')).toBeVisible();
    await expect(page.getByRole('button', { name: 'Share' })).toBeVisible();
    await expect(
      page.getByRole('button', { name: 'Export the document' }),
    ).toBeVisible();
    await expect(
      page.getByRole('button', { name: 'Open the document options' }),
    ).toBeVisible();
  });

  test('it updates the title doc and check the broadcast', async ({
    page,
    browserName,
  }) => {
    const [docTitle] = await createDoc(
      page,
      'doc-title-update',
      browserName,
      1,
    );
    await page.getByRole('button', { name: 'Share' }).click();
    await updateShareLink(page, 'Public', 'Editing');

    const docUrl = page.url();

    const { otherPage, cleanup } = await connectOtherUserToDoc({
      docUrl,
      browserName,
      withoutSignIn: true,
      docTitle,
    });

    // Wait for other page to sync
    await page.waitForTimeout(1000);

    await page.keyboard.press('Escape');
    const elTitle = page.getByRole('textbox', { name: 'Document title' });
    await expect(elTitle).toBeVisible();
    await elTitle.fill('Hello World');
    await elTitle.blur();
    await verifyDocName(page, 'Hello World');

    // Wait for other page to sync
    await page.waitForTimeout(1000);

    // Check other user page
    await verifyDocName(otherPage, 'Hello World');

    const elTitleOther = otherPage.getByRole('textbox', {
      name: 'Document title',
    });
    await elTitleOther.fill('Hello Other World');
    await elTitleOther.blur();

    // Check first user page
    await verifyDocName(page, 'Hello Other World');

    await cleanup();
  });

  test('it updates the title doc adding a leading emoji', async ({
    page,
    browserName,
  }) => {
    await createDoc(page, 'doc-update-emoji', browserName, 1);

    const emojiPicker = page.locator('.--docs--doc-title').getByRole('button');
    const optionMenu = page.getByLabel('Open the document options');
    const addEmojiMenuItem = page.getByRole('menuitem', { name: 'Add emoji' });
    const removeEmojiMenuItem = page.getByRole('menuitem', {
      name: 'Remove emoji',
    });

    // Top parent should not have emoji picker
    await expect(emojiPicker).toBeHidden();
    await optionMenu.click();
    await expect(addEmojiMenuItem).toBeHidden();
    await expect(removeEmojiMenuItem).toBeHidden();
    await page.keyboard.press('Escape');

    const { name: docChild } = await createRootSubPage(
      page,
      browserName,
      'doc-update-emoji-child',
    );

    await verifyDocName(page, docChild);

    // Emoji picker should be hidden initially
    await expect(emojiPicker).toBeHidden();

    // Add emoji
    await optionMenu.click();
    await expect(removeEmojiMenuItem).toBeHidden();
    await addEmojiMenuItem.click();
    await expect(emojiPicker).toHaveText('📄');

    // Change emoji
    await emojiPicker.click({
      delay: 100,
    });
    await page.getByRole('button', { name: '😀' }).first().click();
    await expect(emojiPicker).toHaveText('😀');

    // Update title
    const docTitle = page.getByRole('textbox', { name: 'Document title' });
    await docTitle.fill('Hello Emoji World');
    await docTitle.blur();
    await verifyDocName(page, 'Hello Emoji World');

    // Check the tree
    const row = await getTreeRow(page, 'Hello Emoji World');
    await expect(row.getByText('😀')).toBeVisible();

    // Remove emoji
    await optionMenu.click();
    await expect(addEmojiMenuItem).toBeHidden();
    await removeEmojiMenuItem.click();
    await expect(emojiPicker).toBeHidden();
  });

  test('it deletes the doc', async ({ page, browserName }) => {
    const [randomDoc] = await createDoc(page, 'doc-delete', browserName, 1);

    await page.getByLabel('Open the document options').click();
    await page.getByRole('menuitem', { name: 'Delete document' }).click();

    await expect(
      page.getByRole('heading', { name: 'Delete a doc' }),
    ).toBeVisible();

    await expect(page.getByText(`This document will be`)).toBeVisible();

    await page
      .getByRole('button', {
        name: 'Delete document',
      })
      .click();

    await expect(
      page.getByText('The document has been deleted.'),
    ).toBeVisible();

    await expect(page.getByRole('button', { name: 'New do' })).toBeVisible();

    const row = page
      .getByLabel('Datagrid of the documents page 1')
      .getByRole('table')
      .getByRole('row')
      .filter({
        hasText: randomDoc,
      });

    expect(await row.count()).toBe(0);
  });

  test('it checks the options available if administrator', async ({ page }) => {
    await mockedDocument(page, {
      abilities: {
        accesses_manage: true, // Means admin
        accesses_view: true,
        destroy: false, // Means not owner
        link_configuration: true,
        versions_destroy: true,
        versions_list: true,
        versions_retrieve: true,
        update: true,
        partial_update: true,
        retrieve: true,
      },
    });

    await mockedInvitations(page);
    await mockedAccesses(page);

    await goToGridDoc(page);

    await expect(
      page.getByRole('textbox', { name: 'Document title' }),
    ).toContainText('Mocked document');

    await expect(
      page.getByRole('button', { name: 'Export the document' }),
    ).toBeVisible();

    await page.getByLabel('Open the document options').click();

    await expect(
      page.getByRole('menuitem', { name: 'Delete document' }),
    ).toBeDisabled();

    // Click somewhere else to close the options
    await page.click('body', { position: { x: 0, y: 0 } });

    await page.getByRole('button', { name: 'Share' }).click();

    const shareModal = page.getByRole('dialog', {
      name: 'Share modal content',
    });
    await expect(shareModal).toBeVisible();
    await expect(page.getByText('Share the document')).toBeVisible();

    const invitationCard = shareModal.getByLabel('List invitation card');
    await expect(invitationCard).toBeVisible();
    await expect(
      invitationCard.getByText('test.test@invitation.test').first(),
    ).toBeVisible();
    const invitationRole = invitationCard.getByTestId('doc-role-dropdown');
    await expect(invitationRole).toBeVisible();

    await invitationRole.click();

    await page.getByRole('menuitem', { name: 'Remove access' }).click();
    await expect(invitationCard).toBeHidden();

    const memberCard = shareModal.getByLabel('List members card');
    const roles = memberCard.getByTestId('doc-role-dropdown');
    await expect(memberCard).toBeVisible();
    await expect(
      memberCard.getByText('test.test@accesses.test').first(),
    ).toBeVisible();
    await expect(roles).toBeVisible();

    await roles.click();
    await expect(
      page.getByRole('menuitem', { name: 'Remove access' }),
    ).toBeEnabled();
  });

  test('it checks the options available if editor', async ({ page }) => {
    await mockedDocument(page, {
      abilities: {
        accesses_manage: false, // Means not admin
        accesses_view: true,
        destroy: false, // Means not owner
        link_configuration: false,
        versions_destroy: true,
        versions_list: true,
        versions_retrieve: true,
        update: true,
        partial_update: true, // Means editor
        retrieve: true,
      },
    });

    await mockedInvitations(page, {
      abilities: {
        destroy: false,
        update: false,
        partial_update: false,
        retrieve: true,
      },
    });
    await mockedAccesses(page);

    await goToGridDoc(page);

    await expect(
      page.getByRole('textbox', { name: 'Document title' }),
    ).toContainText('Mocked document');

    await expect(
      page.getByRole('button', { name: 'Export the document' }),
    ).toBeVisible();
    await page.getByLabel('Open the document options').click();

    await expect(
      page.getByRole('menuitem', { name: 'Delete document' }),
    ).toBeDisabled();

    // Click somewhere else to close the options
    await page.click('body', { position: { x: 0, y: 0 } });

    await page.getByRole('button', { name: 'Share' }).click();

    const shareModal = page.getByRole('dialog', {
      name: 'Share modal content',
    });
    await expect(shareModal).toBeVisible();
    await expect(page.getByText('Share the document')).toBeVisible();

    await expect(page.getByPlaceholder('Type a name or email')).toBeHidden();

    const invitationCard = shareModal.getByLabel('List invitation card');
    await expect(invitationCard).toBeVisible();
    await expect(
      invitationCard.getByText('test.test@invitation.test').first(),
    ).toBeVisible();
    await expect(invitationCard.getByLabel('Document role text')).toBeVisible();
    await expect(
      invitationCard.getByRole('button', { name: 'more_horiz' }),
    ).toBeHidden();

    const memberCard = shareModal.getByLabel('List members card');
    await expect(memberCard.getByText('test.test@accesses.test')).toBeVisible();
    await expect(memberCard.getByLabel('Document role text')).toBeVisible();
    await expect(
      memberCard.getByRole('button', { name: 'more_horiz' }),
    ).toBeHidden();
  });

  test('it checks the options available if reader', async ({ page }) => {
    await mockedDocument(page, {
      abilities: {
        accesses_manage: false, // Means not admin
        accesses_view: true,
        destroy: false, // Means not owner
        link_configuration: false,
        versions_destroy: false,
        versions_list: true,
        versions_retrieve: true,
        update: false,
        partial_update: false, // Means not editor
        retrieve: true,
      },
    });

    await mockedInvitations(page, {
      abilities: {
        destroy: false,
        update: false,
        partial_update: false,
        retrieve: true,
      },
    });
    await mockedAccesses(page);

    await goToGridDoc(page);

    await expect(
      page.getByRole('heading', { name: 'Mocked document' }),
    ).toBeVisible();

    await expect(
      page.getByRole('button', { name: 'Export the document' }),
    ).toBeVisible();
    await page.getByLabel('Open the document options').click();

    await expect(
      page.getByRole('menuitem', { name: 'Delete document' }),
    ).toBeDisabled();

    // Click somewhere else to close the options
    await page.click('body', { position: { x: 0, y: 0 } });

    await page.getByRole('button', { name: 'Share' }).click();

    const shareModal = page.getByLabel('Share modal');
    await expect(page.getByText('Share the document')).toBeVisible();

    await expect(page.getByPlaceholder('Type a name or email')).toBeHidden();

    const invitationCard = shareModal.getByLabel('List invitation card');
    await expect(invitationCard).toBeVisible();
    await expect(
      invitationCard.getByText('test.test@invitation.test').first(),
    ).toBeVisible();
    await expect(invitationCard.getByLabel('Document role text')).toBeVisible();
    await expect(
      invitationCard.getByRole('button', { name: 'more_horiz' }),
    ).toBeHidden();

    const memberCard = shareModal.getByLabel('List members card');
    await expect(memberCard.getByText('test.test@accesses.test')).toBeVisible();
    await expect(memberCard.getByLabel('Document role text')).toBeVisible();
    await expect(
      memberCard.getByRole('button', { name: 'more_horiz' }),
    ).toBeHidden();
  });

  test('It checks the copy as Markdown button', async ({
    page,
    browserName,
  }) => {
    test.skip(
      browserName === 'webkit',
      'navigator.clipboard is not working with webkit and playwright',
    );

    // create page and navigate to it
    await page
      .getByRole('button', {
        name: 'New doc',
      })
      .click();

    // Add dummy content to the doc
    const editor = page.locator('.ProseMirror');
    const docFirstBlock = editor.locator('.bn-block-content').first();
    await docFirstBlock.click();
    await page.keyboard.type('# Hello World', { delay: 100 });
    const docFirstBlockContent = docFirstBlock.locator('h1');
    await expect(docFirstBlockContent).toHaveText('Hello World');

    // Copy content to clipboard
    await page.getByLabel('Open the document options').click();
    await page.getByRole('menuitem', { name: 'Copy as Markdown' }).click();
    await expect(page.getByText('Copied to clipboard')).toBeVisible();

    // Test that clipboard is in Markdown format
    const handle = await page.evaluateHandle(() =>
      navigator.clipboard.readText(),
    );
    const clipboardContent = await handle.jsonValue();
    expect(clipboardContent.trim()).toBe('# Hello World');
  });

  test('it checks the copy link button', async ({ page, browserName }) => {
    test.skip(
      browserName === 'webkit',
      'navigator.clipboard is not working with webkit and playwright',
    );
    await mockedDocument(page, {
      abilities: {
        destroy: false, // Means owner
        link_configuration: true,
        versions_destroy: true,
        versions_list: true,
        versions_retrieve: true,
        accesses_manage: false,
        accesses_view: false,
        update: true,
        partial_update: true,
        retrieve: true,
      },
    });

    await goToGridDoc(page);

    const shareButton = page.getByRole('button', {
      name: 'Share',
      exact: true,
    });
    await expect(shareButton).toBeVisible();

    await shareButton.click();
    await page.getByRole('button', { name: 'Copy link' }).click();
    await expect(page.getByText('Link Copied !')).toBeVisible();

    const handle = await page.evaluateHandle(() =>
      navigator.clipboard.readText(),
    );
    const clipboardContent = await handle.jsonValue();

    const origin = await page.evaluate(() => window.location.origin);
    expect(clipboardContent.trim()).toMatch(
      `${origin}/docs/mocked-document-id/`,
    );
  });

  test('it pins a document', async ({ page, browserName }) => {
    const [docTitle] = await createDoc(page, `Pin doc`, browserName);

    await page
      .getByRole('button', { name: 'Open the document options' })
      .click();

    // Pin
    await page.getByRole('menuitem', { name: 'Pin' }).click();
    await page
      .getByRole('button', { name: 'Open the document options' })
      .click();
    await expect(page.getByText('Unpin')).toBeVisible();

    await page.goto('/');

    const row = await getGridRow(page, docTitle);

    // Check is pinned
    await expect(row.getByTestId('doc-pinned-icon')).toBeVisible();
    const leftPanelFavorites = page.getByTestId('left-panel-favorites');
    await expect(leftPanelFavorites.getByText(docTitle)).toBeVisible();

    await row.getByText(docTitle).click();
    await page
      .getByRole('button', { name: 'Open the document options' })
      .click();

    // Unpin
    await page.getByRole('menuitem', { name: 'Unpin' }).click();
    await page
      .getByRole('button', { name: 'Open the document options' })
      .click();
    await expect(page.getByRole('menuitem', { name: 'Pin' })).toBeVisible();

    await page.goto('/');

    // Check is unpinned
    await expect(row.getByTestId('doc-pinned-icon')).toBeHidden();
    await expect(leftPanelFavorites.getByText(docTitle)).toBeHidden();
  });

  test('it duplicates a document', async ({ page, browserName }) => {
    const [docTitle] = await createDoc(page, `Duplicate doc`, browserName);

    const editor = page.locator('.ProseMirror');
    await editor.click();
    await editor.fill('Hello Duplicated World');

    await page.getByLabel('Open the document options').click();

    await page.getByRole('menuitem', { name: 'Duplicate' }).click();
    await expect(
      page.getByText('Document duplicated successfully!'),
    ).toBeVisible();

    const duplicateTitle = 'Copy of ' + docTitle;
    await verifyDocName(page, duplicateTitle);

    await page.goto('/');

    const row = await getGridRow(page, duplicateTitle);

    await expect(row.getByText(duplicateTitle)).toBeVisible();

    await row.getByText(`more_horiz`).click();
    await page.getByRole('menuitem', { name: 'Duplicate' }).click();
    const duplicateDuplicateTitle = 'Copy of ' + duplicateTitle;
    await page.getByText(duplicateDuplicateTitle).click();
    await expect(page.getByText('Hello Duplicated World')).toBeVisible();
  });

  test('it duplicates a child document', async ({ page, browserName }) => {
    await createDoc(page, `Duplicate doc`, browserName);

    const { name: childTitle } = await createRootSubPage(
      page,
      browserName,
      'Duplicate doc - child',
    );

    const editor = page.locator('.ProseMirror');
    await editor.click();
    await editor.fill('Hello Duplicated World');

    const duplicateTitle = 'Copy of ' + childTitle;
    const docTree = page.getByTestId('doc-tree');

    const child = docTree
      .getByRole('treeitem')
      .locator('.--docs-sub-page-item')
      .filter({
        hasText: childTitle,
      });
    await child.hover();
    await child.getByText(`more_horiz`).click();

    const currentUrl = page.url();

    await page.getByRole('menuitem', { name: 'Duplicate' }).click();

    await expect(page).not.toHaveURL(new RegExp(currentUrl));

    await verifyDocName(page, duplicateTitle);

    await expect(
      page.getByTestId('doc-tree').getByText(duplicateTitle),
    ).toBeVisible();
  });
});

test.describe('Documents Header mobile', () => {
  test.use({ viewport: { width: 500, height: 1200 } });

  test.beforeEach(async ({ page }) => {
    await page.goto('/');
  });

  test('it checks the copy link button is displayed', async ({ page }) => {
    await mockedDocument(page, {
      abilities: {
        destroy: false,
        link_configuration: true,
        versions_destroy: true,
        versions_list: true,
        versions_retrieve: true,
        accesses_manage: false,
        accesses_view: false,
        update: true,
        partial_update: true,
        retrieve: true,
      },
    });

    await goToGridDoc(page);

    await expect(page.getByRole('button', { name: 'Copy link' })).toBeHidden();
    await page.getByLabel('Open the document options').click();
    await expect(
      page.getByRole('menuitem', { name: 'Copy link' }),
    ).toBeVisible();
    await page.getByRole('menuitem', { name: 'Share' }).click();
    await expect(page.getByRole('button', { name: 'Copy link' })).toBeVisible();
  });

  test('it checks the close button on Share modal', async ({ page }) => {
    await mockedDocument(page, {
      abilities: {
        destroy: true, // Means owner
        link_configuration: true,
        versions_destroy: true,
        versions_list: true,
        versions_retrieve: true,
        accesses_manage: true,
        accesses_view: true,
        update: true,
        partial_update: true,
        retrieve: true,
      },
    });

    await goToGridDoc(page);

    await page.getByLabel('Open the document options').click();
    await page.getByRole('menuitem', { name: 'Share' }).click();

    const shareModal = page.getByRole('dialog', {
      name: 'Share modal content',
    });
    await expect(shareModal).toBeVisible();
    await page.getByRole('button', { name: 'close' }).click();
    await expect(page.getByLabel('Share modal')).toBeHidden();
  });
});
