import { DecodedJwt, createDecoder } from "fast-jwt";

const KC_URL = process.env.KC_URL;
const KC_REALM = process.env.KC_REALM;
const KC_CLIENT_ID = process.env.KC_SERVICE_ACCOUNT_CLIENT_ID;
const KC_SECRET = process.env.KC_SERVICE_ACCOUNT_SECRET;

let token: string | null = null;
let decoded: DecodedJwt | null = null;

const jwtDecode = createDecoder({ complete: true });

/**
 * Check if token is expired or will expire in 30 seconds
 * @returns true if expire or can't get exp, false otherwise
 */
export function isTokenExpired(token: string, beforeExpire: number = 30) {
  decoded = jwtDecode(token);

  if (decoded && decoded.payload.exp) {
    return Date.now() / 1000 >= decoded.payload.exp - beforeExpire;
  }

  return true;
}

/**
 * Get token from keycloak if needed
 */
export async function getToken() {
  if (!KC_CLIENT_ID || !KC_SECRET) {
    throw new Error("KC_CLIENT_ID and KC_SECRET are required to used this feature.");
  }

  if (token && !isTokenExpired(token)) return token;

  const body = new URLSearchParams();

  body.append("client_id", KC_CLIENT_ID);
  body.append("client_secret", KC_SECRET);
  body.append("grant_type", "client_credentials");

  const res = await fetch(`${KC_URL}/realms/${KC_REALM}/protocol/openid-connect/token`, {
    method: "POST",
    body: body,
  }).catch((e) => console.error(e));

  if (!res) {
    throw new Error("Cannot get token from keycloak.");
  }

  const data = (await res.json()) as any;

  if (data && data.access_token) {
    token = data.access_token;
  }

  return token;
}

/**
 * Create keycloak user by given username and password with roles
 *
 * Client must have permission to manage realm's user
 *
 * @returns user uuid or true if success, false otherwise.
 */
export async function createUser(username: string, password: string, opts?: Record<string, any>) {
  const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/users`, {
    // prettier-ignore
    headers: {
      "authorization": `Bearer ${await getToken()}`,
      "content-type": `application/json`,
    },
    method: "POST",
    body: JSON.stringify({
      enabled: true,
      credentials: [{ type: "password", value: password ,temporary: false,}],
      username,
      ...opts,
    }),
  }).catch((e) => console.log("Keycloak Error: ", e));

  if (!res) return false;
  if (!res.ok) {
    // return Boolean(console.error("Keycloak Error Response: ", await res.json()));
    return await res.json();
  }

  const path = res.headers.get("Location");
  const id = path?.split("/").at(-1);
  return id || true;
}

/**
 * Get keycloak user by uuid
 *
 * Client must have permission to manage realm's user
 *
 * @returns user if success, false otherwise.
 */
export async function getUser(userId: string) {
  const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/users/${userId}`, {
    // prettier-ignore
    headers: {
      "authorization": `Bearer ${await getToken()}`,
      "content-type": `application/json`,
    },
  }).catch((e) => console.log("Keycloak Error: ", e));
  if (!res) return false;
  if (!res.ok) {
    return Boolean(console.error("Keycloak Error Response: ", await res.json()));
  }
  return await res.json();
}

/**
 * Get keycloak user list
 *
 * Client must have permission to manage realm's user
 *
 * @returns user list if success, false otherwise.
 */
export async function getUserList(first = "", max = "", search = "") {
  const res = await fetch(
    // `${KC_URL}/admin/realms/${KC_REALM}/users`.concat(!!search ? `?search=${search}` : ""),
    `${KC_URL}/admin/realms/${KC_REALM}/users?first=${first || "0"}&max=${max || "-1"}${search ? `&search=${search}` : ""}`,
    {
      // prettier-ignore
      headers: {
      "authorization": `Bearer ${await getToken()}`,
      "content-type": `application/json`,
    },
    },
  ).catch((e) => console.log("Keycloak Error: ", e));

  if (!res) return false;
  if (!res.ok) {
    return Boolean(console.error("Keycloak Error Response: ", await res.json()));
  }

  return ((await res.json()) as any[]).map((v: Record<string, string>) => ({
    id: v.id,
    username: v.username,
    firstName: v.firstName,
    lastName: v.lastName,
    email: v.email,
    enabled: v.enabled,
  }));
}

