import React, {
  ComponentType,
  createElement,
  Suspense,
  SuspenseProps,
} from "react"

export type WithSuspenseProps = Pick<SuspenseProps, "fallback"> & {
  suspenseKey?: string
}

type WithSuspenseOptions = {
  ssr?: boolean
}

const isComponent = <P,>(
  component: ComponentType<P> | React.ReactNode,
): component is ComponentType<P> => {
  return typeof component === "function"
}

export const withSuspense = <P extends object>(
  WrappedComponent: ComponentType<P>,
  defaultFallback?: SuspenseProps["fallback"] | ComponentType<P>,
  { ssr = true }: WithSuspenseOptions = {},
) => {
  return function WithSuspense(props: P & WithSuspenseProps) {
    const fallback =
      props.fallback ??
      (isComponent(defaultFallback)
        ? createElement(defaultFallback, props)
        : defaultFallback) ??
      null

    if (typeof window === "undefined" && !ssr) {
      return <>{fallback}</>
    }

    return (
      <Suspense fallback={fallback} key={props.suspenseKey}>
        <WrappedComponent {...props} />
      </Suspense>
    )
  }
}
