import {
    negate, isNil, isString, mapValues, extend, forIn, isFunction, pick, isBoolean,
} from 'lodash';
import moment from 'moment'
import isEmail from 'validator/lib/isEmail';
import Formous from 'formous';

import { isBlank } from './util';

function buildTest(defaultOptions, defaultMessage, test) {
    const { test: defaultTest, critical, message, name: _, ...otherOptions } = defaultOptions;
    return extend({
        tests: [{
            test: test || defaultTest,
            critical: isNil(critical) ? true : critical,
            failProps: {
                errorText: message || defaultMessage || 'is invalid',
            },
        }],
    }, otherOptions);
}

export function hasLength(len) {
    return options =>
        buildTest(options, `must be of length ${len}`, s => s.length >= len);
}

const NUMBER = /^[+-]?(\d*\.)?\d+$/;

export function numberValue(options = {}) {
    return buildTest(options, 'must be a number', v => NUMBER.test(v));
}

export function stringValue(options = {}) {
    return buildTest(options, 'must be a string', isString);
}

export function validEmail(options = {}) {
    return buildTest(options, 'must be a valid email', email => isEmail(email || ''));
}

export function booleanValue(options = {}) {
    return buildTest(options, 'must be true or false', isBoolean);
}

export function nonBlank(options = {}) {
    return buildTest(options, 'is required', negate(isBlank));
}

export function dateValue(options = {}) {
    return buildTest(options, 'must be valid date', v => moment(v, options.format).isValid());
}

export function validation(options = {}) {
    return buildTest(options, 'is required', options.test);
}

export function buildValidations(options = {}) {
    return mapValues(options, (v, name) => (isFunction(v) ? v({ name }) : v));
}

export function testWith(fn, msg='is invalid') {
    return buildTest({}, msg, fn);
}

const passEverything = () => true;

export function anyValue() {
    return buildTest({}, '', passEverything);
}

export function setFieldValue(field, value) {
    if (!field.events) { return; }
    extend(field, { cid: Math.random(), value });
    field.events.onChange({ target: { value: isNil(value) ? '' : value } });
}

export function setFieldValues(fields, values) {
    forIn(fields, (field, name) => {
        setFieldValue(field, values[name]);
    });
}

export function persistFieldValues(fields, model) {
    forIn(fields, (field, name) => (model[name] = field.value)); // eslint-disable-line
    return Promise.resolve(model);
}

export function addFormFieldValidations(component, ...staticProps) {
    const options = { fields: buildValidations(component.formFields) };
    return extend(Formous(options)(component), pick(component, staticProps));
}

@@ -85,16 +85,27 @@
 Boolean,
+ get, keys,
 %0A%7D from 
@@ -218,16 +218,44 @@
 rmous';%0A
+import %7B when %7D from 'mobx';
 %0Aimport 
@@ -2136,58 +2136,9 @@
 ; %7D%0A
-    extend(field, %7B cid: Math.random(), value %7D);
 %0A
+
     
@@ -2244,21 +2244,20 @@
 dValues(
-field
+prop
 s, value
@@ -2269,90 +2269,352 @@
     
-forIn(fields, (field, name) =%3E %7B%0A        setFieldValue(field, values%5Bname%5D
+props.setFieldValues(pick(values, keys(props.fields)));%0A%7D%0A%0Aexport function setFieldsFromModel(props, model) %7B%0A    if (get(model, 'syncInProgress.isFetch')) %7B%0A        when(%0A            () =%3E !get(model, 'syncInProgress.isFetch'),%0A            () =%3E setFieldValues(props, model),%0A        );%0A    %7D else %7B%0A        setFieldValues(props, model
 );%0A    %7D
 );%0A%7D
@@ -2609,18 +2609,16 @@
 );%0A    %7D
-);
 %0A%7D%0A%0Aexpo

set after model is saved