import { readFile, access } from "fs/promises";
import path from "path";

import matter from "gray-matter";
import { Client } from "ldapts";
import { serialize } from "next-mdx-remote/serialize";

import { getCurrentTerm } from "@/lib/events";
import { capitalize } from "@/utils";

const EXECS_PATH = path.join("content", "team", "execs");
const FILETYPE = ".md";
const { year, term } = getCurrentTerm();

const execPositions: { [position: string]: string } = {
  president: "President",
  "vice-president": "Vice President",
  secretary: "Assistant Vice President",
  treasurer: "Treasurer",
  sysadmin: "System Administrator",
};

const orderedExecPositions: string[] = [
  "president",
  "vice-president",
  "secretary",
  "treasurer",
  "sysadmin",
];

export interface Metadata {
  name: string;
  role: string;
  image: string;
}

export async function getExecNamePosPairs() {
  if (process.env.USE_LDAP?.toLowerCase() !== "true") {
    return [["codey", "mascot"]];
  }

  const url = "ldap://ldap1.csclub.uwaterloo.ca";
  const searchDN = "ou=People,dc=csclub,dc=uwaterloo,dc=ca";
  const client = new Client({ url });

  // position: name
  const execMembers: { [position: string]: string } = {};
  let formattedExec: [string, string][] = [];

  try {
    await client.bind("", "");
    const { searchEntries } = await client.search(searchDN, {
      scope: "sub",
      filter: `(&(objectClass=member)(term=${(term as string).slice(
        0,
        1
      )}${year}))`,
    });

    // item.position might be an array if the member has more than one position
    searchEntries.forEach((item) => {
      if (typeof item.position === "string" && item.position in execPositions) {
        execMembers[item.position] = item.cn as string;
      } else if (item.position instanceof Array) {
        item.position.forEach((p) => {
          if ((p as string) in execPositions) {
            execMembers[p as string] = item.cn as string;
          }
        });
      }
    });

    formattedExec = orderedExecPositions.map((position) => {
      return [
        `${execMembers[position].split(" ")[0].toLowerCase()}-${execMembers[
          position
        ]
          .split(" ")[1]
          .toLowerCase()}`,
        position,
      ];
    });

    formattedExec = [...formattedExec, ["codey", "mascot"]];
  } finally {
    await client.unbind();
  }

  return formattedExec;
}

export async function getExec(name: string, pos: string, convert = true) {
  let content, metadata;

  try {
    const raw = await readFile(path.join(EXECS_PATH, `${name}${FILETYPE}`));
    ({ content, data: metadata } = matter(raw));

    const image = await getMemberImagePath(metadata.name);

    return {
      content: convert ? await serialize(content) : content,
      metadata: { ...metadata, image } as Metadata,
    };
  } catch (err) {
    // Capitalize the first letter of the first name and last name
    const firstName = capitalize(name.split("-")[0]);
    const lastName = capitalize(name.split("-")[1]);

    const posName = execPositions[pos];
    content = "Coming Soon!";
    metadata = {
      name: `${firstName} ${lastName}`,
      role: `${posName}`,
    };

    const image = await getMemberImagePath(metadata.name);

    return {
      content: convert ? await serialize(content) : content,
      metadata: { ...metadata, image } as Metadata,
    };
  }
}

async function getImage(imgPath: string) {
  try {
    await access(path.join("public", imgPath));
    return imgPath;
  } catch {
    return undefined;
  }
}

export async function getMemberImagePath(name: string) {
  const imgPath = path.join("images", "team", name.replace(" ", ""));
  const placeholder = path.join(
    "images",
    "team",
    "team-member-placeholder.svg"
  );
  const img =
    (await getImage(imgPath + ".jpg")) ??
    (await getImage(imgPath + ".png")) ??
    (await getImage(imgPath + ".gif")) ??
    (await getImage(imgPath + ".jpeg")) ??
    (await getImage(imgPath + ".JPG")) ??
    (await getImage(imgPath + ".PNG")) ??
    (await getImage(imgPath + ".GIF")) ??
    (await getImage(imgPath + ".JPEG")) ??
    placeholder;
  return img;
}
