const path = require('path')
const fs = require('fs-extra')
const LRU = require('lru-cache')
const winattr = require('@akryum/winattr')

const hiddenPrefix = '.'
const isPlatformWindows = process.platform.indexOf('win') === 0

const pkgCache = new LRU({
  max: 500,
  maxAge: 1000 * 5
})

const cwd = require('./cwd')

function isDirectory (file) {
  file = file.replace(/\\/g, path.sep)
  try {
    return fs.stat(file).then((x) => x.isDirectory())
  } catch (e) {
    if (process.env.VUE_APP_CLI_UI_DEBUG) console.warn(e.message)
  }
  return false
}

async function list (base, context) {
  let dir = base
  if (isPlatformWindows) {
    if (base.match(/^([A-Z]{1}:)$/)) {
      dir = path.join(base, '\\')
    }
  }
  const files = await fs.readdir(dir, 'utf8')

  const f = await Promise.all(
    files.map(async (file) => {
      const folderPath = path.join(base, file)

      const [directory, hidden] = await Promise.all([
        isDirectory(folderPath),
        isHidden(folderPath)
      ])
      if (!directory) {
        return null
      }
      return {
        path: folderPath,
        name: file,
        hidden
      }
    })
  )
  return f.filter((x) => !!x)
}

async function isHiddenWindows (file) {
  const windowsFile = file.replace(/\\/g, '\\\\')
  return new Promise((resolve, reject) => {
    winattr.get(windowsFile, (file, error) => {
      if (error) {
        return reject(error)
      }
      resolve(file)
    })
  }).then((x) => x.hidden)
}

async function isHidden (file) {
  try {
    const prefixed = path.basename(file).charAt(0) === hiddenPrefix
    const result = {
      unix: prefixed,
      windows: false
    }

    if (isPlatformWindows) {
      result.windows = await isHiddenWindows(file)
    }

    return (
      (!isPlatformWindows && result.unix) ||
      (isPlatformWindows && result.windows)
    )
  } catch (e) {
    if (process.env.VUE_APP_CLI_UI_DEBUG) {
      console.log('file:', file)
      console.error(e)
    }
  }
}

function generateFolder (file, context) {
  return {
    name: path.basename(file),
    path: file
  }
}

function getCurrent (args, context) {
  const base = cwd.get()
  return generateFolder(base, context)
}

function open (file, context) {
  cwd.set(file, context)
  return generateFolder(cwd.get(), context)
}

function openParent (file, context) {
  const newFile = path.dirname(file)
  cwd.set(newFile, context)
  return generateFolder(cwd.get(), context)
}

function isPackage (file, context) {
  try {
    return fs.existsSync(path.join(file, 'package.json'))
  } catch (e) {
    console.warn(e.message)
  }
  return false
}

function readPackage (file, context, force = false) {
  if (!force) {
    const cachedValue = pkgCache.get(file)
    if (cachedValue) {
      return cachedValue
    }
  }
  const pkgFile = path.join(file, 'package.json')
  if (fs.existsSync(pkgFile)) {
    const pkg = fs.readJsonSync(pkgFile)
    pkgCache.set(file, pkg)
    return pkg
  }
}

function writePackage ({ file, data }, context) {
  fs.outputJsonSync(path.join(file, 'package.json'), data, {
    spaces: 2
  })
  invalidatePackage(file, context)
  return true
}

function invalidatePackage (file, context) {
  pkgCache.del(file)
  return true
}

function isVueProject (file, context) {
  if (!isPackage(file)) return false

  try {
    const pkg = readPackage(file, context)
    return Object.keys(pkg.devDependencies || {}).includes('@vue/cli-service')
  } catch (e) {
    if (process.env.VUE_APP_CLI_UI_DEBUG) {
      console.log(e)
    }
  }
  return false
}

function listFavorite (context) {
  return context.db
    .get('foldersFavorite')
    .value()
    .map((file) => generateFolder(file.id, context))
}

function isFavorite (file, context) {
  return !!context.db.get('foldersFavorite').find({ id: file }).size().value()
}

function setFavorite ({ file, favorite }, context) {
  const collection = context.db.get('foldersFavorite')
  if (favorite) {
    collection.push({ id: file }).write()
  } else {
    collection.remove({ id: file }).write()
  }
  return generateFolder(file, context)
}

async function deleteFolder (file) {
  await fs.remove(file)
}

function createFolder (name, context) {
  const file = path.join(cwd.get(), name)
  fs.mkdirpSync(file)
  return generateFolder(file, context)
}

module.exports = {
  isDirectory,
  getCurrent,
  list,
  open,
  openParent,
  isPackage,
  readPackage,
  writePackage,
  invalidatePackage,
  isVueProject,
  isFavorite,
  listFavorite,
  setFavorite,
  delete: deleteFolder,
  create: createFolder
}
