/**
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 *
 * @flow strict-local
 * @format
 * @oncall relay
 */

'use strict';

const {
  createContainer: createPaginationContainer,
} = require('../ReactRelayPaginationContainer');
const React = require('react');
const {graphql} = require('relay-runtime');

/**
 * Verifies that normal prop type checking, as well as the methods proxying Relay does, is
 * type-checked correctly on Relay components.
 */

/* $FlowFixMe(>=0.53.0) This comment suppresses an error
 * when upgrading Flow's support for React. Common errors found when upgrading
 * Flow's React support are documented at https://fburl.com/eq7bs81w */
class FooComponent extends React.Component {
  props: {
    optionalProp?: {foo: number, ...},
    defaultProp: string,
    requiredProp: string,
    ...
  };
  static defaultProps: {defaultProp: string} = {
    defaultProp: 'default',
  };
  getNum(): number {
    return 42;
  }
  render(): React.Node {
    const reqLen = this.props.requiredProp.length;
    const optionalProp = this.props.optionalProp;

    /** $FlowExpectedError: `optionalProp` might be null **/
    const optionalFoo = this.props.optionalProp.foo;

    /** $FlowExpectedError: there is no prop `missingProp` **/
    const missing = this.props.missingProp;

    const defLen = this.props.defaultProp.length; // always a valid string, so no error
    return (
      <div>{reqLen && optionalProp && optionalFoo && missing && defLen}</div>
    );
  }
}
// Note that we must reassign to a new identifier to make sure flow doesn't propogate types without
// the relay type definition doing the work.
const Foo = createPaginationContainer(
  FooComponent,
  {
    viewer: graphql`
      fragment ReactRelayPaginationContainerFlowtest_viewer on Viewer {
        account_user {
          friends(after: $cursor, first: $count)
            @connection(
              key: "ReactRelayPaginationContainerFlowtest_viewer__friends"
            ) {
            edges {
              node {
                __typename
              }
            }
          }
        }
      }
    `,
  },
  {
    direction: 'forward',
    getConnectionFromProps: props => props.viewer.account_user.friends,
    // $FlowFixMe[cannot-spread-interface]
    getFragmentVariables: (vars, totalCount) => ({
      ...vars,
      count: totalCount,
    }),
    getVariables: (props, {count, cursor}) => ({
      after: cursor,
      count,
    }),
    query: graphql`
      query ReactRelayPaginationContainerFlowtestQuery(
        $count: Int!
        $cursor: ID
      ) {
        viewer {
          ...ReactRelayPaginationContainerFlowtest_viewer
        }
      }
    `,
  },
);

module.exports = {
  checkMissingProp(): React.Node {
    /** $ShouldBeFlowExpectedError: Foo missing `requiredProp` **/
    return <Foo />;
  },
  checkMinimalProps(): React.Node {
    // All is well
    return <Foo requiredProp="foo" />;
  },
  checkWrongPropType(): React.Node {
    /** $ShouldBeFlowExpectedError: Foo1 wrong `requiredProp` type, should be string **/
    return <Foo requiredProp={17} />;
  },
  checkWrongOptionalType(): React.Node {
    /** $ShouldBeFlowExpectedError: Foo wrong `optionalProp` type, should be `{foo: string}` **/
    return <Foo optionalProp="wrongType" requiredProp="foo" />;
  },
  checkNullOptionalType(): React.Node {
    /** $ShouldBeFlowExpectedError: Foo `optionalProp` must be omitted or truthy, not null **/
    return <Foo optionalProp={null} requiredProp="foo" />;
  },
  checkWrongDefaultPropType(): React.Node {
    /** $ShouldBeFlowExpectedError: Foo wrong `defaultProp` type, should be string **/
    return <Foo defaultProp={false} requiredProp="foo" />;
  },
  checkAllPossibleProps(): React.Node {
    // All is well
    return (
      <Foo defaultProp="bar" optionalProp={{foo: 42}} requiredProp="foo" />
    );
  },
  checkMinimalPropSpread(): React.Node {
    // All is well
    const props = {requiredProp: 'foo'};
    return <Foo {...props} />;
  },
  checkMissingPropSpread(): React.Node {
    const props = {defaultProp: 'foo'};
    /** $ShouldBeFlowExpectedError: Foo missing `requiredProp` with spread **/
    return <Foo {...props} />;
  },
  checkStaticsAndMethodsProxying(): React.Node {
    /* $FlowFixMe(>=0.53.0) This comment suppresses an
     * error when upgrading Flow's support for React. Common errors found when
     * upgrading Flow's React support are documented at
     * https://fburl.com/eq7bs81w */
    class ProxyChecker extends React.PureComponent {
      _fooRef: ?FooComponent;
      getString(): string {
        const ok = this._fooRef ? this._fooRef.getNum() : 'default'; // legit

        /** $FlowExpectedError: Foo does not have `missingMethod` **/
        const bad = this._fooRef ? this._fooRef.missingMethod() : 'default';

        /** $FlowExpectedError: Foo `getNum` gives number, but `getString` assumes string  **/
        return bad ? 'not good' : ok;
      }
      render(): React.Node {
        return (
          <Foo
            componentRef={ref => {
              this._fooRef = ref;
            }}
            requiredProp="bar"
          />
        );
      }
    }
    return <ProxyChecker />;
  },
};