const test = require("ava")
const css = require(process.cwd())
const fs = require("fs")
const glob = require("glob")

let selectors
let classnames

const classPatterns = [
  // HTML class attributes
  /class="([^"]+)"/ig,
  // assume that ERB helpers generate an element with the same class
  /<%= (\w+)\b/g,
]

// Find unique selectors from the cssstats selector list
function uniqueSelectors(s) {
  s = s.map(s => {
    // split multi-selectors into last class used .foo .bar .baz
    return s.split(" ").pop()
  })
  .filter(s => {
    // remove any selector that aren't just regular classnames eg. ::hover [type]
    return s.match(/^\.[a-z\-_]+$/ig)
  })

  // return only the unique selectors
  return [...new Set(s)]
}

// From the given glob sources array, read the files and return found classnames
function documentedClassnames(sources) {
  const classes = []
  const files = sources.reduce((acc, pattern) => {
    return acc.concat(glob.sync(pattern))
  }, [])

  files.forEach(file => {
    let match = null
    const content = fs.readFileSync(file, "utf8")

    classPatterns.forEach(pattern => {
      // match each pattern against the source
      while (match = pattern.exec(content)) {
        // get the matched classnames and split by whitespace into classes
        const klasses = match[1].trim().split(/\s+/)
        classes.push(...klasses)
      }
    })
  })

  // return only the unique classnames
  return Array.from(new Set(classes))
}

// Before all the tests get the selectors and classnames
test.before(t => {
  selectors = uniqueSelectors(css.cssstats.selectors.values)
  classnames = documentedClassnames([
    "docs/*.md",
    "README.md"
  ])
})

test("Every selector class is documented", t => {
  const undocumented = []
  selectors.forEach(selector => {
    if (!classnames.includes(selector.replace(/^\./, ""))) {
      undocumented.push(selector)
    }
  })
  t.is(undocumented.length, 0, `I did not find documentation for the "${undocumented.join(", ")}" selector(s) in the ${process.env.npm_package_name} module.`);
})

const {join} = require('path')
const fse = require('fs-extra')
const globby = require('globby')
const test = require('ava')
const minimatch = require('minimatch')

const cwd = process.cwd()
const css = require(cwd)
const pkg = require(join(cwd, 'package.json'))

const unique = list => Array.from(new Set(list)).sort()

/*
 * These are the regular expressions that match what we
 * expect to be class name instances in the docs.
 * Patterns should group the matched class name(s) such that:
 *
 * ```js
 * const [, klass, ] = pattern.exec(content)
 * ```
 */
const classPatterns = [
  // HTML class attributes
  /class="([^"]+)"/ig,
  /:class ?=> "([^"]+)"/g,
  /class: "([^"]+)"/g,
  // assume that ERB helpers generate an element with the same class
  /<%= (\w+)\b/g,
]

const whitelistClasses = (pkg.primer ? pkg.primer.class_whitelist || [] : [])
  .concat('js*')

const isWhitelisted = klass => {
  return whitelistClasses.some(glob => minimatch(klass, glob))
}

// Find unique selectors from the cssstats selector list
function uniqueSelectors(selectors) {
  return unique(selectors.map(s => {
    // split multi-selectors into last class used .foo .bar .baz
    return s.split(' ').pop()
  })
  .filter(s => {
    // only match exact class selectors
    return s.match(/^\.[a-z\-_]+$/ig)
  }))
}

// From the given glob sources array, read the files and return found classnames
function getDocumentedClassnames(sources) {
  return globby(sources)
    .then(paths => {
      return Promise.all(paths.map(path => {
        return fse.readFile(path, 'utf8')
          .then(content => ({path, content}))
      }))
    })
    .then(files => {
      return files.reduce((classes, {path, content}) => {
        classPatterns.forEach(pattern => {
          let match
          while (match = pattern.exec(content)) {
            // get the matched classnames and split by whitespace into classes
            let klasses = match[1].trim().split(/\s+/)
            classes.push(...klasses)
          }
        })
        return classes
      }, [])
    })
    .then(classes => unique(classes))
}

const selectors = uniqueSelectors(css.cssstats.selectors.values)
let classnames
test.before(t => {
  return getDocumentedClassnames([
      'docs/*.md',
      'README.md'
    ])
    .then(_ => (classnames = _))
})

selectors.forEach(selector => {
  const klass = selector.replace(/^\./, '')
  test(`Selector "${selector}" is documented/whitelisted`, t => {
    t.plan(1)
    if (isWhitelisted(klass)) {
      t.pass(`Selector "${selector}" is whitelisted`)
    } else {
      t.is(classnames.includes(klass), true, `Selector "${selector}" is not documented`)
    }
  })
})

refactor docs test: more async, one test per class