import { RenderContext } from '../common/RenderContext';
import { fieldComment, typeComment } from '../common/comment';
import { sortKeys } from '../common/support';
import { toArgsString } from '../common/toArgsString';
import { requestTypeName } from './requestTypeName';
import {
  GraphQLInterfaceType,
  GraphQLObjectType,
  getNamedType,
  isEnumType,
  isInterfaceType,
  isScalarType,
} from 'graphql';

const INDENTATION = '    ';

export function objectType(type: GraphQLObjectType | GraphQLInterfaceType, ctx: RenderContext) {
  let fields = type.getFields();

  if (ctx.config?.sortProperties) {
    fields = sortKeys(fields);
  }

  let fieldStrings = Object.keys(fields).map((fieldName) => {
    const field = fields[fieldName];

    const types: string[] = [];
    const resolvedType = getNamedType(field.type);
    const resolvable = !(isEnumType(resolvedType) || isScalarType(resolvedType));
    const argsPresent = field.args.length > 0;
    const argsString = toArgsString(field);
    const argsOptional = !argsString.match(/[^?]:/);

    if (argsPresent) {
      if (resolvable) {
        types.push(`[${argsString},${requestTypeName(resolvedType)}]`);
      } else {
        types.push(`[${argsString}]`);
      }
    }

    if (!argsPresent || argsOptional) {
      if (resolvable) {
        types.push(`${requestTypeName(resolvedType)}`);
      } else {
        types.push('boolean | number');
      }
    }

    return `${fieldComment(field)}${field.name}?: ${types.join(' | ')}`;
  });

  if (isInterfaceType(type) && ctx.schema) {
    let interfaceProperties = ctx.schema.getPossibleTypes(type).map((t) => `on_${t.name}?: ${requestTypeName(t)}`);
    if (ctx.config?.sortProperties) {
      interfaceProperties = interfaceProperties.sort();
    }
    fieldStrings = fieldStrings.concat(interfaceProperties);
  }

  fieldStrings.push('__typename?: boolean | number');
  fieldStrings.push('__scalar?: boolean | number');

  // add indentation
  fieldStrings = fieldStrings.map((x) =>
    x
      .split('\n')
      .filter(Boolean)
      .map((l) => INDENTATION + l)
      .join('\n'),
  );

  ctx.addCodeBlock(`${typeComment(type)}export interface ${requestTypeName(type)}{\n${fieldStrings.join('\n')}\n}`);
}
