import { createTeam } from '@documenso/lib/server-only/team/create-team';
import { nanoid } from '@documenso/lib/universal/id';
import { seedOrganisationMembers } from '@documenso/prisma/seed/organisations';
import { seedUser } from '@documenso/prisma/seed/users';
import { expect, test } from '@playwright/test';

import { apiSignin, apiSignout } from '../fixtures/authentication';
import { expectTextToBeVisible, expectTextToNotBeVisible, openDropdownMenu } from '../fixtures/generic';

test('[ORGANISATIONS]: create and delete organisation', async ({ page }) => {
  const { user, organisation } = await seedUser({
    isPersonalOrganisation: false,
  });

  await apiSignin({
    page,
    email: user.email,
    redirectPath: `/settings/organisations`,
  });

  await expect(page.getByRole('button', { name: 'Leave' })).toBeDisabled();

  await page.getByRole('link', { name: 'Manage' }).click();
  await page.waitForURL(`/o/${organisation.url}/settings/general`);

  await page.getByRole('button', { name: 'Delete' }).click();
  await page.getByLabel(`Confirm by typing delete ${organisation.name}`).fill(`delete ${organisation.name}`);
  await page.getByRole('button', { name: 'Delete' }).click();

  await page.waitForURL(`/settings/organisations`);
  await expectTextToBeVisible(page, 'No results found');
  await page.getByRole('button', { name: 'Create organisation' }).click();

  await page.getByLabel('Organisation Name*').fill('test');
  await page.getByRole('button', { name: 'Create' }).click();
  await expect(page.getByText('Your organisation has been created').first()).toBeVisible();
  await page.reload();

  await page.getByRole('row').filter({ hasText: 'test' }).getByRole('link').nth(1).click();
});

test('[ORGANISATIONS]: manage general settings', async ({ page }) => {
  const { user, organisation } = await seedUser({
    isPersonalOrganisation: false,
  });

  await apiSignin({
    page,
    email: user.email,
    redirectPath: `/o/${organisation.url}/settings/general`,
  });

  const updatedOrganisationId = `organisation-${Date.now()}`;

  // Update team.
  await page.getByLabel('Organisation Name*').click();
  await page.getByLabel('Organisation Name*').clear();
  await page.getByLabel('Organisation Name*').fill(updatedOrganisationId);
  await page.getByLabel('Organisation URL*').click();
  await page.getByLabel('Organisation URL*').clear();
  await page.getByLabel('Organisation URL*').fill(updatedOrganisationId);

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

  // Check we have been redirected to the new organisation URL and the name is updated.
  await page.waitForURL(`/o/${updatedOrganisationId}/settings/general`);
});

