import { Config, Plugin, Refs } from 'pretty-format';
import { PlanNode, QueryPlan } from '../QueryPlan';

export default {
  test(value: any) {
    return value && value.kind === 'QueryPlan';
  },

  serialize(
    queryPlan: QueryPlan,
    config: Config,
    indentation: string,
    depth: number,
    refs: Refs,
    printer: any,
  ): string {
    return (
      'QueryPlan {' +
      printNodes(
        queryPlan.node ? [queryPlan.node] : undefined,
        config,
        indentation,
        depth,
        refs,
        printer,
      ) +
      '}'
    );
  },
} as Plugin;

function printNode(
  node: PlanNode,
  config: Config,
  indentation: string,
  depth: number,
  refs: Refs,
  printer: any,
): string {
  let result = '';

  const indentationNext = indentation + config.indent;

  switch (node.kind) {
    case 'Fetch':
      result +=
        `Fetch(service: "${node.serviceName}")` +
        ' {' +
        config.spacingOuter +
        indentationNext +
        (node.requires
          ? printer(
              node.requires,
              config,
              indentationNext,
              depth,
              refs,
              printer,
            ) +
            ' =>' +
            config.spacingOuter +
            indentationNext
          : '') +
        printer(
          node.selectionSet,
          config,
          indentationNext,
          depth,
          refs,
          printer,
        ) +
        config.spacingOuter +
        indentation +
        (node.internalFragments.size > 0
          ? '  ' +
            Array.from(node.internalFragments)
              .map(fragment =>
                printer(
                  fragment,
                  config,
                  indentationNext,
                  depth,
                  refs,
                  printer,
                ),
              )
              .join(`\n${indentationNext}`) +
            config.spacingOuter +
            indentation
          : '') +
        '}';
      break;
    case 'Flatten':
      result += `Flatten(path: "${node.path.join('.')}")`;
      break;
    default:
      result += node.kind;
  }

  const nodes =
    'nodes' in node ? node.nodes : 'node' in node ? [node.node] : [];

  if (nodes.length > 0) {
    result +=
      ' {' + printNodes(nodes, config, indentation, depth, refs, printer) + '}';
  }

  return result;
}

function printNodes(
  nodes: PlanNode[] | undefined,
  config: Config,
  indentation: string,
  depth: number,
  refs: Refs,
  printer: any,
): string {
  let result = '';

  if (nodes && nodes.length > 0) {
    result += config.spacingOuter;

    const indentationNext = indentation + config.indent;
    for (let i = 0; i < nodes.length; i++) {
      const node = nodes[i];
      if (!node) continue;

      result +=
        indentationNext +
        printNode(node, config, indentationNext, depth, refs, printer);

      if (i < nodes.length - 1) {
        result += ',' + config.spacingInner;
      } else if (!config.min) {
        result += ',';
      }
    }

    result += config.spacingOuter + indentation;
  }

  return result;
}
