import type { ReactNode } from 'react';
import React from 'react';
import type { RenderingContext, PreviewPreview, PreviewModule } from '@teambit/preview';
import { PreviewAspect, PreviewRuntime } from '@teambit/preview';
import type { ComponentID } from '@teambit/component-id';

import { DocsAspect } from './docs.aspect';
import type { Docs } from './docs';

export type DocsRootProps = {
  Provider: React.ComponentType | undefined;
  componentId: string;
  docs: Docs | undefined;
  compositions?: any;
  context: RenderingContext;
};

export class DocsPreview {
  constructor(
    /**
     * preview extension.
     */
    private preview: PreviewPreview
  ) {}

  render = (
    componentId: ComponentID,
    envId: string,
    modules: PreviewModule,
    [compositionsFromParams]: [any],
    context: RenderingContext
  ) => {
    const docsModule = this.selectPreviewModel(componentId.fullName, modules);
    const compositions = compositionsFromParams || [];

    const mainModule = modules.modulesMap[envId] || modules.modulesMap.default;
    let defaultExports = mainModule.default;
    // Sometime when using ESM (package.json with type:"module") the default export is nested under "default"
    if (typeof defaultExports !== 'function' && defaultExports.default) {
      defaultExports = defaultExports.default;
    }
    // @ts-ignore Gilad - to fix.
    const isObject = !!defaultExports.apiObject;

    /**
     * for backwards compatibility - can be removed end of 2022
     */
    if (!isObject) {
      const docsPropsArray = [
        NoopProvider as React.ComponentType,
        componentId.toString(),
        docsModule as Docs,
        compositions,
        context,
      ];
      // @ts-ignore Gilad - to fix. it happens because defaultExports might be a func or an object (handled above)
      defaultExports(...docsPropsArray);
      return;
    }

    const docsProps: DocsRootProps = {
      Provider: NoopProvider as React.ComponentType,
      componentId: componentId.toString(),
      docs: docsModule as Docs,
      compositions,
      context,
    };
    // @ts-ignore Gilad - to fix. it happens because defaultExports might be a func or an object (handled above)
    defaultExports(docsProps);
  };

  selectPreviewModel(componentId: string, modules: PreviewModule) {
    const relevant = modules.componentMap[componentId];
    if (!relevant) return undefined;

    // only one doc file is supported.
    return relevant[0];
  }

  static runtime = PreviewRuntime;
  static dependencies = [PreviewAspect];

  static async provider([preview]: [PreviewPreview]) {
    const docsPreview = new DocsPreview(preview);
    preview.registerPreview({
      name: 'overview',
      render: docsPreview.render.bind(docsPreview),
      selectPreviewModel: docsPreview.selectPreviewModel.bind(docsPreview),
      include: ['compositions'],
    });

    return docsPreview;
  }
}

DocsAspect.addRuntime(DocsPreview);

function NoopProvider({ children }: { children: ReactNode }) {
  return <>{children}</>;
}
