const fs = require('fs').promises
const { resolve } = require('path')
const { definitions, aliases } = require('../lib/npm.js')

const describeAll = (content) =>
  content.map(name => definitions[name].describe()).join(
    '\n\n<!-- automatically generated, do not edit manually -->\n' +
      '<!-- see lib/utils/config/definitions.js -->\n\n'
  )

const describeUsage = ({ usage, configDoc, commandFile }) => {
  const synopsis = []

  // Grab the command name from the *.md filename
  // NOTE: We cannot use the name property command file because in the case of
  // `npx` the file being used is `lib/commands/exec.js`
  const commandName = configDoc.split('/').pop().split('.')[0].replace('npm-', '')
  synopsis.push('\n```bash')

  if (commandName) {
    // special case for `npx`:
    // `npx` is not technically a command in and of itself,
    // so it just needs the usage and parameters of npm exec, and none of the aliases
    if (commandName === 'npx') {
      synopsis.push(usage.map(usageInfo => `npx ${usageInfo}`).join('\n'))
    } else {
      const baseCommand = `npm ${commandName}`
      if (!usage) {
        synopsis.push(baseCommand)
      } else {
        synopsis.push(usage.map(usageInfo => `${baseCommand} ${usageInfo}`).join('\n'))
      }

      const cmdAliases = Object.keys(aliases).reduce((p, c) => {
        if (aliases[c] === commandName) {
          p.push(c)
        }
        return p
      }, [])

      if (cmdAliases.length === 1) {
        synopsis.push('')
        synopsis.push(`alias: ${cmdAliases[0]}`)
      } else if (cmdAliases.length > 1) {
        synopsis.push('')
        synopsis.push(`aliases: ${cmdAliases.join(', ')}`)
      }
    }
  } else {
    console.error(`could not determine command name from ${commandFile}`)
  }

  synopsis.push('```')
  return synopsis.join('\n')
}

const addBetweenTags = (
  doc,
  startTag,
  endTag,
  body,
  sourceFilepath = 'lib/utils/config/definitions.js') => {
  const startSplit = doc.split(startTag)

  if (startSplit.length !== 2) {
    throw new Error('Did not find exactly one start tag')
  }

  const endSplit = startSplit[1].split(endTag)
  if (endSplit.length !== 2) {
    throw new Error('Did not find exactly one end tag')
  }

  return [
    startSplit[0],
    startTag,
    '\n<!-- automatically generated, do not edit manually -->\n' +
      '<!-- see ' + sourceFilepath + ' -->\n',
    body,
    '\n\n<!-- automatically generated, do not edit manually -->\n' +
      '<!-- see ' + sourceFilepath + ' -->',
    '\n\n',
    endTag,
    endSplit[1],
  ].join('')
}

const TAGS = {
  CONFIG: {
    START: '<!-- AUTOGENERATED CONFIG DESCRIPTIONS START -->',
    END: '<!-- AUTOGENERATED CONFIG DESCRIPTIONS END -->',
  },
  USAGE: {
    START: '<!-- AUTOGENERATED USAGE DESCRIPTIONS START -->',
    END: '<!-- AUTOGENERATED USAGE DESCRIPTIONS END -->',
  },
}

const addDescriptions = ({ doc, params }) =>
  addBetweenTags(doc, TAGS.CONFIG.START, TAGS.CONFIG.END, describeAll(params))

const addUsageDescriptions = ({ doc, usage, configDoc, commandFile }) =>
  addBetweenTags(doc, TAGS.USAGE.START, TAGS.USAGE.END,
    describeUsage({ usage, configDoc, commandFile }),
    commandFile
  )

const run = async (configDoc, commandFile) => {
  try {
    // Note: commands without params skip this whole process.
    const { params, usage } = require(resolve(commandFile))

    // always write SOMETHING so that Make sees the file is up to date.
    const doc = await fs.readFile(configDoc, 'utf8')
    const hasTag = doc.includes(TAGS.CONFIG.START)
    const hasUsageTag = doc.includes(TAGS.USAGE.START)

    if (params && params.length) {
      let newDoc = hasTag ? addDescriptions({ doc, params }) : doc
      newDoc = hasUsageTag
        ? addUsageDescriptions({ doc: newDoc, usage, configDoc, commandFile })
        : newDoc

      if (!hasTag) {
        console.error('WARNING: did not find config description section', configDoc)
      }

      if ((usage && usage.length) && !hasUsageTag) {
        console.error('WARNING: did not find usage description section', configDoc)
      }
      await fs.writeFile(configDoc, newDoc)
    }
  } catch (err) {
    console.error(`WARNING: file cannot be open: ${configDoc}`)
    throw err
  }
}

run(...process.argv.slice(2)).catch((err) => {
  process.exitCode = 1
  console.error(err)
})
