import {
  DocumentNode,
  DefinitionNode,
  VariableDefinitionNode,
  OperationDefinitionNode,
} from 'graphql';

const invariant = require('invariant');

export enum DocumentType {
  Query,
  Mutation,
  Subscription,
}

export interface IDocumentDefinition {
  type: DocumentType;
  name: string;
  variables: VariableDefinitionNode[];
}

// the parser is mainly a safety check for the HOC
export function parser(document: DocumentNode): IDocumentDefinition {
  // variables
  let variables, type, name;

  /*

    Saftey checks for proper usage of react-apollo

  */
  invariant((!!document && !!document.kind),
    // tslint:disable-line
    `Argument of ${document} passed to parser was not a valid GraphQL DocumentNode. You may need to use 'graphql-tag' or another method to convert your operation into a document`,
  );

  const fragments = document.definitions.filter(
    (x: DefinitionNode) => x.kind === 'FragmentDefinition',
  );

  const queries = document.definitions.filter(
    (x: DefinitionNode) => x.kind === 'OperationDefinition' && x.operation === 'query',
  );

  const mutations = document.definitions.filter(
    (x: DefinitionNode) => x.kind === 'OperationDefinition' && x.operation === 'mutation',
  );

  const subscriptions = document.definitions.filter(
    (x: DefinitionNode) => x.kind === 'OperationDefinition' && x.operation === 'subscription',
  );

  invariant(!fragments.length || (queries.length || mutations.length || subscriptions.length),
    `Passing only a fragment to 'graphql' is not yet supported. You must include a query, subscription or mutation as well`,
  );


  invariant(((queries.length + mutations.length + subscriptions.length) <= 1),
    // tslint:disable-line
    `react-apollo only supports a query, subscription, or a mutation per HOC. ${document} had ${queries.length} queries, ${subscriptions.length} subscriptions and ${mutations.length} mutations. You can use 'compose' to join multiple operation types to a component`,
  );


  type = queries.length ? DocumentType.Query : DocumentType.Mutation;
  if (!queries.length && !mutations.length) type = DocumentType.Subscription;

  const definitions = queries.length ? queries :
    (mutations.length ? mutations : subscriptions);

  invariant(definitions.length === 1,
    // tslint:disable-line
    `react-apollo only supports one defintion per HOC. ${document} had ${definitions.length} definitions. You can use 'compose' to join multiple operation types to a component`,
  );

  const definition = definitions[0] as OperationDefinitionNode;
  variables = definition.variableDefinitions || [];
  let hasName = definition.name && definition.name.kind === 'Name';
  name = hasName ? definition.name.value : 'data'; // fallback to using data if no name
  return { name, type, variables };
}
