const { dirname, join, relative, resolve } = require('path')
const R = require('ramda')
const {
  MarkdownUtil,
  FileUtil: { exists, glob, requireUncached },
  PageUtil: { PAGE_CONFNAME, PAGE_DOCSNAME, pageFilePathToId, pageIdToFilePath, pageIdToPath, pageIdToTitle, determineType, convertUserProvidedChildrenList, convertUserProvidedComponentsList },
  StringUtil: { crossPlatformPath, titleFromContentHeading },
  DebugUtil: { debug2, debug3, debug4 }
} = require('@uiengine/util')

async function readPageFiles (state, id) {
  debug4(state, `Page.readPageFiles(${id}):start`)

  const { base, pages } = state.config.source

  const configPath = pageIdToFilePath(pages, id)
  const dir = dirname(configPath)
  const readmePath = join(dir, PAGE_DOCSNAME)
  const sourcePath = crossPlatformPath(relative(base, dir))
  const data = { attributes: {}, sourcePath }

  // config
  if (exists(configPath)) {
    data.attributes = requireUncached(configPath)
    data.sourceFile = crossPlatformPath(relative(base, configPath))
  }

  // readme
  if (exists(readmePath)) {
    data.content = await MarkdownUtil.fromFile(readmePath)
    data.readmeFile = crossPlatformPath(relative(base, readmePath))
  }

  debug4(state, `Page.readPageFiles(${id}):end`)

  return data
}

async function findPageFiles (pagesPath, pagePath, childIds = []) {
  // see the glob option described here for details:
  // https://github.com/mrmlnc/fast-glob#how-to-exclude-directory-from-reading
  const filesPattern = crossPlatformPath(join(pagesPath, pagePath, '**'))
  const pageExcludes = [crossPlatformPath(join('**', '_**', '**')), crossPlatformPath(join('**', '_*')), crossPlatformPath(join('**', `{${PAGE_CONFNAME},${PAGE_DOCSNAME}}`))]
  const childExcludes = R.map(id => crossPlatformPath(join('**', id, '**')), childIds)
  const ignore = R.concat(pageExcludes, childExcludes)
  const filePaths = await glob(filesPattern, { ignore })

  return filePaths
}

async function findPageIds (state, pagePath = '**') {
  const { pages } = state.config.source
  if (!pages) return []

  const pattern = crossPlatformPath(resolve(pages, pagePath, `{${PAGE_CONFNAME},${PAGE_DOCSNAME}}`))
  const pagePaths = await glob(pattern)
  const pageIdFromPageFilePath = R.partial(pageFilePathToId, [pages])
  const pageIds = R.uniq(R.map(pageIdFromPageFilePath, pagePaths))

  return pageIds
}

async function fetchAll (state) {
  debug2(state, 'Page.fetchAll():start')

  const pageIds = await findPageIds(state)

  const pageFetch = R.partial(fetchById, [state])
  const pageFetches = R.map(pageFetch, pageIds)
  const pageList = await Promise.all(pageFetches)

  const pages = R.reduce((pages, page) => R.assoc(page.id, page, pages), {}, pageList)

  debug2(state, 'Page.fetchAll():end')

  return pages
}

async function fetchById (state, id) {
  debug3(state, `Page.fetchById(${id}):start`)

  const { pages } = state.config.source
  const pagePath = pageIdToPath(id)
  const childPattern = join(pagePath, '*')
  const fetchChildIds = findPageIds(state, childPattern)
  const fetchPageData = readPageFiles(state, id)

  // fetch childPageIds before fetching files to exclude
  // the children directories when looking for files
  const [pageData, childIds] = await Promise.all([fetchPageData, fetchChildIds])
  const files = await findPageFiles(pages, pagePath, childIds)

  let { attributes, content, sourcePath, sourceFile, readmeFile } = pageData

  let title = attributes.title
  let isTitleFromHeading
  if (!title) {
    const titleFromHeading = titleFromContentHeading(content)
    title = titleFromHeading || pageIdToTitle(id)
    isTitleFromHeading = !!titleFromHeading
  }

  const type = determineType(attributes)
  attributes = convertUserProvidedChildrenList(id, childIds, attributes)
  attributes = convertUserProvidedComponentsList(id, attributes)
  const baseData = { childIds }
  const fixData = { id, title, isTitleFromHeading, path: pagePath, sourcePath, sourceFile, readmeFile, type, content, files }
  const data = R.mergeAll([baseData, attributes, fixData])

  if (data.files.length === 0) delete data.files

  debug3(state, `Page.fetchById(${id}):end`)

  return data
}

module.exports = {
  fetchAll,
  fetchById
}
