import * as path from 'path'
import * as fs from 'fs-extra'

import { Config } from '../../../config/argv'
import * as paths from '../../../config/paths'
import { createWatcher } from '../../../states/config'
import { ServerMachineCtx as Context } from '../context'

/**
 * Maps a given relative 'filepath' from 'themesDir/...' to 'src/...'
 */
const replaceThemesDir = (filepath: string, args: Config) => {
  // Make the path to a given relative`filepath` relative to themesDir:
  const rawFilePath = path.relative(
    paths.getThemesDir(args),
    path.resolve(paths.root, filepath)
  )
  // => e.g. '/gatsby-theme-docz/**/index.tsx'

  // Prefix with 'src':
  return path.join('src', rawFilePath)
  // => 'src/gatsby-theme-docz/**/index.tsx'
}

const watchGatsbyThemeFiles = (args: Config) => {
  const watcher = createWatcher(
    path.join(args.themesDir, 'gatsby-theme-**/**/*'),
    args
  )
  const copy = (filepath: string) => {
    const src = path.resolve(paths.root, filepath)
    const dest = path.resolve(paths.docz, replaceThemesDir(filepath, args))
    fs.copySync(src, dest)
  }
  const remove = (filepath: string) => {
    fs.removeSync(path.resolve(paths.docz, filepath))
  }

  watcher
    .on('add', copy)
    .on('addDir', copy)
    .on('change', copy)
    .on('unlink', remove)
    .on('unlinkDir', remove)

  return () => watcher.close()
}

const createWatch = (args: Config) => (
  glob: any,
  src: string,
  custom?: boolean
) => {
  const watcher = createWatcher(glob, args)
  const srcPath = path.join(paths.root, src)
  const destPath = path.join(
    paths.docz,
    custom ? src.replace('.js', '.custom.js') : src
  )

  const copyFile = () => fs.copySync(srcPath, destPath)
  const deleteFile = () => fs.removeSync(destPath)

  watcher
    .on('add', copyFile)
    .on('change', copyFile)
    .on('unlink', deleteFile)

  return () => watcher.close()
}

const watchDoczRc = (args: Context['args']) => {
  const watcher = createWatcher(
    path.join(paths.root, args.config ? args.config : 'doczrc.js'),
    args
  )

  const copy = (filepath: string) => {
    const src = path.resolve(paths.root, filepath)
    const dest = path.resolve(paths.docz, 'doczrc.js')
    fs.copySync(src, dest)
  }

  const remove = () => {
    fs.removeSync(path.resolve(paths.docz, 'doczrc.js'))
  }

  watcher
    .on('add', copy)
    .on('change', copy)
    .on('unlink', remove)
  return () => watcher.close()
}

export const watchFiles = ({ args }: Context) => () => {
  const watch = createWatch(args)
  const doczrc = watchDoczRc(args)
  const gatsbyBrowser = watch(paths.gatsbyBrowser, 'gatsby-browser.js')
  const gatsbyNode = watch(paths.gatsbyNode, 'gatsby-node.js')
  const gatsbySSR = watch(paths.gatsbySSR, 'gatsby-ssr.js')
  const gatsbyConfig = watch(paths.gatsbyConfig, 'gatsby-config.js', true)
  const themeFilesWatcher = watchGatsbyThemeFiles(args)

  return () => {
    doczrc()
    gatsbyConfig()
    gatsbyBrowser()
    gatsbyNode()
    gatsbySSR()
    themeFilesWatcher()
  }
}
