import appRootPath from 'app-root-path'
import { spawn } from 'child_process'
import fs from 'fs'
import path from 'path'

import { getStorageProvider } from '../../media/storageprovider/storageprovider'
import { getFileKeysRecursive } from '../../media/storageprovider/storageProviderUtils'
import logger from '../../ServerLogger'
import { deleteFolderRecursive, writeFileSyncRecursive } from '../../util/fsHelperFunctions'

/**
 * Downloads a specific project to the local file system from the storage provider cache
 * Then runs `npm install --legacy-peer-deps` inside the project to install it's dependencies
 * @param projectName
 * @param storageProviderName
 * @returns {Promise<boolean>}
 */
export const download = async (projectName: string, storageProviderName?: string) => {
  const storageProvider = getStorageProvider(storageProviderName)
  try {
    logger.info(`[ProjectLoader]: Installing project "${projectName}"...`)
    let files = await getFileKeysRecursive(`projects/${projectName}/`)
    const assetsRegex = new RegExp(`^projects/${projectName}/assets`)
    files = files.filter((file) => !assetsRegex.test(file))
    logger.info('[ProjectLoader]: Found files:' + files)

    const localProjectDirectory = path.join(appRootPath.path, 'packages/projects/projects', projectName)
    if (fs.existsSync(localProjectDirectory)) {
      logger.info('[Project temp debug]: fs exists, deleting')
      deleteFolderRecursive(localProjectDirectory)
    }

    await Promise.all(
      files.map(async (filePath) => {
        logger.info(`[ProjectLoader]: - downloading "${filePath}"`)
        const fileResult = await storageProvider.getObject(filePath)
        if (fileResult.Body.length === 0) logger.info(`[ProjectLoader]: WARNING file "${filePath}" is empty`)
        writeFileSyncRecursive(path.join(appRootPath.path, 'packages/projects', filePath), fileResult.Body)
      })
    )

    logger.info(`[ProjectLoader]: Successfully downloaded and mounted project "${projectName}".`)
    if (projectName !== 'default-project') {
      const npmInstallPromise = new Promise<void>((resolve) => {
        const npmInstallProcess = spawn('npm', ['install', '--legacy-peer-deps'], { cwd: localProjectDirectory })
        npmInstallProcess.once('exit', () => {
          logger.info('Finished npm installing %s', projectName)
          resolve()
        })
        npmInstallProcess.once('error', resolve)
        npmInstallProcess.once('disconnect', resolve)
        npmInstallProcess.stdout.on('data', (data) => logger.info(data.toString()))
      }).then((result) => logger.info(result))
      await Promise.race([
        npmInstallPromise,
        new Promise<void>((resolve) => {
          setTimeout(() => {
            logger.warn(`WARNING: npm installing ${projectName} took too long!`)
            resolve()
          }, 5 * 60 * 1000) // timeout after 5 minutes
        })
      ])
    }
  } catch (e) {
    const errorMsg = `[ProjectLoader]: Failed to download project ${projectName} with error: ${e.message}`
    logger.error(e, errorMsg)
    throw e
  }

  return true
}