export async function getUserCount(first = "", max = "", search = "") {
  const res = await fetch(
    `${KC_URL}/admin/realms/${KC_REALM}/users/count?first=${first || "0"}&max=${max || "-1"}${search ? `&search=${search}` : ""}`,
    {
      headers: {
        authorization: `Bearer ${await getToken()}`,
        "content-type": `application/json`,
      },
    },
  ).catch((e) => console.log("Keycloak Error: ", e));

  if (!res) return false;
  if (!res.ok) {
    return Boolean(console.error("Keycloak Error Response: ", await res.json()));
  }

  return await res.json();
}

/**
 * Get keycloak user list
 *
 * Client must have permission to manage realm's user
 *
 * @returns user list if success, false otherwise.
 */
export async function getUserListOrg(first = "", max = "", search = "", userIds: string[] = []) {
  const userIdsParam = userIds.join(",");
  const res = await fetch(
    // `${KC_URL}/admin/realms/${KC_REALM}/users`.concat(!!search ? `?search=${search}` : ""),
    `${KC_URL}/admin/realms/${KC_REALM}/users?first=${first || "0"}&max=${max || "-1"}${search ? `&search=${search}` : ""}${userIdsParam ? `&id=${userIdsParam}` : ""}`,
    {
      // prettier-ignore
      headers: {
      "authorization": `Bearer ${await getToken()}`,
      "content-type": `application/json`,
    },
    },
  ).catch((e) => console.log("Keycloak Error: ", e));

  if (!res) return false;
  if (!res.ok) {
    return Boolean(console.error("Keycloak Error Response: ", await res.json()));
  }

  return ((await res.json()) as any[]).map((v: Record<string, string>) => ({
    id: v.id,
    username: v.username,
    firstName: v.firstName,
    lastName: v.lastName,
    email: v.email,
    enabled: v.enabled,
  }));
}

export async function getUserCountOrg(first = "", max = "", search = "", userIds: string[] = []) {
  console.log(userIds);
  const userIdsParam = userIds.join(",");
  console.log(userIdsParam);
  console.log("xxxxxxxxxxxxxxxxx");
  console.log(
    `${KC_URL}/admin/realms/${KC_REALM}/users/count?first=${first || "0"}&max=${max || "-1"}${search ? `&search=${search}` : ""}${userIdsParam && userIdsParam != "" ? `&id=${userIdsParam}` : ""}`,
  );
  console.log("aaaaaaaaaaaaaaaaaa");
  const res = await fetch(
    `${KC_URL}/admin/realms/${KC_REALM}/users/count?first=${first || "0"}&max=${max || "-1"}${search ? `&search=${search}` : ""}${userIdsParam && userIdsParam != "" ? `&id=${userIdsParam}` : ""}`,
    {
      headers: {
        authorization: `Bearer ${await getToken()}`,
        "content-type": `application/json`,
      },
    },
  ).catch((e) => console.log("Keycloak Error: ", e));

  if (!res) return false;
  if (!res.ok) {
    return Boolean(console.error("Keycloak Error Response: ", await res.json()));
  }

  return await res.json();
}

/**
 * Update keycloak user by uuid
 *
 * Client must have permission to manage realm's user
 *
 * @returns user uuid or true if success, false otherwise.
 */
