/**
 * Higher order component with form
 * @function withForm
 * @param {function} Component - A component constructor
 * @returns {function} - Wrapped component
 */
'use strict'

import {wrap} from 'breact'
import classnames from 'classnames'
import {ApStyle} from 'apeman-react-style'
import uuid from 'uuid'
import {spinalcase} from 'stringcase'
import React, {PropTypes as types} from 'react'
import {alpha} from 'acolor'
import {clone} from 'asobj'

const noop = () => null
const rejectMetaKey = (name) => !/^[$_]/.test(name)

/** @lends withForm */
function withForm (Component) {
  return wrap(Component, {
    displayName: 'withForm',
    propTypes: {
      /** Form id */
      id: types.string,
      /** Submit handling */
      onSubmit: types.func,
      /** Cancel handling */
      onCancel: types.func,
      /** Update values */
      onUpdate: types.func.isRequired,
      /** Form values */
      values: types.object.isRequired,
      /** Form errors */
      errors: types.object,
      /** Error color */
      errorColor: types.string
    },

    getDefaultProps () {
      return {
        onSubmit: noop,
        onCancel: noop,
        onUpdate: noop,
        values: {},
        errors: null,
        errorColor: '#E11'
      }
    },

    render () {
      const s = this
      let { props } = s
      let {
        id = `form-${s.uuid}`,
        errors,
        errorColor
      } = props

      let errorList = <withForm.ErrorList errors={ errors }
                                          id={ id }/>

      let errorStyle = <withForm.ErrorStyle errors={ errors }
                                            errorColor={ errorColor }
                                            id={ id }/>

      let idOf = (name) => `${id}-${spinalcase(name)}`
      let componentProps = clone(props, {
        without: [ 'className' ]
      })
      let className = classnames(props.className, {
        'with-ap-form-error': Object.keys(errors || {}).filter(rejectMetaKey).length > 0
      })
      return (
        <Component { ...componentProps }
                   className={ className }
                   id={ id }
                   idOf={ idOf }
                   errorStyle={ errorStyle }
                   errorList={ errorList }
        />
      )
    },

    // --------------------
    // Lifecycle
    // --------------------

    componentWillMount () {
      const s = this
      s.uuid = uuid.v4()
    }
  })
}

Object.assign(withForm, {
  // ------------------
  // Custom
  // ------------------

  ErrorList ({ errors, id }) {
    let items = Object.keys(errors || {})
      .filter(rejectMetaKey)
      .reduce((items, name) => Object.assign(items, {
        [errors[ name ]]: name // Use message as key to reject duplication
      }), {})
    let className = classnames('ap-form-error-list', {
      'ap-form-error-list-empty': Object.keys(items).length === 0
    })

    return (
      <ul className={ className }
          id={ `error-list-for-${id}` }
      >
        {
          Object.keys(items).map((message) => (
            <li key={ items[ message ] }
                className='ap-form-error-list-item'
            >{ message }</li>
          ))
        }
      </ul>
    )
  },

  ErrorStyle ({ errors, id, errorColor }) {
    let data = Object.keys(errors || {})
      .filter(rejectMetaKey)
      .reduce((data, name) => Object.assign(data, {
        [`#${id} [name="${name}"]`]: {
          borderColor: errorColor,
          outlineColor: errorColor,
          backgroundColor: alpha(errorColor, 0.1)
        }
      }), {})
    return (
      <ApStyle data={ data }/>
    )
  }
})

export default withForm
