// -----------------------------------------------------
//                This code is LOCKED
//
// If any changes are needed to this code, or if this code
// is needed in any other projects, instead of changing or
// using it, instead we must complete this task as a prerequisite
//
// https://app.asana.com/0/1100423001970639/1195001770724993
//
// ------------------------------------------------------

import fs from 'fs'
import path from 'path'
import existsSync from 'fs-exists-sync'
import readdirp from 'readdirp'
import lineReader from 'line-reader'
import matter from 'gray-matter'
import { safeLoad } from 'js-yaml'
import renderToString from 'next-mdx-remote/render-to-string'
import markdownDefaults from '@hashicorp/nextjs-scripts/markdown'
import generateComponents from './components'

export async function generateStaticPaths(subpath) {
  const paths = await getStaticMdxPaths(
    path.join(process.cwd(), 'content', subpath)
  )

  return { paths, fallback: false }
}

export async function generateStaticProps(subpath, productName, params) {
  const docsPath = path.join(process.cwd(), 'content', subpath)
  const pagePath = params.page ? params.page.join('/') : '/'

  // get frontmatter from all other pages in the category, for the sidebar
  const allFrontMatter = await fastReadFrontMatter(docsPath)

  // render the current page path markdown
  const { mdxSource, frontMatter, filePath } = await renderPageMdx(
    docsPath,
    pagePath,
    generateComponents(productName)
  )

  return {
    props: {
      data: allFrontMatter.map((p) => {
        p.__resourcePath = `docs/${p.__resourcePath}`
        return p
      }),
      mdxSource,
      frontMatter,
      filePath,
      pagePath: `/docs/${pagePath}`,
    },
  }
}

export async function getStaticMdxPaths(root) {
  const files = await readdirp.promise(root, { fileFilter: ['*.mdx'] })

  return files.map(({ path: p }) => {
    return {
      params: {
        page: p
          .replace(/\.mdx$/, '')
          .split('/')
          .filter((p) => p !== 'index'),
      },
    }
  })
}

export async function renderPageMdx(root, pagePath, components) {
  // get the page being requested - figure out if its index page or leaf
  // prefer leaf if both are present
  const leafPath = path.join(root, `${pagePath}.mdx`)
  const indexPath = path.join(root, `${pagePath}/index.mdx`)
  let page, filePath

  if (existsSync(leafPath)) {
    page = fs.readFileSync(leafPath, 'utf8')
    filePath = leafPath
  } else if (existsSync(indexPath)) {
    page = fs.readFileSync(indexPath, 'utf8')
    filePath = indexPath
  } else {
    // NOTE: if we decide to let docs pages render dynamically, we should replace this
    // error with a straight 404, at least in production.
    throw new Error(
      `We went looking for "${leafPath}" and "${indexPath}" but neither one was found.`
    )
  }

  const { data: frontMatter, content } = matter(page)
  const mdxSource = await renderToString(content, {
    mdxOptions: markdownDefaults({
      resolveIncludes: path.join(process.cwd(), 'content/partials'),
    }),
    components,
  })

  return { mdxSource, frontMatter, filePath: filePath.replace(`${root}/`, '') }
}

export function fastReadFrontMatter(p) {
  return new Promise((resolve) => {
    const fm = []
    readdirp(p, { fileFilter: '*.mdx' })
      .on('data', (entry) => {
        let lineNum = 0
        const content = []
        fm.push(
          new Promise((resolve2, reject2) => {
            lineReader.eachLine(
              entry.fullPath,
              (line) => {
                // if it has any content other than `---`, the file doesn't have front matter, so we close
                if (lineNum === 0 && !line.match(/^---$/)) return false
                // if it's not the first line and we have a bottom delimiter, exit
                if (lineNum !== 0 && line.match(/^---$/)) return false
                // now we read lines until we match the bottom delimiters
                content.push(line)
                // increment line number
                lineNum++
              },
              (err) => {
                if (err) return reject2(err)
                content.push(`__resourcePath: "${entry.path}"`)
                resolve2(safeLoad(content.slice(1).join('\n')), {
                  filename: entry.fullPath,
                })
              }
            )
          })
        )
      })
      .on('end', () => {
        Promise.all(fm).then((res) => resolve(res))
      })
  })
}