export async function editUser(userId: string, opts: Record<string, any>) {
  const { password, ...rest } = opts;

  const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/users/${userId}`, {
    // prettier-ignore
    headers: {
      "authorization": `Bearer ${await getToken()}`,
      "content-type": `application/json`,
    },
    method: "PUT",
    body: JSON.stringify({
      enabled: true,
      credentials: (password && [{ type: "password", value: opts?.password }]) || undefined,
      ...rest,
    }),
  }).catch((e) => console.log("Keycloak Error: ", e));

  if (!res) return false;
  if (!res.ok) {
    // return Boolean(console.error("Keycloak Error Response: ", await res.json()));
    return await res.json();
  }

  const path = res.headers.get("Location");
  const id = path?.split("/").at(-1);
  return id || true;
}
/**
 * Update keycloak user by uuid
 *
 * Client must have permission to manage realm's user
 *
 * @returns user uuid or true if success, false otherwise.
 */
export async function updateName(
  userId: string,
  firstName: string,
  lastName: string,
  // opts: Record<string, any>,
) {
  // const { password, ...rest } = opts;

  const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/users/${userId}`, {
    // prettier-ignore
    headers: {
      "authorization": `Bearer ${await getToken()}`,
      "content-type": `application/json`,
    },
    method: "PUT",
    body: JSON.stringify({
      enabled: true,
      // credentials: (password && [{ type: "password", value: opts?.password }]) || undefined,
      // ...rest,
      firstName,
      lastName,
    }),
  }).catch((e) => console.log("Keycloak Error: ", e));

  if (!res) return false;
  if (!res.ok) {
    // return Boolean(console.error("Keycloak Error Response: ", await res.json()));
    return await res.json();
  }

  const path = res.headers.get("Location");
  const id = path?.split("/").at(-1);
  return id || true;
}

/**
 * enable keycloak user by uuid
 *
 * Client must have permission to manage realm's user
 *
 * @returns user uuid or true if success, false otherwise.
 */
export async function enableStatus(userId: string, status: boolean) {
  const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/users/${userId}`, {
    headers: {
      authorization: `Bearer ${await getToken()}`,
      "content-type": `application/json`,
    },
    method: "PUT",
    body: JSON.stringify({
      enabled: status,
    }),
  }).catch((e) => console.log("Keycloak Error: ", e));

  if (!res) return false;
  if (!res.ok) {
    return await res.json();
  }

  const path = res.headers.get("Location");
  const id = path?.split("/").at(-1);
  return id || true;
}

/**
 * Delete keycloak user by uuid
 *
 * Client must have permission to manage realm's user
 *
 * @returns user true if success, false otherwise.
 */
export async function deleteUser(userId: string) {
  const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/users/${userId}`, {
    // prettier-ignore
    headers: {
      "authorization": `Bearer ${await getToken()}`,
      "content-type": `application/json`,
    },
    method: "DELETE",
  }).catch((e) => console.log("Keycloak Error: ", e));

  if (!res) return false;
  if (!res.ok) {
    return Boolean(console.error("Keycloak Error Response: ", await res.json()));
  }
  return true;
}

/**
 * Get keycloak user by uuid
 *
 * Client must have permission to manage realm's user
 *
 * @returns user if success, false otherwise.
 */
export async function getRoleMappings(userId: string) {
  const res = await fetch(
    `${KC_URL}/admin/realms/${KC_REALM}/users/${userId}/role-mappings/realm`,
    {
      headers: {
        authorization: `Bearer ${await getToken()}`,
        "content-type": `application/json`,
      },
    },
  ).catch((e) => console.log("Keycloak Error: ", e));
  if (!res) return false;
  if (!res.ok) {
    return Boolean(console.error("Keycloak Error Response: ", await res.json()));
  }

  return await res.json();
}

/**
 * Get roles list or specific role data
 *
 * Client must have permission to get realms roles
 *
 * @returns role's info (array if not specify name) if success, null if not found, false otherwise.
 */
