'use strict'

const fs = require('fs')
const pug = require('pug')
const path = require('path')
const moment = require('moment')
const { Compilation } = require('webpack')
const { OriginalSource } = require('webpack-sources')

const plugin = {
  name: 'MarkdownPostsPlugin',
  manifest: require('../src/assets/manifest.json'),
  filters: [
    'markdown-it-emoji',
    'markdown-it-abbr',
    'markdown-it-decorate',
    'markdown-it-mark',
    'markdown-it-footnote',
    'markdown-it-ins',
    'markdown-it-container',
    'markdown-it-sub',
    'markdown-it-sup',
    'markdown-it-video'
  ]
}

class MarkdownPostsPlugin {
  constructor (options) {
    if (!('location' in options)) {
      return false
    }
    Object.assign(this, options, plugin)
  }

  apply (compiler) {
    compiler.hooks.entryOption.tap(plugin.name, (context, entry) => {
      Object.assign(this, { entry, context })
      this.posts = this.process(this.collection)
    })
    compiler.hooks.compilation.tap(plugin.name, (compilation) => {
      compilation.hooks.processAssets.tap(
        {
          name: plugin.name,
          stage: Compilation.PROCESS_ASSETS_STAGE_ADDITIONS
        },
        (assets) => {
          this.posts.forEach(post => {
            assets[post.savePath] = post.OriginalSource
          })
        }
      )
    })
  }

  process (collection) {
    collection.forEach(post => {
      post.template = `extends /src/views/partials/layout.pug\n\nblock content\n\tmain.post\n\t\tsection\n\t\t\tinclude:markdown /${this.location}/${post.filename}\n\tinclude /src/views/partials/footer.pug`
      pug.render(post.template, {
        filename: path.join(this.output, post.savePath),
        basedir: this.context,
        manifest: this.manifest,
        postname: post.friendly,
        filters: {
          markdown: string => _filter(string)
        }
      }, (error, html) => {
        if (!error) {
          post.OriginalSource = new OriginalSource(html, path.join(this.context, this.output, post.savePath))
        } else {
          console.error(error)
        }
      })
    })
    return collection
  }
}

const collection = (source) => {
  return fs.readdirSync(source).map(filename => {
    const file = path.resolve(source, filename)
    const stats = fs.statSync(file)
    const content = fs.readFileSync(file, { encoding: 'utf8', flag: 'r' })
    return {
      comments: _comments(content),
      readtime: _readtime(200, content),
      filename: filename,
      stats: {
        birthtime: moment(stats.birthtime).subtract('days').calendar(),
        size: (Math.floor(stats.size / 1024) > 0 ? `${Math.floor(stats.size / 1024)} kilobytes` : `${stats.size} bytes`)
      },
      savePath: `./posts/${filename.split('.')[0]}.html`,
      friendly: _capitalize(`${filename.split('.')[0]}`, '-')
    }
  })
}

const _capitalize = (string, delimiter) => {
  return string.split(delimiter).map(word => {
    return word[0].toUpperCase() + word.substring(1)
  }).join(' ')
}

const _comments = (input) => {
  const output = { text: [] }
  const comments = input.match(/(\[\/\/]:\s#\s(?=\(*).([^\n\r]*[a-zA-Z]*)+\))/gm)
  if (comments !== null) {
    comments.forEach(comment => {
      const content = comment.slice(9, (comment.length - 1))
      try {
        output.data = JSON.parse(content)
      } catch (e) {
        output.text.push(content)
      }
    })
  }
  if (output.text.length < 1 && !output.data) {
    return null
  } else {
    return output
  }
}

const _readtime = (wpm, string) => {
  const words = string.split(' ').length
  return words > 0 ? Math.ceil(words / wpm) : 0
}

const _filter = (source) => {
  const hljs = require('highlight.js')
  const md = require('markdown-it')({
    html: true,
    linkify: true,
    typographer: true,
    highlight: (string, language) => {
      if (language && hljs.getLanguage(language)) {
        try {
          return hljs.highlight(string, { language }).value
        } catch (error) {
          console.error(error)
        }
      }
    }
  })
  plugin.filters.forEach(filter => {
    if (filter === 'markdown-it-container') {
      // below is the dumbest thing ive ever seen, why can't we use an array?!
      // oh... this is why "There are no need to "improve" working things :)"
      // https://github.com/markdown-it/markdown-it-container/issues/38
      md.use(require(filter), 'warning', [])
      md.use(require(filter), 'spoiler', [])
      md.use(require(filter), 'information', [])
    } else {
      md.use(require(filter), [])
    }
  })
  return md.render(source)
}

module.exports = { MarkdownPostsPlugin, collection }
