/**
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow
 * @format
 */

'use strict';

const React = require('React');
const ReactRelayContext = require('./ReactRelayContext');

const assertFragmentMap = require('./assertFragmentMap');
const invariant = require('invariant');
const mapObject = require('mapObject');
const readContext = require('./readContext');

const {
  getComponentName,
  getContainerName,
} = require('./ReactRelayContainerUtils');

import type {GeneratedNodeMap} from './ReactRelayTypes';
import type {FragmentMap} from 'relay-runtime';

type ContainerCreator = (
  Component: React$ComponentType<any>,
  fragments: FragmentMap,
) => React$ComponentType<any>;

/**
 * Creates a component class whose instances adapt to the
 * `context.relay.environment` in which they are rendered and which have the
 * necessary static methods (`getFragment()` etc) to be composed within classic
 * `Relay.Containers`.
 */
function buildReactRelayContainer<TBase: React$ComponentType<*>>(
  ComponentClass: TBase,
  fragmentSpec: GeneratedNodeMap,
  createContainerWithFragments: ContainerCreator,
): TBase {
  // Sanity-check user-defined fragment input
  const containerName = getContainerName(ComponentClass);
  assertFragmentMap(getComponentName(ComponentClass), fragmentSpec);

  // Memoize a container for the last environment instance encountered
  let environment;
  let Container;
  function ContainerConstructor(props) {
    if (Container == null || props.__relayContext.environment !== environment) {
      environment = props.__relayContext.environment;
      if (__DEV__) {
        const {isRelayModernEnvironment} = require('relay-runtime');
        if (!isRelayModernEnvironment(environment)) {
          throw new Error(
            'RelayModernContainer: Can only use Relay Modern component ' +
              `${containerName} in a Relay Modern environment!`,
          );
        }
      }
      const {getFragment: getFragmentFromTag} = environment.unstable_internal;
      const fragments = mapObject(fragmentSpec, getFragmentFromTag);
      Container = createContainerWithFragments(ComponentClass, fragments);

      // Attach static lifecycle to wrapper component so React can see it.
      ContainerConstructor.getDerivedStateFromProps = (Container: any).getDerivedStateFromProps;
    }
    // $FlowFixMe
    return new Container(props);
  }
  ContainerConstructor.prototype = React.Component.prototype;

  function forwardRef(props, ref) {
    const context = readContext(ReactRelayContext);
    invariant(
      context,
      `${containerName} tried to render a context that was ` +
        `not valid this means that ${containerName} was rendered outside of a ` +
        'query renderer.',
    );

    return (
      <ContainerConstructor
        {...props}
        __relayContext={context}
        componentRef={props.componentRef || ref}
      />
    );
  }
  forwardRef.displayName = containerName;
  const ForwardContainer = React.forwardRef(forwardRef);

  if (__DEV__) {
    // Used by RelayModernTestUtils
    (ForwardContainer: any).__ComponentClass = ComponentClass;
  }

  /* $FlowFixMe(>=0.89.0 site=www,mobile,react_native_fb,oss) Suppressing errors
   * found while preparing to upgrade to 0.89.0 */
  return ForwardContainer;
}

module.exports = buildReactRelayContainer;

@@ -2567,16 +2567,68 @@
 ototype;
+%0A  ContainerConstructor.displayName = containerName;
 %0A%0A  func

set displayName for ContainerConstructor