test('[ORGANISATIONS]: inherit members', async ({ page }) => {
  const {
    user,
    organisation,
    team: teamWithInheritMembers,
  } = await seedUser({
    isPersonalOrganisation: false,
  });

  const teamWithoutInheritedMembersUrl = `team-${nanoid()}`;

  await createTeam({
    userId: user.id,
    teamName: 'No inherit',
    teamUrl: teamWithoutInheritedMembersUrl,
    organisationId: organisation.id,
    inheritMembers: false,
  });

  const memberEmail = `member-${nanoid()}@test.documenso.com`;
  const memberEmail2 = `member-2-${nanoid()}@test.documenso.com`;
  const memberEmail3 = `member-3-${nanoid()}@test.documenso.com`;
  const managerEmail = `manager-${nanoid()}@test.documenso.com`;
  const adminEmail = `admin-${nanoid()}@test.documenso.com`;
  const ownerEmail = user.email;

  await seedOrganisationMembers({
    members: [
      {
        email: memberEmail,
        name: 'Member 1',
        organisationRole: 'MEMBER',
      },
      {
        email: memberEmail2,
        name: 'Member 2',
        organisationRole: 'MEMBER',
      },
      {
        email: memberEmail3,
        name: 'Member 3',
        organisationRole: 'MEMBER',
      },
      {
        email: managerEmail,
        name: 'Manager',
        organisationRole: 'MANAGER',
      },
      {
        email: adminEmail,
        name: 'Admin',
        organisationRole: 'ADMIN',
      },
    ],
    organisationId: organisation.id,
  });

  await apiSignin({
    page,
    email: user.email,
    redirectPath: `/t/${teamWithoutInheritedMembersUrl}/settings/members`,
  });

  // Check from admin POV that member counts are correct
  // You should only see the manager/admins from the organisation in this table.
  await expect(page.getByRole('row').filter({ hasText: 'Team Admin' }).getByText(managerEmail)).toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: 'Team Admin' }).getByText(adminEmail)).toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: 'Team Admin' }).getByText(ownerEmail)).toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: memberEmail })).not.toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: memberEmail2 })).not.toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: memberEmail3 })).not.toBeVisible();

  // Explicitly add a member to the team.
  await page.getByRole('button', { name: 'Add members' }).click();
  await page.getByRole('combobox').click();
  await page.getByRole('option', { name: 'Member 1' }).first().click();
  await page.getByRole('button', { name: 'Next' }).click();
  await page.getByRole('button', { name: 'Add Members' }).click();
  await expect(page.getByRole('row').filter({ hasText: 'Team Member' }).getByText(memberEmail)).toBeVisible();

  await page.goto(`/t/${teamWithInheritMembers.url}/settings/members`);

  // Check from member POV that member counts are correct for inherit members team.
  await expect(page.getByRole('row').filter({ hasText: 'Team Admin' }).getByText(managerEmail)).toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: 'Team Admin' }).getByText(adminEmail)).toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: 'Team Admin' }).getByText(ownerEmail)).toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: 'Team Member' }).getByText(memberEmail)).toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: 'Team Member' }).getByText(memberEmail2)).toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: 'Team Member' }).getByText(memberEmail3)).toBeVisible();

  // Disable inherit mode.
  await page.goto(`/t/${teamWithInheritMembers.url}/settings/groups`);
  await page.getByRole('button', { name: 'Disable access' }).click();
  await page.getByRole('button', { name: 'Disable' }).click();
  await expect(page.getByText('Enable Access').first()).toBeVisible();

  // Expect the inherited members to disappear
  await page.goto(`/t/${teamWithInheritMembers.url}/settings/members`);
  await expect(page.getByRole('row').filter({ hasText: 'Team Admin' }).getByText(managerEmail)).toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: 'Team Admin' }).getByText(adminEmail)).toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: 'Team Admin' }).getByText(ownerEmail)).toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: memberEmail })).not.toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: memberEmail2 })).not.toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: memberEmail3 })).not.toBeVisible();
});

