import './global-types.js';
import type {
  FieldMap,
  FieldRef,
  GenericFieldRef,
  InterfaceParam,
  SchemaTypes,
} from '@pothos/core';

export const selectionShapeKey = Symbol.for('Pothos.federationSelectionKey');

export type EntityObjectFieldsShape<Types extends SchemaTypes, Shape, Fields extends FieldMap> = (
  t: PothosSchemaTypes.FieldBuilder<Types, Shape, 'EntityObject'>,
) => Fields;

export type ShapeFromField<T extends GenericFieldRef> = T extends FieldRef<infer U> ? U : never;

export type ExternalEntityOptions<
  Types extends SchemaTypes,
  Shape extends object,
  Interfaces extends InterfaceParam<Types>[],
> = Omit<
  | PothosSchemaTypes.ObjectTypeOptions<Types, Shape>
  | PothosSchemaTypes.ObjectTypeWithInterfaceOptions<Types, Shape, Interfaces>,
  'fields'
> & {
  externalFields?: ExternalFieldsShape<Types, {}>;
  fields?: ExtendedEntityFieldsShape<Types, Shape>;
};

export type ExtendedEntityFieldsShape<Types extends SchemaTypes, Shape> = (
  t: PothosSchemaTypes.FieldBuilder<Types, Shape, 'ExtendedEntity'>,
) => FieldMap;

export type ExternalFieldsShape<Types extends SchemaTypes, Fields extends FieldMap> = (
  t: PothosSchemaTypes.RootFieldBuilder<Types, unknown, 'ExternalEntity'>,
) => Fields;

export type ShapeFromExternalFields<Fields extends FieldMap> = {
  [K in keyof Fields]: ShapeFromField<Fields[K]> extends Date | boolean | number | string
    ? ShapeFromField<Fields[K]>
    : unknown;
};

type NonNullableNonList<T> =
  NonNullable<T> extends Array<infer U> ? NonNullable<U> : NonNullable<T>;

export type SelectionFromShape<Shape, Space extends string = ''> =
  {} extends Required<Shape>
    ? ''
    : {
        [K in keyof Shape]-?: Omit<Shape, K> extends infer R
          ? NonNullableNonList<Shape[K]> extends object
            ? `${Space}${K & string} { ${NonNullableNonList<Shape[K]> extends infer T
                ? SelectionFromShape<T>
                : never} }${SelectionFromShape<R, ' '>}`
            : `${Space}${K & string}${SelectionFromShape<R, ' '>}`
          : never;
      }[keyof Shape];

export interface Selection<Shape extends object> {
  selection: string;
  [selectionShapeKey]: Shape;
}

export interface KeyDirective<Shape extends object, Resolvable extends boolean = true>
  extends Selection<Shape> {
  resolvable?: Resolvable;
}
