import zoid from '@graphcms/zoid/lib/zoid.js';

import { ExtensionDialogProps, reservedExtensionProps } from './base';
export { ExtensionDialogProps, reservedExtensionProps } from './base';
import type { FieldExtensionProps } from './field';
import type { FormSidebarExtensionProps } from './formSidebar';
export * from './base';
export type {
  FormState,
  FieldState,
  FieldSubscription,
  Subscriber,
  FormSubscription,
  Form,
} from './type-helpers/form';

export type {
  VisibilityTypes,
  SetFieldsVisibility,
  VisibilityMap,
} from './type-helpers/visibility';

export type { FieldExtensionProps } from './field';
export type { AppProps } from './app';
export type { FormSidebarExtensionProps } from './formSidebar';
export { FieldExtensionFeature, FieldExtensionType } from './field';
export * from './type-helpers/appInstallation';

export type ExtensionProps =
  | FieldExtensionProps
  | FormSidebarExtensionProps
  | ExtensionDialogProps;

type Xprops = {
  onProps: (props: Record<string, any>) => void;
  onConnected: (uid: string) => Promise<unknown>;
  resize: (size: {
    height: 'auto' | 'full' | number;
    width: 'auto' | '100%' | number;
  }) => unknown;
} & ExtensionProps;

declare global {
  interface Window {
    xprops: Xprops;
  }
}

function handleProps(props: any) {
  const transformedProps: any = {};
  Object.keys(props).forEach((key: string) => {
    // do not pass down zoid props to the extension
    if (reservedExtensionProps.includes(key)) return;

    // transform props that were prefixed with '_' to bypass zoid reverved props
    if (
      key.startsWith('_') &&
      reservedExtensionProps.includes(key.replace(/^_/g, ''))
    ) {
      transformedProps[key.replace(/^_/g, '')] = props[key];
    } else {
      transformedProps[key] = props[key];
    }
  });
  return transformedProps;
}

export function init({
  debug,
  onProps = () => undefined,
  uid: givenUid,
}: {
  onProps: (props: any) => unknown;
  debug?: boolean;
  uid?: string;
}) {
  return new Promise<{ status: 'ok'; props: any } | { status: 'validation' }>(
    (resolve, reject) => {
      if (
        typeof window === 'undefined' ||
        typeof window.postMessage === 'undefined'
      ) {
        return reject({
          error: 'unsupported_env',
          message:
            'Unsupported environment: Not in a browser supporting PostMessage',
        });
      }

      const uid =
        givenUid ||
        (typeof URLSearchParams !== 'undefined' &&
          new URLSearchParams(window.location.search).get('extensionUid'));

      if (!uid) {
        if (debug)
          console.error(`[UIX] no uid found in init params or extension URL`);
        return reject({
          error: 'missing_uid',
          message: 'Missing UID: no UID found in init params or extension URL',
        });
      }
      if (debug) console.info(`[UIX:${uid}] initializing with uid ${uid}`);

      zoid.create({
        tag: uid,
        url: window.location.href.toString(),
        autoResize: {
          width: false,
          height: true,
          element: 'html',
        },
        props: {
          onConnected: {
            type: 'function',
          },
        },
      });
      if (typeof window.xprops !== 'undefined') {
        const { onConnected, onProps: initialOnProps } = window.xprops;

        onConnected(uid).then((status) => {
          if (status === true) {
            const {
              onProps: onParentProps,
              onConnected: _nevermind,
              resize,
              ...extensionProps
            } = window.xprops;

            if (debug)
              console.info(`[UIX:${uid}] initial shared props`, extensionProps);
            onParentProps((p: Xprops) => {
              const {
                onProps: onParentProps,
                onConnected,
                ...newExtensionProps
              } = p;

              if (debug)
                console.info(
                  `[UIX:${uid}] new shared props`,
                  newExtensionProps
                );
              if ('isExpanded' in p && typeof p.isExpanded === 'boolean') {
                p.isExpanded
                  ? resize({ height: 'full', width: '100%' })
                  : resize({ height: 'auto', width: '100%' });
              }
              onProps(handleProps(newExtensionProps));
            });

            if (debug) console.info(`[UIX:${uid}] initialized`);
            onProps(handleProps(extensionProps));
            resolve({ status: 'ok', props: handleProps(extensionProps) });
          } else {
            if (debug)
              console.info(
                `[UIX:${uid}] sdk renderer returned status:`,
                status
              );
            resolve({ status: 'validation' });
          }
        });
      } else {
        if (debug) console.error(`[UIX] no shared props from host found`);
        reject({
          error: 'failed_communication',
          message:
            'No communication established with host, please check your URL',
        });
      }
    }
  );
}

export default { init };