test('[ORGANISATIONS]: manage groups and members', async ({ page }) => {
  const {
    user,
    organisation,
    team: teamInherit,
  } = await seedUser({
    isPersonalOrganisation: false,
  });

  const teamInheritName = teamInherit.name;

  const teamA = `team-${nanoid()}`;
  const teamAName = `TeamA - No inherit`;
  const teamB = `team-${nanoid()}`;
  const teamBName = `TeamB - No inherit`;

  await createTeam({
    userId: user.id,
    teamName: teamAName,
    teamUrl: teamA,
    organisationId: organisation.id,
    inheritMembers: false,
  });

  await createTeam({
    userId: user.id,
    teamName: teamBName,
    teamUrl: teamB,
    organisationId: organisation.id,
    inheritMembers: false,
  });

  const memberEmail1 = `member-1-${nanoid()}@test.documenso.com`;
  const memberEmail2 = `member-2-${nanoid()}@test.documenso.com`;
  const memberEmail3 = `member-3-${nanoid()}@test.documenso.com`;
  const memberEmail4 = `member-4-${nanoid()}@test.documenso.com`;
  const memberEmail5 = `member-5-${nanoid()}@test.documenso.com`;
  const memberEmail6 = `member-6-${nanoid()}@test.documenso.com`;

  const adminEmail1 = `admin-1-${nanoid()}@test.documenso.com`;
  const adminEmail2 = `admin-2-${nanoid()}@test.documenso.com`;
  const adminEmail3 = `admin-3-${nanoid()}@test.documenso.com`;

  const ownerEmail = user.email;

  await seedOrganisationMembers({
    members: [
      {
        email: memberEmail1,
        name: 'Member1',
        organisationRole: 'MEMBER',
      },
      {
        email: memberEmail2,
        name: 'Member2',
        organisationRole: 'MEMBER',
      },
      {
        email: memberEmail3,
        name: 'Member3',
        organisationRole: 'MEMBER',
      },
      {
        email: memberEmail4,
        name: 'Member4',
        organisationRole: 'MEMBER',
      },
      {
        email: memberEmail5,
        name: 'Member5',
        organisationRole: 'MEMBER',
      },
      {
        email: memberEmail6,
        name: 'Member6',
        organisationRole: 'MEMBER',
      },
      {
        email: adminEmail1,
        name: 'Admin1',
        organisationRole: 'ADMIN',
      },
      {
        email: adminEmail2,
        name: 'Admin2',
        organisationRole: 'ADMIN',
      },
      {
        email: adminEmail3,
        name: 'Admin3',
        organisationRole: 'ADMIN',
      },
    ],
    organisationId: organisation.id,
  });

  await apiSignin({
    page,
    email: user.email,
    redirectPath: `/o/${organisation.url}/settings/groups`,
  });

  // Create a custom group A with 3 members "ORGANISATION ADMIN" to check that they get the correct roles.
  await page.getByRole('button', { name: 'Create group' }).click();
  await page.getByRole('textbox', { name: 'Group Name *' }).fill('CUSTOM_GROUP');
  await page.getByRole('combobox').filter({ hasText: 'Organisation Member' }).click();
  await page.getByRole('option', { name: 'Organisation Admin' }).click();
  await page.getByTestId('group-members-picker').click();
  await page.getByRole('option', { name: 'Member1' }).click();
  await page.getByRole('option', { name: 'Member2' }).click();
  await page.getByRole('option', { name: 'Member3' }).click();
  // Close the multiselect dropdown so it doesn't overlap the submit button.
  await page.getByRole('heading', { name: 'Create group' }).click();
  await page.getByTestId('dialog-create-organisation-button').click();
  await expect(page.getByText('Group has been created.').first()).toBeVisible();

  await page.goto(`/o/${organisation.url}/settings/members`);

  // Confirm org roles have been applied to these members.
  await expect(page.getByRole('row').filter({ hasText: 'Organisation Admin' }).getByText(memberEmail1)).toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: 'Organisation Admin' }).getByText(memberEmail2)).toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: 'Organisation Admin' }).getByText(memberEmail3)).toBeVisible();

  // Test updating the group.
  await page.goto(`/o/${organisation.url}/settings/groups`);
  await page.getByRole('link', { name: 'Manage' }).click();
  await page.getByRole('textbox', { name: 'Group Name *' }).fill('CUSTOM_GROUP_A');
  await page.getByRole('combobox').filter({ hasText: 'Organisation Admin' }).click();
  await page.getByRole('option', { name: 'Organisation Member' }).click();
  // Remove Member3 by clicking the X on its chip in the multiselect.
  await page
    .getByTestId('group-members-picker')
    .locator('div', { hasText: /^Member3/ })
    .getByRole('button', { name: 'Remove' })
    .click();
  await page.getByRole('button', { name: 'Update' }).click();
  await expect(page.getByText('Group has been updated successfully').first()).toBeVisible();

  await page.goto(`/o/${organisation.url}/settings/groups`);

  // Create a custom member group with the 3 admins to check that they still get the ADMIN roles.
  await page.getByRole('button', { name: 'Create group' }).click();
  await page.getByRole('textbox', { name: 'Group Name *' }).fill('CUSTOM_GROUP_ADMINS');
  await page.getByTestId('group-members-picker').click();
  await page.getByRole('option', { name: 'Admin1' }).click();
  await page.getByRole('option', { name: 'Admin2' }).click();
  await page.getByRole('option', { name: 'Admin3' }).click();
  // Close the multiselect dropdown so it doesn't overlap the submit button.
  await page.getByRole('heading', { name: 'Create group' }).click();
  await page.getByTestId('dialog-create-organisation-button').click();
  await expect(page.getByText('Group has been created.').first()).toBeVisible();

  await page.goto(`/o/${organisation.url}/settings/members`);

  // Confirm admins still get admin roles.
  await expect(page.getByRole('row').filter({ hasText: 'Organisation Admin' }).getByText(adminEmail1)).toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: 'Organisation Admin' }).getByText(adminEmail2)).toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: 'Organisation Admin' }).getByText(adminEmail3)).toBeVisible();

  // Create another custom group with 3 members with "ORGANISATION MEMBER" role.
  await page.goto(`/o/${organisation.url}/settings/groups`);
  await page.getByRole('button', { name: 'Create group' }).click();
  await page.getByRole('textbox', { name: 'Group Name *' }).fill('CUSTOM_GROUP_B');
  await page.getByRole('combobox').filter({ hasText: 'Organisation Member' }).click();
  await page.getByRole('option', { name: 'Organisation Admin' }).click();
  await page.getByTestId('group-members-picker').click();
  await page.getByRole('option', { name: 'Member4' }).click();
  await page.getByRole('option', { name: 'Member5' }).click();
  // Close the multiselect dropdown so it doesn't overlap the submit button.
  await page.getByRole('heading', { name: 'Create group' }).click();
  await page.getByTestId('dialog-create-organisation-button').click();
  await expect(page.getByText('Group has been created.').first()).toBeVisible();

  // Assign CUSTOM_GROUP_A to TeamA
  await page.goto(`/t/${teamA}/settings/groups`);
  await page.getByRole('button', { name: 'Add groups' }).click();
  await page.getByTestId('team-groups-picker').click();
  await page.getByRole('option', { name: 'CUSTOM_GROUP_A', exact: true }).click();
  // Close the multiselect dropdown so it doesn't overlap the Next button.
  await page.getByRole('heading', { name: 'Add groups' }).click();
  await page.getByRole('button', { name: 'Next' }).click();
  await page.getByRole('combobox').click();
  await page.getByRole('option', { name: 'Manager' }).click();
  await page.getByRole('button', { name: 'Create Groups' }).click();
  await expect(page.getByText('Team members have been added').first()).toBeVisible();

  // Assign CUSTOM_GROUP_B to TeamA
  await page.goto(`/t/${teamA}/settings/groups`);
  await page.getByRole('button', { name: 'Add groups' }).click();
  await page.getByTestId('team-groups-picker').click();
  await page.getByRole('option', { name: 'CUSTOM_GROUP_B', exact: true }).click();
  // Close the multiselect dropdown so it doesn't overlap the Next button.
  await page.getByRole('heading', { name: 'Add groups' }).click();
  await page.getByRole('button', { name: 'Next' }).click();
  await page.getByRole('combobox').click();
  await page.getByRole('option', { name: 'Manager' }).click();
  await page.getByRole('button', { name: 'Create Groups' }).click();
  await expect(page.getByText('Team members have been added').first()).toBeVisible();

  // Update CUSTOM_GROUP_B
  const updateBtn = page.getByRole('row', { name: 'CUSTOM_GROUP_B' }).getByRole('button');
  await openDropdownMenu(page, updateBtn);
  await expect(page.getByRole('menuitem', { name: 'Update role' })).toBeVisible();
  await page.getByRole('menuitem', { name: 'Update role' }).click();
  await page.getByRole('combobox').click();
  await page.getByRole('option', { name: 'Team Admin' }).click();
  await page.getByRole('button', { name: 'Update' }).click();
  await expectTextToBeVisible(page, 'You have updated the team group');
  await expect(page.getByText('Team Admin').first()).toBeVisible();
  await page.reload();

  // Delete CUSTOM_GROUP_B
  const deleteBtn = page.getByRole('row', { name: 'CUSTOM_GROUP_B' }).getByRole('button');
  await openDropdownMenu(page, deleteBtn);
  await expect(page.getByRole('menuitem', { name: 'Remove' })).toBeVisible();
  await page.getByRole('menuitem', { name: 'Remove' }).click();
  await page.getByRole('button', { name: 'Delete' }).click();
  await expectTextToBeVisible(page, 'You have successfully removed this group from the team.');
  await expect(page.getByText('CUSTOM_GROUP_B')).not.toBeVisible();

  // Navigate to team members and validate members are there.
  await page.goto(`/t/${teamA}/settings/members`);

  await expect(page.getByRole('row').filter({ hasText: 'Team Manager' }).getByText(memberEmail1)).toBeVisible();
  await expect(page.getByRole('row').filter({ hasText: 'Team Manager' }).getByText(memberEmail2)).toBeVisible();

  // Member 1 should see inherit team and teamA
  await apiSignout({ page });
  await apiSignin({ page, email: memberEmail1, redirectPath: `/o/${organisation.url}` });
  await expectTextToBeVisible(page, teamInheritName);
  await expectTextToBeVisible(page, teamAName);
  await expectTextToNotBeVisible(page, teamBName);

  // Member 3 should only see inherit team
  await apiSignout({ page });
  await apiSignin({ page, email: memberEmail3, redirectPath: `/o/${organisation.url}` });
  await expectTextToBeVisible(page, teamInheritName);
  await expectTextToNotBeVisible(page, teamAName);
  await expectTextToNotBeVisible(page, teamBName);

  // Admin 1 should see all teams.
  await apiSignout({ page });
  await apiSignin({ page, email: adminEmail1, redirectPath: `/o/${organisation.url}` });
  await expectTextToBeVisible(page, teamInheritName);
  await expectTextToBeVisible(page, teamAName);
  await expectTextToBeVisible(page, teamBName);
});

