const debug = require('debug')('thumbsup')
const downsize = require('thumbsup-downsize')
const os = require('os')
const fs = require('fs-extra')
const ListrWorkQueue = require('listr-work-queue')
const path = require('path')

exports.run = function (files, opts, parentTask) {
  const jobs = exports.create(files, opts)
  // wrap each job in a Listr task that returns a Promise
  const tasks = jobs.map(job => listrTaskFromJob(job, opts.output))
  const listr = new ListrWorkQueue(tasks, {
    concurrent: os.cpus().length,
    update: (done, total) => {
      const progress = done === total ? '' : `(${done}/${total})`
      parentTask.title = `Processing media ${progress}`
    }
  })
  return listr
}

/*
  Return a list of task to build all required outputs (new or updated)
*/
exports.create = function (files, opts) {
  var tasks = {}
  const actionMap = getActionMap(opts)
  // accumulate all tasks into an object
  // to remove duplicate destinations
  files.forEach(f => {
    debug(`Tasks for ${f.path}, ${JSON.stringify(f.output)}`)
    Object.keys(f.output).forEach(out => {
      var src = path.join(opts.input, f.path)
      var dest = path.join(opts.output, f.output[out].path)
      var destDate = modifiedDate(dest)
      var action = actionMap[f.output[out].rel]
      // ignore output files that don't have an action (e.g. existing links)
      if (action && f.date > destDate) {
        tasks[dest] = {
          file: f,
          dest: dest,
          rel: f.output[out].rel,
          action: (done) => {
            fs.mkdirsSync(path.dirname(dest))
            debug(`${f.output[out].rel} from ${src} to ${dest}`)
            return action({src: src, dest: dest}, done)
          }
        }
      }
    })
  })
  // back into an array
  const list = Object.keys(tasks).map(dest => tasks[dest])
  debug(`Created ${list.length} tasks`)
  return list
}

function listrTaskFromJob (job, outputRoot) {
  const relative = path.relative(outputRoot, job.dest)
  return {
    title: relative,
    task: (ctx, task) => {
      return new Promise((resolve, reject) => {
        var progressEmitter = job.action(err => {
          err ? reject(err) : resolve()
        })
        // render progress percentage for videos
        if (progressEmitter) {
          progressEmitter.on('progress', (percent) => {
            task.title = `${relative} (${percent}%)`
          })
        }
      })
    }
  }
}

function modifiedDate (filepath) {
  try {
    return fs.statSync(filepath).mtime.getTime()
  } catch (ex) {
    return 0
  }
}

function getActionMap (opts) {
  const thumbSize = opts.thumbSize || 120
  const largeSize = opts.largeSize || 1000
  const thumbnail = { height: thumbSize, width: thumbSize }
  const large = { width: largeSize }
  return {
    'fs:copy': (task, done) => fs.copy(task.src, task.dest, done),
    'fs:symlink': (task, done) => fs.symlink(task.src, task.dest, done),
    'photo:thumbnail': (task, done) => downsize.image(task.src, task.dest, thumbnail, done),
    'photo:large': (task, done) => downsize.image(task.src, task.dest, large, done),
    'video:thumbnail': (task, done) => downsize.still(task.src, task.dest, thumbnail, done),
    'video:poster': (task, done) => downsize.still(task.src, task.dest, large, done),
    'video:resized': (task, done) => downsize.video(task.src, task.dest, {}, done)
  }
}
