const chokidar = require('chokidar')
const bodyParser = require('body-parser')
const chalk = require('chalk')
const path = require('path')
const fs = require('fs')
const Mock = require('mockjs')

const responseFake = (url, type, respond) => {
  return {
    url: new RegExp(`/mock${url}`),
    type: type || 'get',
    async response(req, res, next) {
      res.json(Mock.mock(respond instanceof Function ? await respond(req, res, next) : respond))
    }
  }
}

function readFileList(dir, filesFileName = null) {
  let filesList = []
  fs.readdirSync(dir).filter(file => {
    let fullPath = path.join(dir, file)
    if (fs.statSync(fullPath).isDirectory()) {
      filesList = filesList.concat(readFileList(path.join(dir, file), filesFileName))
    } else {
      if (filesFileName && file === filesFileName) {
        filesList = filesList.concat(require(fullPath).default)
        return true
      }
      if (!filesFileName) {
        filesList = filesList.concat(require(fullPath).default)
        return true
      }
      return false
    }
  })
  return filesList;
}

const mockDir = path.join(process.cwd(), 'src/mock/mockFiles')
const appDir = path.join(process.cwd(), 'src/apps')
function registerRoutes(app) {
  let mockLastIndex
  let mocks = readFileList(appDir, 'mock.js')
  mocks = mocks.concat(readFileList(mockDir))
  let mocksRoutes = mocks.map(route => {
    return responseFake(route.url, route.type, route.response)
  })
  for (const mock of mocksRoutes) {
    app[mock.type](mock.url, mock.response)
    mockLastIndex = app._router.stack.length
  }
  const mockRoutesLength = Object.keys(mocksRoutes).length
  return {
    mockRoutesLength: mockRoutesLength,
    mockStartIndex: mockLastIndex - mockRoutesLength
  }
}

function unregisterRoutes() {
  Object.keys(require.cache).forEach(i => {
    if (i.includes(mockDir) || i.includes(appDir)) {
      delete require.cache[require.resolve(i)]
    }
  })
}

module.exports = app => {
  // es6 polyfill
  require('@babel/register')({
    presets: ['@babel/preset-env', 'react-app']
  })
  // parse app.body
  // https://expressjs.com/en/4x/api.html#req.body
  app.use(bodyParser.json())
  app.use(bodyParser.urlencoded({
    extended: true
  }))

  const mockRoutes = registerRoutes(app)
  var mockRoutesLength = mockRoutes.mockRoutesLength
  var mockStartIndex = mockRoutes.mockStartIndex

  // watch files, hot reload mock server
  chokidar.watch(appDir, {
    ignored: /[^mock]\.js$|\.jsx$/,
    ignoreInitial: true
  }).on('all', onEvent)

  chokidar.watch(mockDir, {
    ignoreInitial: true
  }).on('all', onEvent)

  function onEvent(event, path) {
    if (event === 'change' || event === 'add') {
      try {
        // remove mock routes stack
        app._router.stack.splice(mockStartIndex, mockRoutesLength)

        // clear routes cache
        unregisterRoutes()

        const mockRoutes = registerRoutes(app)
        mockRoutesLength = mockRoutes.mockRoutesLength
        mockStartIndex = mockRoutes.mockStartIndex

        console.log(chalk.magentaBright(`\n > Mock Server 重载成功! \n > 已修改：${path}`))
      } catch (error) {
        console.log(chalk.redBright(error))
      }
    }
  }
}
