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

const KC_URL = process.env.KC_URL;
const KC_REALMS = process.env.KC_REALMS;
const KC_CLIENT_ID = process.env.KC_SERVICE_ACCOUNT_CLIENT_ID;
const KC_SECRET = process.env.KC_SERVICE_ACCOUNT_SECRET;
const AUTH_ACCOUNT_SECRET = process.env.AUTH_ACCOUNT_SECRET;
const API_KEY = process.env.API_KEY;

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_REALMS}/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;
  }
  console.log(`token: ${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>,
  token?: string,
) {
  const res = await fetch(`${KC_URL}/admin/realms/${KC_REALMS}/users`, {
    // prettier-ignore
    headers: {
      "authorization": `Bearer ${token || 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, token?: string) {
  const res = await fetch(`${KC_URL}/admin/realms/${KC_REALMS}/users/${userId}`, {
    // prettier-ignore
    headers: {
      "authorization": `Bearer ${token || 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 by Username (citizenId)
 *
 * Client must have permission to manage realm's user
 *
 * @returns user if success, false otherwise.
 */
export async function getUserByUsername(citizenId: string, token?: string) {
  const res = await fetch(`${KC_URL}/admin/realms/${KC_REALMS}/users?username=${citizenId}`, {
    // prettier-ignore
    headers: {
      "authorization": `Bearer ${token || 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_REALMS}/users`.concat(!!search ? `?search=${search}` : ""),
    `${KC_URL}/admin/realms/${KC_REALMS}/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_REALMS}/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_REALMS}/users`.concat(!!search ? `?search=${search}` : ""),
    `${KC_URL}/admin/realms/${KC_REALMS}/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[] = []) {
  const userIdsParam = userIds.join(",");
  const res = await fetch(
    `${KC_URL}/admin/realms/${KC_REALMS}/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_REALMS}/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_REALMS}/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_REALMS}/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, token?: string) {
  const res = await fetch(`${KC_URL}/admin/realms/${KC_REALMS}/users/${userId}`, {
    // prettier-ignore
    headers: {
      "authorization": `Bearer ${token || 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_REALMS}/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, token?: string) {
  const res = await fetch(
    `${KC_URL}/admin/realms/${KC_REALMS}/roles`.concat((name && `/${name}`) || ""),
    {
      // prettier-ignore
      headers: {
        "authorization": `Bearer ${token || 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_REALMS}/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 }[],
  token?: string,
) {
  const res = await fetch(
    `${KC_URL}/admin/realms/${KC_REALMS}/users/${userId}/role-mappings/realm`,
    {
      // prettier-ignore
      headers: {
        "authorization": `Bearer ${token || 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_REALMS}/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_REALMS}/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_REALMS}/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_REALMS}/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_REALMS}/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_REALMS}/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_REALMS}/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_REALMS}/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;
}

// Function to change user password
export async function changeUserPassword(userId: string, newPassword: string) {
  try {
    const res = await fetch(`${KC_URL}/admin/realms/${KC_REALMS}/users/${userId}/reset-password`, {
      // prettier-ignore
      headers: {
          "authorization": `Bearer ${await getToken()}`,
          "content-type": `application/json`,
        },
      method: "PUT",
      body: JSON.stringify({
        type: "password",
        value: newPassword,
        temporary: false, // Set to true if the user must reset their password on next login
      }),
    }).catch((e) => console.log("Keycloak Error: ", e));

    return true;
  } catch (error) {
    console.error("Error changing password:", error);
    return false;
  }
}

// Function to reset password
export async function resetPassword(username: string) {
  try {
    // if (!API_KEY || !AUTH_ACCOUNT_SECRET) {
    //   throw new Error("KC_CLIENT_ID and KC_SECRET are required to used this feature.");
    // }
    // const body = new URLSearchParams();
    // body.append("client_id", "gettoken");
    // body.append("client_secret", AUTH_ACCOUNT_SECRET?.toString());
    // body.append("grant_type", "client_credentials");
    // const tokenResponse = await fetch(`${process.env.KC_URL}/realms/${process.env.KC_REALMS}/protocol/openid-connect/token`, {
    //   method: "POST",
    //   headers: {
    //     "Content-Type": "application/x-www-form-urlencoded",
    //     api_key: API_KEY,
    //   },
    //   body: body
    // });
    // if (!tokenResponse.ok) {
    //   throw new Error("Failed to get admin token");
    // }
    // const tokenData = await tokenResponse.json();
    // const adminToken = tokenData.access_token;

    const users = await fetch(
      `${KC_URL}/admin/realms/${KC_REALMS}/users?email=${encodeURIComponent(username)}`,
      {
        headers: {
          authorization: `Bearer ${await getToken()}`,
          // "authorization": `Bearer ${adminToken}`,
          "content-type": `application/json`,
        },
      },
    );
    if (!users.ok) {
      return false;
    }
    const usersData = await users.json();
    const userId = usersData[0].id;
    const resetResponse = await fetch(
      `${KC_URL}/admin/realms/${KC_REALMS}/users/${userId}/execute-actions-email`,
      {
        method: "PUT",
        headers: {
          Authorization: `Bearer ${await getToken()}`,
          // "Authorization": `Bearer ${adminToken}`,
          "Content-Type": "application/json",
        },
        body: JSON.stringify(["UPDATE_PASSWORD"]),
      },
    );
    if (!resetResponse.ok) {
      return false;
    }
    return { message: "Password reset email sent" };
  } catch (error) {
    console.error("Error triggering password reset:", error);
    return false;
  }
}