export async function getRoles(name?: string) {
  const res = await fetch(
    `${KC_URL}/admin/realms/${KC_REALM}/roles`.concat((name && `/${name}`) || ""),
    {
      // prettier-ignore
      headers: {
        "authorization": `Bearer ${await getToken()}`,
      },
    },
  ).catch((e) => console.log(e));

  if (!res) return false;
  if (!res.ok && res.status !== 404) {
    return Boolean(console.error("Keycloak Error Response: ", await res.json()));
  }

  if (res.status === 404) {
    return null;
  }

  const data = (await res.json()) as any;

  if (Array.isArray(data)) {
    return data.map((v: Record<string, string>) => ({
      id: v.id,
      name: v.name,
      description: v.description,
    }));
  }

  // return {
  //   id: data.id,
  //   name: data.name,
  // };
}

/**
 * Get roles list of user
 *
 * Client must have permission to get realms roles
 *
 * @returns role's info (array if not specify name) if success, null if not found, false otherwise.
 */
export async function getUserRoles(userId: string) {
  const res = await fetch(
    `${KC_URL}/admin/realms/${KC_REALM}/users/${userId}/role-mappings/realm`,
    {
      // prettier-ignore
      headers: {
        "authorization": `Bearer ${await getToken()}`,
      },
    },
  ).catch((e) => console.log(e));

  if (!res) return false;
  if (!res.ok && res.status !== 404) {
    return Boolean(console.error("Keycloak Error Response: ", await res.json()));
  }

  if (res.status === 404) {
    return null;
  }

  const data = (await res.json()) as any;

  if (Array.isArray(data)) {
    return data.map((v: Record<string, string>) => ({ id: v.id, name: v.name }));
  }

  return {
    id: data.id,
    name: data.name,
  };
}

/**
 * Assign role to user
 *
 * Client must have permission to manage realm's user roles
 *
 * @returns true if success, false otherwise.
 */
export async function addUserRoles(userId: string, roles: { id: string; name: string }[]) {
  const res = await fetch(
    `${KC_URL}/admin/realms/${KC_REALM}/users/${userId}/role-mappings/realm`,
    {
      // prettier-ignore
      headers: {
        "authorization": `Bearer ${await getToken()}`,
        "content-type": `application/json`,
      },
      method: "POST",
      body: JSON.stringify(roles),
    },
  ).catch((e) => console.log(e));

  if (!res) return false;
  if (!res.ok) {
    return Boolean(console.error("Keycloak Error Response: ", await res.json()));
  }
  return true;
}

/**
 * Remove role from user
 *
 * Client must have permission to manage realm's user roles
 *
 * @returns true if success, false otherwise.
 */
export async function removeUserRoles(userId: string, roles: { id: string; name: string }[]) {
  const res = await fetch(
    `${KC_URL}/admin/realms/${KC_REALM}/users/${userId}/role-mappings/realm`,
    {
      // prettier-ignore
      headers: {
        "authorization": `Bearer ${await getToken()}`,
        "content-type": `application/json`,
      },
      method: "DELETE",
      body: JSON.stringify(roles),
    },
  ).catch((e) => console.log(e));

  if (!res) return false;
  if (!res.ok) {
    return Boolean(console.error("Keycloak Error Response: ", await res.json()));
  }
  return true;
}

/**
 * Get group list or specific group data
 *
 * Client must have permission to manage realms group
 *
 * @returns group's info (array if not specify name) if success, null if not found, false otherwise.
 */
export async function getGroups(id?: string) {
  const res = await fetch(
    `${KC_URL}/admin/realms/${KC_REALM}/groups`.concat((id && `/${id}`) || ""),
    {
      // prettier-ignore
      headers: {
        "authorization": `Bearer ${await getToken()}`,
      },
    },
  ).catch((e) => console.log(e));

  if (!res) return false;
  if (!res.ok && res.status !== 404) {
    return Boolean(console.error("Keycloak Error Response: ", await res.json()));
  }

  if (res.status === 404) {
    return null;
  }

  const data = (await res.json()) as any;

  if (Array.isArray(data)) {
    return data.map((v: Record<string, string>) => ({ id: v.id, name: v.name }));
  }

  return {
    id: data.id,
    name: data.name,
  };
}

