import React, { Component } from 'react'
import { connect } from 'react-redux'
import invariant from 'invariant'
import find from 'lodash.find'

import Plurality from './constants/Plurality'
import { createRequest } from './creators'
import { fetchResource } from './sagas'
import { deriveContentTypeOptions } from './contentTypes'

function wowSuchFunction () {}

const isMinified = wowSuchFunction.name !== 'wowSuchFunction'

function invariantTargetMinifiedWithoutDisplayName (target) {
  invariant(
    !isMinified || (isMinified && !target.displayName),
    'Pepperoni cannot derive the content type from a minified component. ' +
    'Add a static property `displayName` to the component.'
  )
}

function invariantCannotDeriveContentType (targetName, contentTypeOptions) {
  invariant(
    contentTypeOptions,
    'Could not derive content type from name "%s". ' +
    'Pass built-ins using `pepperoni/contentTypes`. For example, ContentTypes.POST. ' +
    'Custom content types should be registered at initialisation and passed in using registered name.',
    targetName
  )
}

function invariantContentTypeNotRecognised (contentTypeOptions, contentType) {
  invariant(
    contentTypeOptions,
    'The content type "%s" is not recognised. ' +
    'Register custom content types during initialisation.',
    contentType
  )
}

/**
 * Connect a component to data from the WP-API.
 *
 * Derived content type, explicit identifier:
 * ```js
 * connectWordPress('my-page-slug')(Component) // slug
 * connectWordPress(1337)(Component) // ID
 * ```
 *
 * Derived content type, derived identifier:
 * ```js
 * connectWordPress((props) => props.params.slug)(Component)
 * ```
 *
 * Explicit content type, derived identifier:
 * ```js
 * const { Page } from 'pepperoni/contentTypes'
 * connectWordPress(Page, (props) => props.params.slug)(Component)
 * ```
 *
 * @params {String|Function|Number} contentType The content type of the data to fetch from WP-API
 * @params {String|Function|Number} [identifier] The subject identifier of the content
 * @returns {Function}
 */
export default function connectWordPress (contentType, identifier) {
  let hasDispatchedRequestAction = false;
  let hasWarnedNoEntity = false;

  return (target) => {
    const targetName = target.displayName
      ? target.displayName
      : target.name

    invariant(
      !target.__pepperoni,
      'The component "%s" is already wrapped by Pepperoni.',
      targetName
    )

    let getContentTypeOptions = (contentTypes) => {
      const contentTypeOptions = find(contentTypes, { name: { canonical: contentType } })
      invariantContentTypeNotRecognised(contentTypeOptions, contentType)
      return contentTypeOptions
    }

    let getIdentifier = (props) => typeof identifier === 'function'
      ? identifier(props)
      : identifier

    if (typeof contentType !== 'string' && typeof identifier === 'undefined') {
      let contentTypeOptions

      invariantTargetMinifiedWithoutDisplayName(target)

      getContentTypeOptions = (contentTypes) => {
        if (!contentTypeOptions) {
          contentTypeOptions = deriveContentTypeOptions(targetName, contentTypes)
          invariantCannotDeriveContentType(targetName, contentTypeOptions)
        }
        return contentTypeOptions
      }

      getIdentifier = (props) => typeof contentType === 'function'
        ? contentType(props)
        : contentType
    }

    function mapStateToProps (state, ownProps) {
      const { contentTypes } = state.wordpress.config

      const subject = getIdentifier(ownProps)
      const contentTypeOpts = getContentTypeOptions(contentTypes)

      const isSlugSubject = isNaN(Number(subject))
      const nameSingular = contentTypeOpts.name[Plurality.SINGULAR]
      const namePlural = contentTypeOpts.name[Plurality.PLURAL]
      const contentTypeCollection = state.wordpress.entities[namePlural]

      let entity = null

      if (contentTypeCollection) {
        entity = isSlugSubject
          ? find(contentTypeCollection, (obj) => obj.slug === subject)
          : contentTypeCollection[String(subject)]
      }

      return {
        wordpress: state.wordpress,
        [nameSingular]: entity
      }
    }

    class PepperoniComponentWrapper extends Component {
      render () {
        const { contentTypes } = this.props.wordpress.config

        const subject = getIdentifier(this.props)
        const contentTypeOpts = getContentTypeOptions(contentTypes)

        const nameSingular = contentTypeOpts.name[Plurality.SINGULAR]
        const canonicalName = contentTypeOpts.name.canonical

        if (!this.props[nameSingular] && !hasDispatchedRequestAction) {
          hasDispatchedRequestAction = true
          this.props.dispatch(createRequest(canonicalName, subject))
        } else if (!this.props[nameSingular] && hasDispatchedRequestAction) {
          if (!hasWarnedNoEntity) {
            hasWarnedNoEntity = true
            console.warn(`Pepperoni: subject was not found with identifier \`${subject}\``)
            // TODO do something other than console.warn here
          }
        }

        return React.createElement(target, this.props)
      }

      shouldComponentUpdate (nextProps) {
        const { contentTypes } = this.props.wordpress.config

        const contentTypeOpts = getContentTypeOptions(contentTypes)
        const nameSingular = contentTypeOpts.name[Plurality.SINGULAR]
        const sameIdentifier = getIdentifier(nextProps) === getIdentifier(this.props);
        const hasDispatchedAndReceivedEntity = hasDispatchedRequestAction && !!this.props[nameSingular];

        return hasDispatchedAndReceivedEntity || (!hasDispatchedRequestAction && sameIdentifier);
      }

      componentWillUpdate () {
        hasDispatchedRequestAction = false
        hasWarnedNoEntity = false
      }
    }

    PepperoniComponentWrapper.__pepperoni = true

    PepperoniComponentWrapper.fetchData = (renderProps, store) => {
      const { contentTypes } = store.wordpress.config

      const subject = getIdentifier(renderProps)
      const contentTypeOpts = getContentTypeOptions(contentTypes)
      const contentType = contentTypeOpts.name.canonical

      return [[fetchResource, { contentType, subject }]]
    }

    return connect(mapStateToProps)(PepperoniComponentWrapper)
  }
}

@@ -5165,16 +5165,19 @@
 %7D%0A%0A     
+ //
  shouldC
@@ -5202,32 +5202,35 @@
 xtProps) %7B%0A     
+ //
    const %7B conte
@@ -5261,38 +5261,49 @@
 ordpress.config%0A
+      //
 %0A     
+ //
    const content
@@ -5346,32 +5346,35 @@
 tentTypes)%0A     
+ //
    const nameSin
@@ -5419,32 +5419,35 @@
 .SINGULAR%5D%0A     
+ //
    const sameIde
@@ -5513,24 +5513,27 @@
 rops);%0A     
+ //
    const has
@@ -5620,22 +5620,33 @@
 gular%5D;%0A
+      //
 %0A     
+ //
    retur
@@ -5731,24 +5731,27 @@
 fier);%0A     
+ //
  %7D%0A%0A      co
@@ -5769,20 +5769,150 @@
 Update (
+nextProps
 ) %7B%0A
+        const sameIdentifier = getIdentifier(nextProps) === getIdentifier(this.props);%0A%0A        if (!sameIdentifier) %7B%0A  
         
@@ -5946,32 +5946,34 @@
 = false%0A        
+ 
+ 
 hasWarnedNoEntit
@@ -5978,24 +5978,34 @@
 ity = false%0A
+        %7D%0A
       %7D%0A    

Remove shouldComponentUpdate;