test('[ORGANISATIONS]: member invites', async ({ page }) => {
  const { user, organisation, team } = await seedUser({
    inheritMembers: false,
  });

  const { user: user2 } = await seedUser({
    isPersonalOrganisation: false,
  });
  const { user: user3 } = await seedUser({
    isPersonalOrganisation: false,
  });

  await apiSignin({
    page,
    email: user.email,
    redirectPath: `/o/${organisation.url}/settings/members`,
  });

  await page.getByRole('button', { name: 'Invite member' }).click();
  await page.getByRole('textbox', { name: 'Email address *' }).click();
  await page.getByRole('textbox', { name: 'Email address *' }).fill(user2.email);
  await page.getByRole('button', { name: 'Add more' }).click();

  await page.locator('input[name="invitations\\.1\\.email"]').fill(user3.email);
  await page.getByRole('button', { name: 'Invite' }).click();

  await page.getByRole('tab', { name: 'Pending' }).click();
  await expect(page.getByText(user2.email)).toBeVisible();
  await expect(page.getByText(user3.email)).toBeVisible();

  const inviteActionBtn = page.getByRole('row', { name: user3.email }).getByRole('button');
  await openDropdownMenu(page, inviteActionBtn);
  await expect(page.getByRole('menuitem', { name: 'Remove' })).toBeVisible();
  await page.getByRole('menuitem', { name: 'Remove' }).click();
  await expect(page.getByText('Invitation has been deleted').first()).toBeVisible();
  await expect(page.getByText(user3.email)).not.toBeVisible();

  // Sign in as member and accept invite
  await apiSignout({ page });
  await apiSignin({ page, email: user2.email, redirectPath: `/settings/organisations` });
  await page.getByRole('button', { name: 'View invites' }).click();
  await page.getByRole('button', { name: 'Accept' }).click();
  await expect(page.getByText('Invitation accepted').first()).toBeVisible();

  // Sign back in as org owner.
  await apiSignout({ page });
  await apiSignin({ page, email: user.email, redirectPath: `/t/${team.url}/settings/members` });

  // Expect 1 member in team.
  await expect(page.getByText(user.email)).toBeVisible();
  await expect(page.getByText(user2.email)).not.toBeVisible();

  // Add member to team.
  await page.getByRole('button', { name: 'Add members' }).click();
  await page.getByRole('combobox').click();
  await page.getByRole('option', { name: user2.name ?? '' }).click();
  await page.getByRole('button', { name: 'Next' }).click();
  await page.getByRole('button', { name: 'Add Members' }).click();

  // Expect 2 members to be visible.
  await expect(page.getByText(user.email)).toBeVisible();
  await expect(page.getByText(user2.email)).toBeVisible();

  const memberActionBtn = page.getByRole('row', { name: user2.email }).getByRole('button');
  await openDropdownMenu(page, memberActionBtn);
  await expect(page.getByRole('menuitem', { name: 'Remove' })).toBeVisible();
  await page.getByRole('menuitem', { name: 'Remove' }).click();
  await page.getByRole('button', { name: 'Remove' }).click();
  await expect(page.getByText('You have successfully removed').first()).toBeVisible();

  // Expect 1 member in team.
  await expect(page.getByText(user.email)).toBeVisible();
  await expect(page.getByText(user2.email)).not.toBeVisible();

  // Expect 2 members in organisation.
  await page.goto(`/o/${organisation.url}/settings/members`);
  await expect(page.getByText(user.email)).toBeVisible();
  await expect(page.getByText(user2.email)).toBeVisible();

  const orgMemberBtn = page.getByRole('row', { name: user2.email }).getByRole('button');
  await openDropdownMenu(page, orgMemberBtn);
  await expect(page.getByRole('menuitem', { name: 'Remove' })).toBeVisible();
  await page.getByRole('menuitem', { name: 'Remove' }).click();
  await page.getByRole('button', { name: 'Delete' }).click();
  await expect(page.getByText('You have successfully removed this user').first()).toBeVisible();

  // Expect 1 member in organisation.
  await expect(page.getByText(user.email)).toBeVisible();
  await expect(page.getByText(user2.email)).not.toBeVisible();
});

test('[ORGANISATIONS]: leave organisation', async ({ page }) => {
  const { organisation } = await seedUser({
    isPersonalOrganisation: false,
  });

  const memberEmail = `member-${nanoid()}@test.documenso.com`;

  await seedOrganisationMembers({
    members: [
      {
        email: memberEmail,
        name: 'Member 1',
        organisationRole: 'MEMBER',
      },
    ],
    organisationId: organisation.id,
  });

  await apiSignin({
    page,
    email: memberEmail,
    redirectPath: `/settings/organisations`,
  });
  await page.getByRole('button', { name: 'Leave' }).click();
  await page.getByRole('button', { name: 'Leave' }).click();

  await expect(page.getByText('You have successfully left this organisation').first()).toBeVisible();
  await expect(page.getByText('No results found').first()).toBeVisible();
});
