import fs from "fs/promises";
import path from "path";

import { parse } from "date-fns";
import matter from "gray-matter";
import truncateMarkdown from "markdown-truncate";
import { MDXRemoteSerializeResult } from "next-mdx-remote";
import { serialize } from "next-mdx-remote/serialize";

import { isTerm, Term, TERMS } from "@/utils";

import { DATE_FORMAT, getLocalDateFromEST } from "../utils";

export const NEWS_PATH = path.join("content", "news");

export interface Metadata {
  author: string;
  date: string;
  permalink: string;
}

export interface News {
  content: MDXRemoteSerializeResult;
  metadata: Metadata;
}

export async function getNewsYears(): Promise<string[]> {
  return (await fs.readdir(NEWS_PATH, { withFileTypes: true }))
    .filter((dirent) => dirent.isDirectory())
    .map((dirent) => dirent.name)
    .sort();
}

export async function getNewsTermsByYear(year: string): Promise<Term[]> {
  return (
    await fs.readdir(path.join(NEWS_PATH, year), {
      withFileTypes: true,
    })
  )
    .filter((dirent) => dirent.isDirectory() && isTerm(dirent.name))
    .map((dirent) => dirent.name as Term)
    .sort((a, b) => TERMS.indexOf(a) - TERMS.indexOf(b));
}

export async function getNewsDateByTerm(
  year: string,
  term: Term
): Promise<string[]> {
  return (await getNewsByTerm(year, term)).map(
    (news) => news.split("-").slice(0, 3).join("-") // retrieves date from filename
  );
}

export async function getNewsByTerm(
  year: string,
  term: Term
): Promise<string[]> {
  return (
    await fs.readdir(path.join(NEWS_PATH, year, term), {
      withFileTypes: true,
    })
  )
    .filter((dirent) => dirent.isFile() && dirent.name.endsWith(".md"))
    .map((dirent) => dirent.name.slice(0, -".md".length));
}

export async function getNewsBySlug(
  year: string,
  term: Term,
  slug: string,
  shortened = false
): Promise<News> {
  const raw = await fs.readFile(
    path.join(NEWS_PATH, year, term, `${slug}.md`),
    "utf-8"
  );
  const { content: rawContent, data: metadata } = matter(raw);
  const slugDate = slug.split("-").slice(0, 3).join("-");
  const content: string = shortened
    ? truncateMarkdown(rawContent, {
        limit: 150,
        ellipsis: true,
      })
    : rawContent;

  return {
    content: await serialize(content),
    metadata: {
      ...metadata,
      date: getLocalDateFromEST(
        parse(metadata.date, DATE_FORMAT, new Date())
      ).toString(),
      permalink: `/news/${year}/${term}/${slugDate}`,
    } as Metadata,
  };
}

export async function getRecentNews(): Promise<News[]> {
  const today = new Date();
  const currentYear = today.getFullYear();
  const currentTerm = Math.trunc(today.getMonth() / 4);
  const prevYear = currentTerm > 0 ? currentYear : currentYear - 1;
  const prevTerm = (currentTerm - 1 + 3) % 3;

  const news: News[] = (
    await Promise.all(
      [
        { year: currentYear.toString(), term: currentTerm },
        { year: prevYear.toString(), term: prevTerm },
      ].map(async ({ year, term }) => {
        try {
          const newsInTerm = await getNewsByTerm(year, TERMS[term]);
          return await Promise.all(
            newsInTerm.map((slug) => {
              return getNewsBySlug(year, TERMS[term], slug, true);
            })
          );
        } catch (error) {
          return [];
        }
      })
    )
  ).flat();

  return news.sort((a, b) => {
    return (
      new Date(b.metadata.date).getTime() - new Date(a.metadata.date).getTime()
    );
  });
}
