import {
  doModifyAvatar as dbModifyAvatar,
  doModifyAvatarIntro as dbModifyAvatarIntro,
  getAvatar,
  getLatestEnrolledAvatars as dbGetLatestEnrolledAvatars,
  getLatestLoggedInAvatars as dbGetLatestLoggedInAvatars,
  postAvatar as dbPostAvatar,
  setLastDate,
} from "@/app/www/system/DB";
import { post, wipe } from "@/app/www/system/Totem";
import { AVATAR_ENTRY_PATH } from "@/app/www/utilities/Path";
import { equalCipher } from "@/app/www/utilities/Utility";
import { pbkdf2, randomBytes } from "crypto";
import { readFile } from "fs/promises";
import { join } from "path";
import { getDatetime } from "taehui-lib/date";
import { promisify } from "util";

const pw = promisify(pbkdf2);

export const getTotem = async (
  pendingAvatarID: string,
  pendingAvatarCipher: string,
) => {
  const data = await getAvatar(pendingAvatarID);
  if (data) {
    const { avatarID, avatarCipher, avatarName, level, fax, avatarIntro } =
      data;
    if (await equalCipher(avatarCipher, pendingAvatarCipher)) {
      await setLastDate(avatarID, getDatetime());
      return {
        totem: await post(avatarID, getDatetime()),
        avatarID,
        avatarName,
        level,
        fax,
        avatarIntro,
      };
    }
  }
};

const getDBCipher = async (avatarCipher: string) => {
  const salt = randomBytes(24).toString("base64");

  return `sha256:12000:${salt}:${Buffer.from(
    await pw(avatarCipher, salt, 12000, 24, "sha256"),
  ).toString("base64")}`;
};

export const doModifyAvatar = async (
  totem: string,
  avatarID: string,
  avatarCipher: string,
  avatarCipherModified: string,
  avatarName: string,
  fax: string,
  avatarIntro: string,
) => {
  if (
    !!avatarCipher &&
    !!avatarName &&
    !avatarName.startsWith("@") &&
    avatarName.length <= 16 &&
    (!fax || /^.+@.+$/.test(fax)) &&
    (await dbModifyAvatar(
      avatarID,
      avatarCipher,
      avatarCipherModified && (await getDBCipher(avatarCipherModified)),
      avatarName,
      fax,
      avatarIntro,
    ))
  ) {
    if (avatarCipherModified) {
      await wipe(totem);
      try {
        await fetch("http://localhost:8300/totem", {
          method: "PATCH",
          body: avatarID,
        });
      } catch {}
    }
    return true;
  } else {
    return false;
  }
};

export const doModifyAvatarIntro = async (
  avatarID: string,
  avatarIntro: string,
) => {
  return dbModifyAvatarIntro(avatarID, avatarIntro);
};

export const postAvatar = async (
  avatarID: string,
  avatarCipher: string,
  avatarName: string,
  fax: string,
) => {
  return (
    /[a-zA-Z\d]/.test(avatarID) &&
    !!avatarCipher &&
    !!avatarName &&
    !avatarName.startsWith("@") &&
    avatarName.length <= 16 &&
    (!fax || /^.+@.+$/.test(fax)) &&
    !(await getAvatar(avatarID)) &&
    (await dbPostAvatar(
      avatarID,
      await getDBCipher(avatarCipher),
      avatarName,
      fax,
      getDatetime(),
    ))
  );
};

export const wipeTotem = async (avatarID: string, totem: string) => {
  return Promise.all([setLastDate(avatarID, getDatetime()), wipe(totem)]);
};

export const getAvatarDrawing = async (avatarID: string) => {
  try {
    return await readFile(join(AVATAR_ENTRY_PATH, `${avatarID}.png`));
  } catch (e: any) {
    if (e.code === "ENOENT") {
      return null;
    }

    throw e;
  }
};

export const getLatestLoggedInAvatars = async () => {
  return dbGetLatestLoggedInAvatars();
};

export const getLatestEnrolledAvatars = async () => {
  return dbGetLatestEnrolledAvatars();
};

export const doInvalidateDrawing = async (avatarID: string) => {
  try {
    await fetch("http://localhost:8300/drawing", {
      method: "PATCH",
      body: avatarID,
    });
  } catch {}
};
