import fs from "node:fs/promises";
import { GLOBAL } from "./variables";

type MarkdownData<T extends object> = {
  frontmatter: T;
  file: string;
  url: string;
};


/**
 * This function processes the content of a directory and returns an array of processed content.
 * It takes a content type, a function to process the content, and an optional directory.
 * If no directory is provided, it defaults to the current working directory.
 * 
 * @param contentType the type of content to process
 * @param processFn the function to process the content
 * @param dir the directory to process the content from
 * @returns a promise that resolves to an array of processed content
 */
export const processContentInDir = async <T extends object, K>(
  contentType: "projects" | "blog",
  processFn: (data: MarkdownData<T>) => K,
  dir: string = process.cwd(),
) => {
  const files = await fs.readdir(dir + `/src/pages/${contentType}`);
  const markdownFiles = files
    .filter((file: string) => file.endsWith(".md"))
    .map((file) => file.split(".")[0]);
  const readMdFileContent = async (file: string) => {
    try {
      if (contentType === "projects") {
        const content = import.meta
          .glob(`/src/pages/projects/*.md`)
          [`/src/pages/projects/${file}.md`]();
        const data = (await content) as {
          frontmatter: T;
          file: string;
          url: string;
        };
        
        // Validate frontmatter before processing
        if (!data || !data.frontmatter) {
          console.warn(`Warning: Missing or invalid frontmatter in ${file}.md`);
          return null;
        }
        
        return processFn(data);
      } else {
        const content = import.meta
          .glob(`/src/pages/blog/*.md`)
          [`/src/pages/blog/${file}.md`]();
        const data = (await content) as {
          frontmatter: T;
          file: string;
          url: string;
        };
        
        // Validate frontmatter before processing
        if (!data || !data.frontmatter) {
          console.warn(`Warning: Missing or invalid frontmatter in ${file}.md`);
          return null;
        }
        
        return processFn(data);
      }
    } catch (error) {
      console.error(`Error processing ${file}.md:`, error);
      return null;
    }
  };
  const results = await Promise.all(markdownFiles.map(readMdFileContent));
  // Filter out null results from files with errors
  return results.filter(result => result !== null) as K[];
};

/**
 * Shortens a string by removing words at the end until it fits within a certain length.
 * @param content the content to shorten
 * @param maxLength the maximum length of the shortened content (default is 20)
 * @returns a shortened version of the content
 */
export const getShortDescription = (content: string, maxLength = 20) => {
  const splitByWord = content.split(" ");
  const length = splitByWord.length;
  return length > maxLength ? splitByWord.slice(0, maxLength).join(" ") + "..." : content;
};

/**
 * Processes the date of an article and returns a string representing the processed date.
 * @param timestamp the timestamp to process
 * @returns a string representing the processed timestamp
 */
export const processArticleDate = (timestamp: string) => {
  const date = new Date(timestamp);
  const monthSmall = date.toLocaleString("default", { month: "short" });
  const day = date.getDate();
  const year = date.getFullYear();
  return `${monthSmall} ${day}, ${year}`;
};

/**
 * Generates a source URL for a content item. The URL is used in meta tags and social media cards.
 * @param sourceUrl the source URL of the content
 * @param contentType the type of content (either "projects" or "blog")
 * @returns a string representing the source URL with the appropriate domain
 */
export const generateSourceUrl = (
  sourceUrl: string,
  contentType: "projects" | "blog",
) => {
  return `${GLOBAL.rootUrl}/${contentType}/${sourceUrl}`;
};

export const countTags = (tags: string[]) => {
  const tagCount: Record<string, number> = {};
  tags.forEach((tag) => {
    if (tagCount[tag]) {
      tagCount[tag]++;
    } else {
      tagCount[tag] = 1;
    }
  });
  return tagCount;
}
