import ClientRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientRepresentation";
import IdentityProviderRepresentation from "@keycloak/keycloak-admin-client/lib/defs/identityProviderRepresentation";
import { Page, expect, test } from "@playwright/test";
import { randomUUID } from "node:crypto";

import {
  createClient,
  createIdentityProvider,
  createRandomUserWithPassword,
  deleteClient,
  deleteIdentityProvider,
  deleteUser,
  findClientByClientId,
  inRealm,
} from "../admin-client";
import { SERVER_URL } from "../constants";
import groupsIdPClient from "../realms/groups-idp.json" assert { type: "json" };

const realm = "groups";

test.describe("Account linking", () => {
  let groupIdPClientId: string;
  let user: string;
  // Tests for keycloak account console, section Account linking in Account security
  test.beforeAll(async () => {
    user = await createRandomUserWithPassword("user-" + randomUUID(), "pwd");

    const kcGroupsIdpId = await findClientByClientId("groups-idp");
    if (kcGroupsIdpId) {
      await deleteClient(kcGroupsIdpId);
    }
    groupIdPClientId = await createClient(
      groupsIdPClient as ClientRepresentation,
    );
    const idp: IdentityProviderRepresentation = {
      alias: "master-idp",
      providerId: "oidc",
      enabled: true,
      config: {
        clientId: "groups-idp",
        clientSecret: "H0JaTc7VBu3HJR26vrzMxgidfJmgI5Dw",
        validateSignature: "false",
        tokenUrl: `${SERVER_URL}/realms/master/protocol/openid-connect/token`,
        jwksUrl: `${SERVER_URL}/realms/master/protocol/openid-connect/certs`,
        issuer: `${SERVER_URL}/realms/master`,
        authorizationUrl: `${SERVER_URL}/realms/master/protocol/openid-connect/auth`,
        logoutUrl: `${SERVER_URL}/realms/master/protocol/openid-connect/logout`,
        userInfoUrl: `${SERVER_URL}/realms/master/protocol/openid-connect/userinfo`,
      },
    };

    await inRealm(realm, () => createIdentityProvider(idp));
  });

  test.afterAll(async () => {
    await deleteUser(user);
  });
  test.afterAll(async () => {
    await deleteClient(groupIdPClientId);
  });
  test.afterAll(async () => {
    await inRealm(realm, () => deleteIdentityProvider("master-idp"));
  });

  test("Linking", async ({ page }) => {
    // If refactoring this, consider introduction of helper functions for individual pages - login, update profile etc.
    await page.goto(`/realms/${realm}/account`);

    // Click the login via master-idp provider button
    await loginWithIdp(page, "master-idp");

    // Now the login at the master-idp should be visible
    await loginWithUsernamePassword(page, "admin", "admin");

    // Now the update-profile page should be visible
    await updateProfile(page, "test", "user", "testuser@keycloak.org");

    // Now the account console should be visible
    await page.getByTestId("accountSecurity").click();
    await expect(
      page.getByTestId("account-security/linked-accounts"),
    ).toBeVisible();
    await page.getByTestId("account-security/linked-accounts").click();
    await expect(
      page
        .getByTestId("linked-accounts/master-idp")
        .getByRole("button", { name: "Unlink account" }),
    ).toBeVisible();
    await page
      .getByTestId("linked-accounts/master-idp")
      .getByRole("button", { name: "Unlink account" })
      .click();

    // Expect an error shown that the account cannot be unlinked
    await expect(page.getByTestId("last-alert")).toContainText(
      "You can''t remove last federated identity as you don''t have a password.",
    );
  });
});

async function updateProfile(
  page: Page,
  firstName: string,
  lastName: string,
  email: string,
) {
  await expect(
    page.getByRole("heading", { name: "Update Account Information" }),
  ).toBeVisible();
  await page.getByLabel("Email").fill(email);
  await page.getByLabel("First name").fill(firstName);
  await page.getByLabel("Last name").fill(lastName);
  await page.getByRole("button", { name: "Submit" }).click();
}

async function loginWithUsernamePassword(page, username, password) {
  await page.getByLabel("Username").fill(username);
  await page.getByLabel("Password", { exact: true }).fill(password);
  await page.getByRole("button", { name: "Sign In" }).click();
}

async function loginWithIdp(page, idpAlias: string) {
  await page.getByRole("link", { name: idpAlias }).click();
}
