const path = require('path')
const fs = require('fs-extra')
const parseDiff = require('../util/parse-diff')
// Connectors
const cwd = require('./cwd')
// Utils
const { execa, hasProjectGit } = require('@vue/cli-shared-utils')

async function getNewFiles (context) {
  if (!hasProjectGit(cwd.get())) return []

  const { stdout } = await execa('git', [
    'ls-files',
    '-o',
    '--exclude-standard',
    '--full-name'
  ], {
    cwd: cwd.get()
  })
  if (stdout.trim()) {
    return stdout.split(/\r?\n/g)
  }
  return []
}

async function getDiffs (context) {
  if (!hasProjectGit(cwd.get())) return []

  const { highlightCode } = require('../util/highlight')

  const newFiles = await getNewFiles(context)
  await execa('git', ['add', '-N', '*'], {
    cwd: cwd.get()
  })
  const { stdout } = await execa('git', ['diff'], {
    cwd: cwd.get()
  })
  await reset(context)
  const list = parseDiff(stdout)
  for (const n in list) {
    const fileDiff = list[n]
    const isNew = newFiles.includes(fileDiff.to)
    let fromContent
    if (!isNew) {
      const result = await execa('git', ['show', `HEAD:${fileDiff.from}`], {
        cwd: cwd.get()
      })
      fromContent = result.stdout
    }
    const highlightedContentFrom = fromContent && highlightCode(fileDiff.from, fromContent).split('\n')
    const highlightedContentTo = highlightCode(fileDiff.to, fs.readFileSync(path.resolve(cwd.get(), fileDiff.to), { encoding: 'utf8' })).split('\n')
    for (const chunk of fileDiff.chunks) {
      for (const change of chunk.changes) {
        const firstChar = change.content.charAt(0)
        let highlightedCode
        if (change.normal) {
          highlightedCode = highlightedContentFrom[change.ln1 - 1]
        } else if (change.type === 'del') {
          highlightedCode = highlightedContentFrom[change.ln - 1]
        } else if (change.type === 'add') {
          highlightedCode = highlightedContentTo[change.ln - 1]
        }
        if (highlightedCode) {
          change.content = firstChar + highlightedCode
        }
      }
    }
    list[n] = {
      id: fileDiff.index.join(' '),
      ...fileDiff,
      new: isNew
    }
  }

  return list
}

async function commit (message, context) {
  if (!hasProjectGit(cwd.get())) return false

  await execa('git', ['add', '*'], {
    cwd: cwd.get()
  })
  await execa('git', ['commit', '-m', message.replace(/"/, '\\"')], {
    cwd: cwd.get()
  })
  return true
}

async function reset (context) {
  if (!hasProjectGit(cwd.get())) return false

  await execa('git', ['reset'], {
    cwd: cwd.get()
  })
  return true
}

async function getRoot (context) {
  if (!hasProjectGit(cwd.get())) return cwd.get()

  const { stdout } = await execa('git', [
    'rev-parse',
    '--show-toplevel'
  ], {
    cwd: cwd.get()
  })
  return stdout
}

async function resolveFile (file, context) {
  const root = await getRoot(context)
  return path.resolve(root, file)
}

module.exports = {
  getDiffs,
  commit,
  reset,
  getRoot,
  resolveFile
}
