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

export const BROWSERS = ['chromium', 'webkit', 'firefox'];

export const CONFIG = {
  AI_FEATURE_ENABLED: true,
  CRISP_WEBSITE_ID: null,
  COLLABORATION_WS_URL: 'ws://localhost:4444/collaboration/ws/',
  COLLABORATION_WS_NOT_CONNECTED_READY_ONLY: true,
  ENVIRONMENT: 'development',
  FRONTEND_CSS_URL: null,
  FRONTEND_HOMEPAGE_FEATURE_ENABLED: true,
  FRONTEND_THEME: null,
  MEDIA_BASE_URL: 'http://localhost:8083',
  LANGUAGES: [
    ['en-us', 'English'],
    ['fr-fr', 'Français'],
    ['de-de', 'Deutsch'],
    ['nl-nl', 'Nederlands'],
    ['es-es', 'Español'],
  ],
  LANGUAGE_CODE: 'en-us',
  POSTHOG_KEY: {},
  SENTRY_DSN: null,
  theme_customization: {},
} as const;

export const overrideConfig = async (
  page: Page,
  newConfig: { [K in keyof typeof CONFIG]?: unknown },
) =>
  await page.route('**/api/v1.0/config/', async (route) => {
    const request = route.request();
    if (request.method().includes('GET')) {
      await route.fulfill({
        json: {
          ...CONFIG,
          ...newConfig,
        },
      });
    } else {
      await route.continue();
    }
  });

export const keyCloakSignIn = async (
  page: Page,
  browserName: string,
  fromHome: boolean = true,
) => {
  if (fromHome) {
    await page.getByRole('button', { name: 'Start Writing' }).first().click();
  }

  const login = `user-e2e-${browserName}`;
  const password = `password-e2e-${browserName}`;

  await expect(
    page.locator('.login-pf-page-header').getByText('impress'),
  ).toBeVisible();

  if (await page.getByLabel('Restart login').isVisible()) {
    await page.getByLabel('Restart login').click();
  }

  await page.getByRole('textbox', { name: 'username' }).fill(login);
  await page.getByRole('textbox', { name: 'password' }).fill(password);
  await page.click('input[type="submit"]', { force: true });
};

export const randomName = (name: string, browserName: string, length: number) =>
  Array.from({ length }, (_el, index) => {
    return `${browserName}-${Math.floor(Math.random() * 10000)}-${index}-${name}`;
  });

export const createDoc = async (
  page: Page,
  docName: string,
  browserName: string,
  length: number = 1,
  isChild: boolean = false,
) => {
  const randomDocs = randomName(docName, browserName, length);

  for (let i = 0; i < randomDocs.length; i++) {
    if (!isChild) {
      const header = page.locator('header').first();
      await header.locator('h2').getByText('Docs').click();
    }

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

    await page.waitForURL('**/docs/**', {
      timeout: 10000,
      waitUntil: 'networkidle',
    });

    const input = page.getByLabel('doc title input');
    await expect(input).toBeVisible();
    await expect(input).toHaveText('');
    await input.click();

    await input.fill(randomDocs[i]);
    await input.blur();
  }

  return randomDocs;
};

export const verifyDocName = async (page: Page, docName: string) => {
  await expect(
    page.getByLabel('It is the card information about the document.'),
  ).toBeVisible({
    timeout: 10000,
  });

  try {
    await expect(
      page.getByRole('textbox', { name: 'doc title input' }),
    ).toHaveText(docName);
  } catch {
    await expect(page.getByRole('heading', { name: docName })).toBeVisible();
  }
};

export const addNewMember = async (
  page: Page,
  index: number,
  role: 'Administrator' | 'Owner' | 'Editor' | 'Reader',
  fillText: string = 'user ',
) => {
  const responsePromiseSearchUser = page.waitForResponse(
    (response) =>
      response.url().includes(`/users/?q=${encodeURIComponent(fillText)}`) &&
      response.status() === 200,
  );

  const inputSearch = page.getByRole('combobox', {
    name: 'Quick search input',
  });

  // Select a new user
  await inputSearch.fill(fillText);

  // Intercept response
  const responseSearchUser = await responsePromiseSearchUser;
  const users = (await responseSearchUser.json()) as {
    email: string;
  }[];

  // Choose user
  await page.getByRole('option', { name: users[index].email }).click();

  // Choose a role
  await page.getByLabel('doc-role-dropdown').click();
  await page.getByLabel(role).click();
  await page.getByRole('button', { name: 'Invite' }).click();

  return users[index].email;
};

export const getGridRow = async (page: Page, title: string) => {
  const docsGrid = page.getByRole('grid');
  await expect(docsGrid).toBeVisible();
  await expect(page.getByTestId('grid-loader')).toBeHidden();

  const rows = docsGrid.getByRole('row');

  const row = rows.filter({
    hasText: title,
  });

  await expect(row).toBeVisible();

  return row;
};

