text: 'use strict'
  const _ = require('lodash')
  
  module.exports = function PersistentKnexModelMixin (Model, knex, inOptions, userOptions) {
    if (typeof Model.convertFromPersistent !== 'function') {
      Model.convertFromPersistent = function (data) {
        return data
      }
    }
  
    if (typeof Model.convertToPersistent !== 'function') {
      Model.convertToPersistent = function (data) {
        return data
      }
    }
  
    /**
     * Set data from database.
     *
     * Applies filters for conversion from database format to normal format.
     */
    Model.prototype.setDataPersistent = function (data) {
      this.setData(this._applyFilter(this.constructor.convertFromPersistent, data))
    }
  
    Model.prototype.getDataPersistent = function () {
      return this._applyFilter(this.constructor.convertToPersistent, this.getData())
    }
  
    Model.prototype.save = function (options) {
      const myKnex = knexOrTransaction(knex, options)(Model.tableName)
      const data = this.getDataPersistent()
      if (this.id) {
        return myKnex.update(data).where('id', this.id).then()
      } else {
        return myKnex.insert(data).then()
      }
    }
  
    Model.prototype.destroy = function (options) {
      const myKnex = knexOrTransaction(knex, options)
      return myKnex(Model.tableName).where('id', this.id).del().then()
    }
  
    Model.fromDatabaseModel = function (data) {
      if (!data) return null
      // If result is an array (return from knex select query), take the first object
      if (Array.isArray(data)) {
        data = data[0]
      }
      const model = new Model()
      model.setDataPersistent(data)
      return model
    }
  
    Model.fromDataPersistent = function (data) {
      const model = new Model()
      model.setDataPersistent(data)
      return model
    }
  
    Model.findAll = function * findAll (options) {
      // this returns an array of JSON objects
      const myKnex = knexOrTransaction(knex, options)
      return myKnex.from(Model.tableName).select().then()
    }
  
    Model.findWhere = function * findWhere (where, options) {
      const myKnex = knexOrTransaction(knex, options)
      return myKnex.from(Model.tableName).select().where(where).then()
    }
  
    Model.findById = function * findById (id, options) {
      const myKnex = knexOrTransaction(knex, options)
      return myKnex.from(Model.tableName).select().where('id', id).then(fromDatabaseModel)
    }
  
    Model.findByKey = function findByKey (keyName, value, options) {
      const myKnex = knexOrTransaction(knex, options)
      return myKnex.from(Model.tableName).select().where(keyName, value).then(fromDatabaseModel)
    }
  
    // Optional: transaction
    // set this to knex transaction if you want this operation to be done inside transaction.
    Model.create = function * create (values, options) {
      const model = Model.fromData(values)
      const modelPersistent = model.getDataPersistent()
      const myKnex = knexOrTransaction(knex, options)
      return myKnex.insert(modelPersistent).into(Model.tableName)
    }
  
    Model.createExternal = function createExternal (values, options) {
      const model = Model.fromDataExternal(values)
      return Model.create(model.getDataPersistent(), options)
    }
  
    Model.bulkCreate = function * bulkCreate (records, options) {
      const models = records
        .map(Model.fromData.bind(Model))
        .map(_.method('getDataPersistent'))
  
      yield bulkCreateInternal(models, options)
    }
  
    Model.bulkCreateExternal = function * bulkCreateExternal (records, options) {
      const models = records
        .map(Model.fromDataExternal.bind(Model))
        .map(_.method('getDataPersistent'))
  
      yield bulkCreateInternal(models, options)
    }
  
    // Helper functions
    function * bulkCreateInternal (models, options) {
      const myKnex = knexOrTransaction(knex, options)
      if (models.length > 0) {
        return myKnex(Model.tableName).insert(models)
      }
    }
  
    Model.destroy = function (options) {
      const myKnex = knexOrTransaction(knex, options)
      const where = options.where || {id: this.id}
      return myKnex(Model.tableName).where(where).del().then()
    }
  
    function knexOrTransaction (knex, options) {
      return !options ? knex : (!options.transaction ? knex : options.transaction)
    }
  
    function fromDatabaseModel (result) {
      return result && Array.isArray(result) && result.length > 0
        ? Model.fromDatabaseModel(result) : null
    }
  }
  