const { hasProjectYarn, hasProjectPnpm, execa } = require('@vue/cli-shared-utils')

const severity = {
  critical: 0,
  high: 1,
  moderate: 2,
  low: 3
}

exports.auditProject = async function (cwd) {
  try {
    if (hasProjectYarn(cwd)) {
      const child = await execa('yarn', [
        'audit',
        '--json',
        '--non-interactive',
        '--no-progress'
      ], {
        cwd,
        reject: false
      })

      if (child.stderr) {
        const errLines = child.stderr.split('\n').map(l => l.trim()).filter(l => l)
        const error = errLines.find(l => l.startsWith('Error:'))
        if (error) {
          throw new Error(error.substr('Error:'.length).trim())
        }
      }

      const data = child.stdout

      const auditAdvisories = []

      const ids = {}

      const lines = data.split('\n').filter(l => l.trim()).map(l => JSON.parse(l))
      for (const line of lines) {
        if (line.type === 'auditAdvisory') {
          if (!ids[line.data.advisory.id]) {
            auditAdvisories.push(line)
            ids[line.data.advisory.id] = true
          }
        }
      }

      const details = {
        vulnerabilities: [],
        summary: {
          critical: 0,
          high: 0,
          moderate: 0,
          low: 0
        }
      }

      auditAdvisories.sort((a, b) => severity[a.data.advisory.severity] - severity[b.data.advisory.severity])

      let id = 0
      for (const { data: { advisory } } of auditAdvisories) {
        for (const finding of advisory.findings) {
          // const [finding] = advisory.findings
          const detail = {
            id: id++,
            name: advisory.module_name,
            version: finding.version,
            parents: finding.paths.sort(
              (a, b) => a.length - b.length
            ).map(
              parents => parents.split('>').slice(0, parents.length - 2).map(p => ({
                name: p
              }))
            ),
            moreInfo: advisory.url,
            severity: advisory.severity,
            title: advisory.title,
            message: advisory.overview,
            versions: {
              vulnerable: advisory.vulnerable_versions,
              patched: advisory.patched_versions
            },
            recommendation: advisory.recommendation
          }
          details.vulnerabilities.push(detail)
          details.summary[advisory.severity]++
        }
      }

      const status = {
        status: 'ok',
        count: details.vulnerabilities.length,
        message: null
      }

      if (status.count) {
        status.status = 'attention'
      }

      for (const n in details.summary) {
        if (details.summary) {
          status.severity = n
          break
        }
      }

      return {
        status,
        details
      }
    } else if (hasProjectPnpm(cwd)) {
      // TODO pnpm audit
      return {
        status: {
          status: 'error',
          message: 'Not implemented for PNPM projects yet'
        },
        details: null
      }
    } else {
      // TODO NPM audit
      return {
        status: {
          status: 'error',
          message: 'Not implemented for NPM projects yet'
        },
        details: null
      }
    }
  } catch (e) {
    return {
      status: {
        status: 'error',
        message: e.message
      },
      details: null
    }
  }
}
