import fs from 'fs'
import path from 'path'

import matter from 'gray-matter'

import remarkGfm from 'remark-gfm'
import rehypeHighlight from 'rehype-highlight'
import rehypeSlug from 'rehype-slug'
import remarkToc from 'remark-toc'

import nix from 'highlight.js/lib/languages/nix'

const fsPromises = fs.promises
const postsDirectory = path.join(process.cwd(), 'posts')

export const mdxOptions = {
  remarkPlugins: [
    remarkGfm,
    [remarkToc, {tight: true}],
  ],
  rehypePlugins: [
    [rehypeHighlight, {languages: {nix}}],
    rehypeSlug,
  ],
};

export async function getPostSlugs() {
  return (await fsPromises.readdir(postsDirectory)).filter(fileName => fileName.match(/\.md$/)).map(fileName => {
    return fileName.replace(/\.md$/, '')
  })
}

function mdxify(serialize) {
  return async function mdxifyInner(post) {
    if (post.excerptRaw) {
      post.excerptMdx = await serialize(post.excerptRaw, { mdxOptions })
      delete post.excerptRaw
    }
    if (post.contentRaw) {
      post.contentMdx = await serialize(post.contentRaw, { mdxOptions })
      delete post.contentRaw
    }
    return post
  }
}

export async function _getPostBySlug(slug) {
  const fileName = `${slug}.md`
  const fullPath = path.join(postsDirectory, fileName)
  const fileContents = await fsPromises.readFile(fullPath, 'utf8')
  const matterResult = matter(fileContents, { excerpt: true })

  return {
    ...matterResult.data,
    slug,
    date: matterResult.data.date.toISOString().substring(0, 10),
    excerptRaw: matterResult.excerpt,
    contentRaw: matterResult.content,
    sortKey: matterResult.data.date,
  }
}

export async function getPostBySlug(slug, serialize) {
  const data = await _getPostBySlug(slug)
  delete data.sortKey
  delete data.excerptRaw
  return mdxify(serialize)(data)
}

export async function getSortedPostsData(serialize) {
  const mdxifyPost = mdxify(serialize)
  const allPostsData = await Promise.all((await getPostSlugs()).map(_getPostBySlug))

  return Promise.all(allPostsData.sort(({ sortKey: a }, { sortKey: b }) => {
    [a, b] = [Date.parse(a), Date.parse(b)]
    if (a < b) {
      return 1
    } else if (a > b) {
      return -1
    } else {
      return 0
    }
  }).map((x) => {
    delete x.sortKey
    delete x.contentRaw
    return x
  }).map(mdxifyPost))
}