interface GoToGridDocOptions {
  nthRow?: number;
  title?: string;
}
export const goToGridDoc = async (
  page: Page,
  { nthRow = 1, title }: GoToGridDocOptions = {},
) => {
  const header = page.locator('header').first();
  await header.locator('h2').getByText('Docs').click();

  const docsGrid = page.getByTestId('docs-grid');
  await expect(docsGrid).toBeVisible();
  await expect(page.getByTestId('grid-loader')).toBeHidden();

  const rows = docsGrid.getByRole('row');

  const row = title
    ? rows.filter({
        hasText: title,
      })
    : rows.nth(nthRow);

  await expect(row).toBeVisible();

  const docTitleContent = row.locator('[aria-describedby="doc-title"]').first();
  const docTitle = await docTitleContent.textContent();
  expect(docTitle).toBeDefined();

  await row.getByRole('link').first().click();

  return docTitle as string;
};

export const updateDocTitle = async (page: Page, title: string) => {
  const input = page.getByLabel('doc title input');
  await expect(input).toBeVisible();
  await expect(input).toHaveText('');
  await input.click();
  await input.fill(title);
  await input.click();
  await input.blur();
  await verifyDocName(page, title);
};

export const waitForResponseCreateDoc = (page: Page) => {
  return page.waitForResponse(
    (response) =>
      response.url().includes('/documents/') &&
      response.url().includes('/children/') &&
      response.request().method() === 'POST',
  );
};

export const mockedDocument = async (page: Page, data: object) => {
  await page.route('**/documents/**/', async (route) => {
    const request = route.request();
    if (
      request.method().includes('GET') &&
      !request.url().includes('page=') &&
      !request.url().includes('versions') &&
      !request.url().includes('accesses') &&
      !request.url().includes('invitations')
    ) {
      const { abilities, ...doc } = data as unknown as {
        abilities?: Record<string, unknown>;
      };
      await route.fulfill({
        json: {
          id: 'mocked-document-id',
          content: '',
          title: 'Mocked document',
          path: '000000',
          abilities: {
            destroy: false, // Means not owner
            link_configuration: false,
            versions_destroy: false,
            versions_list: true,
            versions_retrieve: true,
            accesses_manage: false, // Means not admin
            update: false,
            partial_update: false, // Means not editor
            retrieve: true,
            link_select_options: {
              public: ['reader', 'editor'],
              authenticated: ['reader', 'editor'],
              restricted: null,
            },
            ...abilities,
          },
          link_reach: 'restricted',
          computed_link_reach: 'restricted',
          computed_link_role: 'reader',
          ancestors_link_reach: null,
          ancestors_link_role: null,
          created_at: '2021-09-01T09:00:00Z',
          user_role: 'owner',
          ...doc,
        },
      });
    } else {
      await route.continue();
    }
  });
};

export const mockedListDocs = async (page: Page, data: object[] = []) => {
  await page.route('**/documents/**/', async (route) => {
    const request = route.request();
    if (request.method().includes('GET') && request.url().includes('page=')) {
      await route.fulfill({
        json: {
          count: data.length,
          next: null,
          previous: null,
          results: data,
        },
      });
    }
  });
};

export const mockedInvitations = async (page: Page, json?: object) => {
  let result = [
    {
      id: '120ec765-43af-4602-83eb-7f4e1224548a',
      abilities: {
        destroy: true,
        update: true,
        partial_update: true,
        retrieve: true,
      },
      created_at: '2024-10-03T12:19:26.107687Z',
      email: 'test@invitation.test',
      document: '4888c328-8406-4412-9b0b-c0ba5b9e5fb6',
      role: 'editor',
      issuer: '7380f42f-02eb-4ad5-b8f0-037a0e66066d',
      is_expired: false,
      ...json,
    },
  ];
  await page.route('**/invitations/**/', async (route) => {
    const request = route.request();
    if (
      request.method().includes('GET') &&
      request.url().includes('invitations') &&
      request.url().includes('page=')
    ) {
      await route.fulfill({
        json: {
          count: 1,
          next: null,
          previous: null,
          results: result,
        },
      });
    } else {
      await route.continue();
    }
  });

  await page.route(
    '**/invitations/120ec765-43af-4602-83eb-7f4e1224548a/**/',
    async (route) => {
      const request = route.request();
      if (request.method().includes('DELETE')) {
        result = [];

        await route.fulfill({
          json: {},
        });
      }
    },
  );
};

export const mockedAccesses = async (page: Page, json?: object) => {
  await page.route('**/accesses/**/', async (route) => {
    const request = route.request();

    if (
      request.method().includes('GET') &&
      request.url().includes('accesses')
    ) {
      await route.fulfill({
        json: [
          {
            id: 'bc8bbbc5-a635-4f65-9817-fd1e9ec8ef87',
            user: {
              id: 'b4a21bb3-722e-426c-9f78-9d190eda641c',
              email: 'test@accesses.test',
            },
            team: '',
            max_ancestors_role: null,
            max_role: 'reader',
            role: 'reader',
            document: {
              id: 'mocked-document-id',
              path: '000000',
              depth: 1,
            },
            abilities: {
              destroy: true,
              update: true,
              partial_update: true,
              retrieve: true,
              link_select_options: {
                public: ['reader', 'editor'],
                authenticated: ['reader', 'editor'],
                restricted: null,
              },
              set_role_to: ['administrator', 'editor'],
            },
            ...json,
          },
        ],
      });
    } else {
      await route.continue();
    }
  });
};

export const expectLoginPage = async (page: Page) =>
  await expect(
    page.getByRole('heading', { name: 'Collaborative writing' }),
  ).toBeVisible({
    timeout: 10000,
  });
