// tslint:disable:no-unused-expression
import { faker } from "@faker-js/faker";
import { fail } from "assert";
import * as chai from "chai";
import { KeycloakAdminClient } from "../src/client.js";
import type GroupRepresentation from "../src/defs/groupRepresentation.js";
import type { PartialImportRealmRepresentation } from "../src/defs/realmRepresentation.js";
import { credentials } from "./constants.js";

const expect = chai.expect;

const createRealm = async (kcAdminClient: KeycloakAdminClient) => {
  const realmId = faker.internet.userName().toLowerCase();
  const realmName = faker.internet.userName().toLowerCase();
  const realm = await kcAdminClient.realms.create({
    id: realmId,
    realm: realmName,
  });
  expect(realm.realmName).to.be.equal(realmName);

  return { realmId, realmName };
};

const deleteRealm = async (
  kcAdminClient: KeycloakAdminClient,
  currentRealmName: string,
) => {
  await kcAdminClient.realms.del({ realm: currentRealmName });
  const realm = await kcAdminClient.realms.findOne({
    realm: currentRealmName,
  });
  expect(realm).to.be.null;
};

describe("Realms", () => {
  let kcAdminClient: KeycloakAdminClient;
  let currentRealmId: string;
  let currentRealmName: string;

  before(async () => {
    kcAdminClient = new KeycloakAdminClient();
    await kcAdminClient.auth(credentials);
  });

  it("list realms", async () => {
    const realms = await kcAdminClient.realms.find();
    expect(realms.length).to.be.least(1);
  });

  it("create realm", async () => {
    const realmId = faker.internet.userName().toLowerCase();
    const realmName = faker.internet.userName().toLowerCase();
    const realm = await kcAdminClient.realms.create({
      id: realmId,
      realm: realmName,
    });
    expect(realm.realmName).to.be.equal(realmName);
    currentRealmId = realmId;
    currentRealmName = realmName;
  });

  it("get a realm", async () => {
    const realm = await kcAdminClient.realms.findOne({
      realm: currentRealmName,
    });
    expect(realm).to.include({
      id: currentRealmId,
      realm: currentRealmName,
    });
  });

  const roleToImport: PartialImportRealmRepresentation = {
    ifResourceExists: "FAIL",
    roles: {
      realm: [
        {
          id: "9d2638c8-4c62-4c42-90ea-5f3c836d0cc8",
          name: "myRole",
          scopeParamRequired: false,
          composite: false,
        },
      ],
    },
  };

  it("does partial import", async () => {
    const result = await kcAdminClient.realms.partialImport({
      realm: currentRealmName,
      rep: roleToImport,
    });
    expect(result.added).to.be.eq(1);
    expect(result.overwritten).to.be.eq(0);
    expect(result.skipped).to.be.eq(0);
    expect(result.results.length).to.be.eq(1);
    expect(result.results[0].action).to.be.eq("ADDED");
    expect(result.results[0].resourceName).to.be.eq("myRole");
    expect(result.results[0].id).to.exist;
  });

  it("export a realm", async () => {
    const realm = await kcAdminClient.realms.export({
      realm: currentRealmName,
      exportClients: true,
      exportGroupsAndRoles: true,
    });
    expect(realm).to.include({
      id: currentRealmId,
      realm: currentRealmName,
    });
  });

  it("update a realm", async () => {
    await kcAdminClient.realms.update(
      { realm: currentRealmName },
      {
        displayName: "test",
      },
    );
    const realm = await kcAdminClient.realms.findOne({
      realm: currentRealmName,
    });
    expect(realm).to.include({
      id: currentRealmId,
      realm: currentRealmName,
      displayName: "test",
    });
  });

  it("client registration policy providers", async () => {
    const list =
      await kcAdminClient.realms.getClientRegistrationPolicyProviders({
        realm: currentRealmName,
      });

    expect(list).to.be.ok;
  });

  it("delete a realm", async () => {
    await kcAdminClient.realms.del({ realm: currentRealmName });
    const realm = await kcAdminClient.realms.findOne({
      realm: currentRealmName,
    });
    expect(realm).to.be.null;
  });

  describe("Realm Client Initial Access", () => {
    before(async () => {
      kcAdminClient = new KeycloakAdminClient();
      await kcAdminClient.auth(credentials);

      const created = await createRealm(kcAdminClient);
      currentRealmName = created.realmName;

      await kcAdminClient.realms.createClientsInitialAccess(
        { realm: currentRealmName },
        { count: 1, expiration: 0 },
      );
    });

    after(async () => {
      deleteRealm(kcAdminClient, currentRealmName);
    });

    it("list client initial access", async () => {
      const initialAccess = await kcAdminClient.realms.getClientsInitialAccess({
        realm: currentRealmName,
      });
      expect(initialAccess).to.be.ok;
      expect(initialAccess[0].count).to.be.eq(1);
    });

    it("del client initial access", async () => {
      const access = await kcAdminClient.realms.createClientsInitialAccess(
        { realm: currentRealmName },
        { count: 1, expiration: 0 },
      );
      expect(
        (
          await kcAdminClient.realms.getClientsInitialAccess({
            realm: currentRealmName,
          })
        ).length,
      ).to.be.eq(2);

      await kcAdminClient.realms.delClientsInitialAccess({
        realm: currentRealmName,
        id: access.id!,
      });

      const initialAccess = await kcAdminClient.realms.getClientsInitialAccess({
        realm: currentRealmName,
      });
      expect(initialAccess).to.be.ok;
      expect(initialAccess[0].count).to.be.eq(1);
    });
  });

  describe("Realm default groups", () => {
    const groupName = "my-group";
    let currentGroup: GroupRepresentation;

    before(async () => {
      kcAdminClient = new KeycloakAdminClient();
      await kcAdminClient.auth(credentials);

      currentRealmName = (await createRealm(kcAdminClient)).realmName;
      currentGroup = await kcAdminClient.groups.create({
        name: groupName,
        realm: currentRealmName,
      });
    });

    after(async () => {
      deleteRealm(kcAdminClient, currentRealmName);
    });

    it("add group to default groups", async () => {
      await kcAdminClient.realms.addDefaultGroup({
        id: currentGroup.id!,
        realm: currentRealmName,
      });

      const defaultGroups = await kcAdminClient.realms.getDefaultGroups({
        realm: currentRealmName,
      });

      expect(defaultGroups).to.be.ok;
      expect(defaultGroups.length).to.be.eq(1);
      expect(defaultGroups[0].id).to.be.eq(currentGroup.id);
    });

    it("get a group by its path name", async () => {
      const queriedGroup = await kcAdminClient.realms.getGroupByPath({
        realm: currentRealmName,
        path: groupName,
      });

      expect(queriedGroup).to.be.ok;
      expect(queriedGroup.id).to.be.eq(currentGroup.id);
    });

    it("remove group from default groups", async () => {
      await kcAdminClient.realms.removeDefaultGroup({
        id: currentGroup.id!,
        realm: currentRealmName,
      });

      const defaultGroups = await kcAdminClient.realms.getDefaultGroups({
        realm: currentRealmName,
      });

      expect(defaultGroups).to.be.ok;
      expect(defaultGroups.length).to.be.eq(0);
    });
  });

  describe("Realm Events", () => {
    before(async () => {
      kcAdminClient = new KeycloakAdminClient();
      await kcAdminClient.auth(credentials);

      const created = await createRealm(kcAdminClient);
      currentRealmId = created.realmId;
      currentRealmName = created.realmName;
    });

    it("get events config for a realm", async () => {
      const config = await kcAdminClient.realms.getConfigEvents({
        realm: currentRealmName,
      });

      expect(config).to.be.ok;
      expect(config.adminEventsEnabled).to.be.eq(false);
    });

    it("enable events", async () => {
      const config = await kcAdminClient.realms.getConfigEvents({
        realm: currentRealmName,
      });
      config.eventsEnabled = true;
      await kcAdminClient.realms.updateConfigEvents(
        { realm: currentRealmName },
        config,
      );

      const newConfig = await kcAdminClient.realms.getConfigEvents({
        realm: currentRealmName,
      });

      expect(newConfig).to.be.ok;
      expect(newConfig.eventsEnabled).to.be.eq(true);
    });

    it("list events of a realm", async () => {
      // @TODO: In order to test it, there have to be events
      const events = await kcAdminClient.realms.findEvents({
        realm: currentRealmName,
      });

      expect(events).to.be.ok;
    });

    it("list admin events of a realm", async () => {
      // @TODO: In order to test it, there have to be events
      const events = await kcAdminClient.realms.findAdminEvents({
        realm: currentRealmName,
      });

      expect(events).to.be.ok;
    });

    it("clear events", async () => {
      await kcAdminClient.realms.clearEvents({ realm: currentRealmName });
      await kcAdminClient.realms.clearAdminEvents({ realm: currentRealmName });

      const events = await kcAdminClient.realms.findAdminEvents({
        realm: currentRealmName,
      });

      expect(events).to.deep.eq([]);
    });

    after(async () => {
      deleteRealm(kcAdminClient, currentRealmName);
    });
  });

  describe("Realm Users Management Permissions", () => {
    before(async () => {
      kcAdminClient = new KeycloakAdminClient();
      await kcAdminClient.auth(credentials);

      const created = await createRealm(kcAdminClient);
      currentRealmId = created.realmId;
      currentRealmName = created.realmName;
    });

    it("get users management permissions", async () => {
      const managementPermissions =
        await kcAdminClient.realms.getUsersManagementPermissions({
          realm: currentRealmName,
        });
      expect(managementPermissions).to.be.ok;
    });

    it.skip("enable users management permissions", async () => {
      const managementPermissions =
        await kcAdminClient.realms.updateUsersManagementPermissions({
          realm: currentRealmName,
          enabled: true,
        });
      expect(managementPermissions).to.include({ enabled: true });
    });

    it("get realm keys", async () => {
      const keys = await kcAdminClient.realms.getKeys({
        realm: currentRealmName,
      });
      expect(keys.active).to.be.ok;
    });

    after(async () => {
      deleteRealm(kcAdminClient, currentRealmName);
    });
  });

  describe("Realm Session Management", () => {
    before(async () => {
      kcAdminClient = new KeycloakAdminClient();
      await kcAdminClient.auth(credentials);

      const created = await createRealm(kcAdminClient);
      currentRealmId = created.realmId;
      currentRealmName = created.realmName;
    });

    it("gets client session stats", async () => {
      const sessionStats = await kcAdminClient.realms.getClientSessionStats({
        realm: currentRealmName,
      });
      expect(sessionStats).to.be.ok;
    });

    it("push revocation", async () => {
      const push = await kcAdminClient.realms.pushRevocation({
        realm: currentRealmName,
      });
      expect(push).to.be.ok;
    });

    it("logs out all sessions", async () => {
      const logout = await kcAdminClient.realms.logoutAll({
        realm: currentRealmName,
      });
      expect(logout).to.be.ok;
    });

    after(async () => {
      deleteRealm(kcAdminClient, currentRealmName);
    });
  });

  describe("Realm connection settings", () => {
    it("should fail with invalid ldap settings", async () => {
      try {
        await kcAdminClient.realms.testLDAPConnection(
          { realm: "master" },
          {
            action: "testConnection",
            authType: "simple",
            bindCredential: "1",
            bindDn: "1",
            connectionTimeout: "",
            connectionUrl: "1",
            startTls: "",
            useTruststoreSpi: "always",
          },
        );
        fail("exception should have been thrown");
      } catch (error) {
        expect(error).to.be.ok;
      }
    });

    it("should fail with invalid smtp settings", async () => {
      try {
        const user = (
          await kcAdminClient.users.find({ username: credentials.username })
        )[0];
        user.email = "test@test.com";
        await kcAdminClient.users.update({ id: user.id! }, user);
        await kcAdminClient.realms.testSMTPConnection(
          { realm: "master" },
          {
            from: "cdd1641ff4-1781a4@inbox.mailtrap.io",
            host: "localhost",
            port: 3025,
          },
        );
        fail("exception should have been thrown");
      } catch (error) {
        expect(error).to.be.ok;
      }
    });

    it("should fail with invalid ldap server capabilities", async () => {
      try {
        await kcAdminClient.realms.ldapServerCapabilities(
          { realm: "master" },
          {
            action: "testConnection",
            authType: "simple",
            bindCredential: "1",
            bindDn: "1",
            connectionTimeout: "",
            connectionUrl: "1",
            startTls: "",
            useTruststoreSpi: "always",
          },
        );
        fail("exception should have been thrown");
      } catch (error) {
        expect(error).to.be.ok;
      }
    });
  });

  describe("Realm localization", () => {
    currentRealmName = "master";

    it.skip("enable localization", async () => {
      await kcAdminClient.realms.getRealmLocalizationTexts({
        realm: currentRealmName,
        selectedLocale: "nl",
      });
    });

    it.skip("should add localization", async () => {
      await kcAdminClient.realms.addLocalization(
        { realm: currentRealmName, selectedLocale: "nl", key: "theKey" },
        "value",
      );
    });

    it.skip("should get realm specific locales", async () => {
      const locales = await kcAdminClient.realms.getRealmSpecificLocales({
        realm: currentRealmName,
      });

      expect(locales).to.be.ok;
      expect(locales).to.be.deep.eq(["nl"]);
    });

    it.skip("should get localization for specified locale", async () => {
      const texts = await kcAdminClient.realms.getRealmLocalizationTexts({
        realm: currentRealmName,
        selectedLocale: "nl",
      });

      expect(texts).to.be.ok;
      expect(texts.theKey).to.be.eq("value");
    });

    it.skip("should delete localization for specified locale key", async () => {
      await kcAdminClient.realms.deleteRealmLocalizationTexts({
        realm: currentRealmName,
        selectedLocale: "nl",
        key: "theKey",
      });

      const texts = await kcAdminClient.realms.getRealmLocalizationTexts({
        realm: currentRealmName,
        selectedLocale: "nl",
      });
      expect(texts).to.be.ok;
      expect(texts).to.be.deep.eq({});
    });

    it.skip("should delete localization for specified locale", async () => {
      await kcAdminClient.realms.deleteRealmLocalizationTexts({
        realm: currentRealmName,
        selectedLocale: "nl",
      });

      const locales = await kcAdminClient.realms.getRealmSpecificLocales({
        realm: currentRealmName,
      });
      expect(locales).to.be.ok;
      expect(locales).to.be.deep.eq([]);
    });
  });
});
