import chokidar from 'chokidar'
import path from 'path'
import fs from 'fs-extra'
import _ from 'lodash'

import { monorepoPaths } from '../monorepoPaths'

const PROJECT_FIXTURE_DIRECTORY = 'system-tests/projects'

const DIR_PATH = path.join(monorepoPaths.root, PROJECT_FIXTURE_DIRECTORY)
const OUTPUT_PATH = path.join(monorepoPaths.toolingDir, 'lib/fixtureDirs.ts')

export async function e2eTestScaffold () {
  return _e2eTestScaffold(true)
}

export async function _e2eTestScaffold (cleanupEmpty = true) {
  const possibleDirectories = await fs.readdir(DIR_PATH)
  const dirs = await Promise.all(possibleDirectories.map(async (dir) => {
    const fullPath = path.join(DIR_PATH, dir)
    const stat = await fs.stat(fullPath)

    if (stat.isDirectory()) {
      if (await hasVisibleFileRecursive(fullPath)) {
        return fullPath
      }

      if (cleanupEmpty) {
        await fs.remove(fullPath)
      }

      return null
    }
  }))
  const allDirs = dirs.filter((dir) => dir) as string[]

  await fs.writeFile(
    OUTPUT_PATH,
`/* eslint-disable */
// Auto-generated by ${path.basename(__filename)} (run yarn gulp e2eTestScaffold)
export const fixtureDirs = [
${allDirs
.map((dir) => `  '${path.basename(dir)}'`).join(',\n')}
] as const

export type ProjectFixtureDir = typeof fixtureDirs[number]
`,
  )

  return allDirs
}

export async function e2eTestScaffoldWatch () {
  const fixtureWatcher = chokidar.watch(PROJECT_FIXTURE_DIRECTORY, {
    cwd: monorepoPaths.root,
    // ignoreInitial: true,
    depth: 0,
  })

  fixtureWatcher.on('unlinkDir', () => {
    e2eTestScaffold()
  })

  fixtureWatcher.on('addDir', _.debounce(() => {
    _e2eTestScaffold(false)
  }))

  await e2eTestScaffold()
}

/**
 * From the basePath, checks for a valid file within that directory. Used to ignore empty
 * system tests directories left over from switching branches
 */
async function hasVisibleFileRecursive (basePath: string): Promise<boolean> {
  if (basePath.endsWith('node_modules')) {
    return false
  }

  const files = await fs.readdir(basePath)

  const toCheck = await Promise.all(files.filter((f) => !f.startsWith('.')).map(async (f) => {
    try {
      return {
        file: f,
        stat: await fs.stat(path.join(basePath, f)),
      }
    } catch {
      return null
    }
  }))

  if (toCheck.some((f) => f?.stat.isFile())) {
    return true
  }

  const directories = toCheck.filter((f) => f?.stat.isDirectory())

  if (directories.length) {
    for (const dir of directories) {
      if (dir?.file && await hasVisibleFileRecursive(path.join(basePath, dir?.file))) {
        return true
      }
    }
  }

  return false
}
