const fse = require('fs-extra')
const glob = require('glob')
const { flatMap } = require('./utils')

function resolveOptions({ files = [] } = {}) {
  return {
    files: files.length ? files : ['./mochawesome-report/mochawesome*.json'],
  }
}

const collectSourceFiles = flatMap(pattern => {
  const files = glob.sync(pattern)
  if (!files.length) {
    throw new Error(`Pattern ${pattern} matched no report files`)
  }
  return files
})

function generateStats(suites, reports) {
  const tests = getAllTests(suites)
  const passes = tests.filter(test => test.state === 'passed')
  const pending = tests.filter(test => test.state === 'pending')
  const failures = tests.filter(test => test.state === 'failed')
  const skipped = tests.filter(test => test.state === 'skipped')
  
  const timeStats = getStateTimeSpan(reports)
  
  return {
    suites: suites.length,
    tests: tests.length,
    passes: passes.length,
    pending: pending.length,
    failures: failures.length,
    testsRegistered: tests.length,
    passPercent: (passes.length * 100) / tests.length,
    pendingPercent: (pending.length * 100) / tests.length,
    other: 0,
    hasOther: false,
    skipped: skipped.length,
    hasSkipped: !!skipped.length,
    ...timeStats
  }
}

function collectReportFiles(files) {
  return Promise.all(files.map(filename => fse.readJson(filename)))
}

const collectReportSuites = flatMap(report =>
  report.results.filter(r => r !== false)
)

const getAllTests = flatMap(suite => [
  ...suite.tests,
  ...getAllTests(suite.suites),
])

const getStateTimeSpan = reports => {
  const spans = reports.map(({ stats: { start, end } }) => {
    return { start: new Date(start), end: new Date(end) }
  })

  const maxSpan = spans.reduce(
    (currentMaxSpan, span) => {
      const start = new Date(
        Math.min(currentMaxSpan.start.getTime(), span.start.getTime())
      )
      const end = new Date(
        Math.max(currentMaxSpan.end.getTime(), span.end.getTime())
      )
      return { start, end }
    }
  )

  return {
    start: maxSpan.start.toISOString(),
    end: maxSpan.end.toISOString(),
    duration: maxSpan.end.getTime() - maxSpan.start.getTime()
  }
}

exports.merge = async function merge(options) {
  options = resolveOptions(options)
  const files = collectSourceFiles(options.files)
  const reports = await collectReportFiles(files)
  const suites = collectReportSuites(reports)

  return {
    stats: generateStats(suites, reports),
    results: suites,
    meta: reports[0].meta,
  }
}