/**
 * Create group
 *
 * Client must have permission to manage realms group
 *
 * @returns true if success, false otherwise.
 */
export async function createGroup(name: string) {
  const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/groups`, {
    // prettier-ignore
    headers: {
      "authorization": `Bearer ${await getToken()}`,
      "content-type": `application/json`,
    },
    method: "POST",
    body: JSON.stringify({ name }),
  }).catch((e) => console.log(e));

  if (!res) return false;
  if (!res.ok && res.status !== 404) {
    return Boolean(console.error("Keycloak Error Response: ", await res.json()));
  }

  return true;
}

/**
 * Edit group
 *
 * Client must have permission to manage realms group
 *
 * @returns true if success, false otherwise.
 */
export async function editGroup(id: string, name: string) {
  const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/groups/${id}`, {
    // prettier-ignore
    headers: {
      "authorization": `Bearer ${await getToken()}`,
      "content-type": `application/json`,
    },
    method: "PUT",
    body: JSON.stringify({ name }),
  }).catch((e) => console.log(e));

  if (!res) return false;
  if (!res.ok && res.status !== 404) {
    return Boolean(console.error("Keycloak Error Response: ", await res.json()));
  }

  return true;
}

/**
 * Delete group
 *
 * Client must have permission to manage realms group
 *
 * @returns true if success, false otherwise.
 */
export async function deleteGroup(id: string) {
  const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/groups/${id}`, {
    // prettier-ignore
    headers: {
      "authorization": `Bearer ${await getToken()}`,
    },
    method: "DELETE",
  }).catch((e) => console.log(e));

  if (!res) return false;
  if (!res.ok && res.status !== 404) {
    return Boolean(console.error("Keycloak Error Response: ", await res.json()));
  }

  return true;
}

/**
 * Get group list or specific group data
 *
 * Client must have permission to manage realms group
 *
 * @returns group's info (array if not specify name) if success, null if not found, false otherwise.
 */
export async function getUserGroups(userId?: string) {
  const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/users/${userId}/groups`, {
    // prettier-ignore
    headers: {
      "authorization": `Bearer ${await getToken()}`,
    },
  }).catch((e) => console.log(e));

  if (!res) return false;
  if (!res.ok && res.status !== 404) {
    return Boolean(console.error("Keycloak Error Response: ", await res.json()));
  }

  if (res.status === 404) {
    return null;
  }

  const data = (await res.json()) as any;

  if (Array.isArray(data)) {
    return data.map((v: Record<string, string>) => ({ id: v.id, name: v.name }));
  }

  return {
    id: data.id,
    name: data.name,
  };
}

/**
 * Add group to user
 *
 * Client must have permission to manage user group
 *
 * @returns true if success, false otherwise.
 */
export async function addUserGroup(userId: string, groupId: string) {
  const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/users/${userId}/groups/${groupId}`, {
    // prettier-ignore
    headers: {
      "authorization": `Bearer ${await getToken()}`,
    },
    method: "PUT",
  }).catch((e) => console.log(e));

  if (!res) return false;
  if (!res.ok && res.status !== 404) {
    return Boolean(console.error("Keycloak Error Response: ", await res.json()));
  }

  return true;
}

/**
 * Delete group from user
 *
 * Client must have permission to manage user group
 *
 * @returns true if success, false otherwise.
 */
export async function removeUserGroup(userId: string, groupId: string) {
  const res = await fetch(`${KC_URL}/admin/realms/${KC_REALM}/users/${userId}/groups/${groupId}`, {
    // prettier-ignore
    headers: {
      "authorization": `Bearer ${await getToken()}`,
    },
    method: "PUT",
  }).catch((e) => console.log(e));

  if (!res) return false;
  if (!res.ok && res.status !== 404) {
    return Boolean(console.error("Keycloak Error Response: ", await res.json()));
  }

  return true;
}
