import type { GQtyClient, ResolveOptions, SubscriptionsClient } from 'gqty';
import type { GeneratedSchema } from './gqty';

export type ResolveOptionsWithSession<T> = ResolveOptions<T>;

type Client = GQtyClient<GeneratedSchema>;
export function wrapGraphQLClient(
  getClient: () => Client,
  subscriptionsClient?: SubscriptionsClient
) {
  return {
    get client() {
      return getClient();
    },
    subscriptionsClient,
    async mutate<T = unknown>(
      resolved: (sub: Client['mutation']) => T,
      options?: ResolveOptionsWithSession<T>
    ) {
      if (!options) options = {};
      if (options.noCache === undefined) options.noCache = true;

      const client = getClient();
      const result = await client.resolved(
        () => resolved(client.mutation),
        options
      );
      return result;
    },
    async query<T = unknown>(
      resolved: (sub: Client['query']) => T,
      options?: ResolveOptionsWithSession<T>
    ) {
      const client = getClient();
      const result = await client.resolved(
        () => resolved(client.query),
        options
      );
      return result;
    },
    subscribe<T = unknown>(
      resolved: (sub: Client['subscription']) => T,
      onData: (data: T, unsubscribeFn: () => Promise<void>) => void,
      onError?: (err: Error, unsubscribeFn: () => Promise<void>) => void
    ) {
      if (!subscriptionsClient) throw new Error('Subscriptions not enabled');
      let unsub: undefined | (() => Promise<void>);

      const stop = async () => {
        if (!unsub) return;
        await unsub().catch(() => undefined);
      };

      const client = getClient();
      client.resolved(() => resolved(client.subscription), {
        onSubscription(event) {
          if (event.unsubscribe) unsub = event.unsubscribe;

          if (event.type === 'data') onData(event.data, stop);
          else if (event.type === 'with-errors' && onError)
            onError(event.error, stop);
        },
      });
      return stop;
    },
  };
}
