import { defineComponent, onErrorCaptured, ref, h, type Component } from "vue"
import type { WidgetBase } from "@racletteCore/store/types"
import logger from "@racletteCore/lib/logger"

/**
 * Creates an error boundary wrapper for a widget component
 */
export const createErrorBoundary = (
  originalComponent: Component,
  widget: Partial<WidgetBase>,
  errorComponent: Component,
) =>
  defineComponent({
    name: `${widget.name}WithErrorBoundary`,
    props: originalComponent.props || {},
    emits: originalComponent.emits || [],
    setup(props, context) {
      const hasError = ref(false)
      const runtimeError = ref<Error | null>(null)
      const errorCaptured = ref(false)

      onErrorCaptured((err: Error, instance, info) => {
        if (errorCaptured.value) {
          return false
        }

        logger.widgets("Runtime error captured", {
          pluginKey: widget.pluginKey,
          name: widget.name,
          uuid: widget.uuid,
          error: err.message,
          info,
        })

        errorCaptured.value = true
        hasError.value = true
        runtimeError.value = err

        context.emit?.("error-captured", {
          pluginKey: widget.pluginKey,
          widgetName: widget.name,
          error: err,
        })

        return false
      })

      return () => {
        if (hasError.value) {
          return h(errorComponent, {
            uuid: widget.uuid,
            pluginKey: widget.pluginKey,
            widgetName: widget.name,
            error: runtimeError.value,
          })
        }

        try {
          return h(originalComponent, { ...props }, context.slots)
        } catch (renderError) {
          if (!errorCaptured.value) {
            const error =
              renderError instanceof Error
                ? renderError
                : new Error(String(renderError))

            logger.widgets("Render error", {
              pluginKey: widget.pluginKey,
              name: widget.name,
              error: error.message,
            })

            errorCaptured.value = true
            hasError.value = true
            runtimeError.value = error
          }

          return h(errorComponent, {
            uuid: widget.uuid,
            pluginKey: widget.pluginKey,
            widgetName: widget.name,
            error: runtimeError.value,
          })
        }
      }
    },
  })
