var path = require('path')
var url = require('url')

var webpack = require('webpack')
var mime = require('mime')
var Promise = require('bluebird')
var _ = require('lodash')
var MemoryFileSystem = require('webpack/node_modules/memory-fs')

var logger = require('../../../util/logger')
var lifecycle = require('../../../util/lifecycle')
var globalOptions = require('../../../../webpack.config').webpack

// Similar to webpack-dev-middleware, but integrates with our custom
// lifecycle, behaves more like normal express middleware, and removes
// all unnecessary features.
module.exports = function(options) {
  var log = logger.createLogger('middleware:webpack')
  options = _.defaults(options || {}, globalOptions)

  var compiler = webpack(options)
  var fs = compiler.outputFileSystem = new MemoryFileSystem()

  var valid = false
  var queue = []

  log.info('Creating bundle')
  var watching = compiler.watch(options.watchDelay, function(err) {
    if (err) {
      log.fatal('Webpack had an error', err.stack)
      lifecycle.fatal()
    }
  })

  lifecycle.observe(function() {
    if (watching.watcher) {
      watching.watcher.close()
    }
  })

  function doneListener(stats) {
    process.nextTick(function() {
      if (valid) {
        log.info(stats.toString(options.stats))

        if (stats.hasErrors()) {
          log.error('Bundle has errors')
        }
        else if (stats.hasWarnings()) {
          log.warn('Bundle has warnings')
        }
        else {
          log.info('Bundle is now valid')
        }

        queue.forEach(function(resolver) {
          resolver.resolve()
        })
      }
    })

    valid = true
  }

  function invalidate() {
    if (valid) {
      log.info('Bundle is now invalid')
      valid = false
    }
  }

  compiler.plugin('done', doneListener)
  compiler.plugin('invalid', invalidate)
  compiler.plugin('compile', invalidate)

  function bundle() {
    if (valid) {
      return Promise.resolve()
    }
    else {
      log.info('Waiting for bundle to finish')
      var resolver = Promise.defer()
      queue.push(resolver)
      return resolver.promise
    }
  }

  return function(req, res, next) {
    var parsedUrl = url.parse(req.url)

    var target = path.join(
      compiler.outputPath
    , parsedUrl.pathname
    )

    bundle()
      .then(function() {
        try {
          var body = fs.readFileSync(target)
          res.set('Content-Type', mime.lookup(target))
          res.end(body)
        }
        catch (err) {
          return next()
        }
      })
      .catch(next)
  }
}
