import type * as React from 'react';
import { WrappedPromise } from '@assets/js-shop/Interfaces';

export type OmitNever<T> = { [K in keyof T as T[K] extends never ? never : K]: T[K] };
export type Id<T> = {} & { [P in keyof T] : T[P]};
export type OrProduceWith<T, S = T> = T | ((old: S) => T);
export type WithoutTypename<T extends { __typename?: string }> = OmitNever<Omit<T, '__typename'>>;

function objectToUrlEncodedRec(value: unknown, key?: string, acc: string[] = []) {
   if (typeof value === 'undefined') {
      return acc;
   }

   if (typeof value === 'object') {
      for (const rawKey in value) {
         if (typeof value === 'undefined') {
            continue;
         }

         const subKey = key ? `${key}[${rawKey}]` : rawKey;

         objectToUrlEncodedRec(value[rawKey as keyof typeof value], subKey, acc);
      }
   } else {
      acc.push(`${key}=${encodeURIComponent(String(value))}`);
   }

   return acc;
}

export function objectToUrlEncoded(obj: Record<string | number, unknown>): string {
   return objectToUrlEncodedRec(obj).join('&');
}

/**
 * Creates a new listener function, that then calls all the provided listeners.
 *
 * @param listeners event listeners to combine into one
 */
export function composeListeners<
   TElement extends Element,
   TEvent extends React.SyntheticEvent<TElement>
>(...listeners: (((ev: TEvent) => void)|undefined|null)[]) {
   return (ev: TEvent) => {
      for (const listener of listeners) {
         listener && listener(ev);
      }
   };
}

export function createWrappedPromise<T>(): WrappedPromise<T> {
   return function(): WrappedPromise<T>  {
      let resolve, reject;
      let isResolved = false;
      const promise = new Promise<T>((res, rej) => {
         resolve = res;
         reject = rej;
      });
      return {
         resolve: (value?: T) => {resolve(value); isResolved = true},
         reject: (reason: any) => {reject(reason); isResolved = true},
         get isResolved() { return isResolved },
         get isUnresolved() { return !isResolved },
         get promise() { return promise }
      };
   }();
}

