import { useEffect, useRef } from 'react'
import type { WidgetLoaderProps } from './types'
import { useWidgetStore, widgetStoreActions } from '../stores/widget-store'
import type { WrapperState } from '../wrapper'

/**
 * Foundation component for all widgets that manages state initialization, synchronization with the widget store, and data transformation pipeline execution.
 *
 * @remarks
 * The WidgetLoader is stateless -- it manages data flow but does not render any UI. Always wrap it with WidgetWrapper for consistent widget appearance.
 *
 * @example
 * ```tsx
 * <WidgetLoader
 *   id="sales-chart"
 *   type="bar"
 *   data={data}
 *   isLoading={false}
 *   isFetching={false}
 *   config={barConfig({ data })}
 * >
 *   <WidgetWrapper id="sales-chart" title="Sales">
 *     <Echart id="sales-chart" />
 *   </WidgetWrapper>
 * </WidgetLoader>
 * ```
 */
export function WidgetLoader<T extends object = Record<string, unknown>>(
  props: WidgetLoaderProps<T>,
) {
  // Subscribe only to this widget's registeredTools via Zustand selector.
  // The selector returns a stable reference when other widgets change,
  // avoiding unnecessary re-evaluations of Effect 4.
  const registeredTools = useWidgetStore(
    (state) => state.widgets[props.id]?.registeredTools,
  )

  const dataRef = useRef(props.data)
  const configRef = useRef(props.config)
  const isMountedRef = useRef(false)

  useEffect(() => {
    dataRef.current = props.data
    configRef.current = props.config
  })

  // Effect 1: Metadata — type, loading, and error states in a single setWidget call.
  // Merged to reduce store updates from 2 to 1 per widget during initialization.
  useEffect(() => {
    widgetStoreActions.setWidget<WrapperState>(props.id, {
      type: props.type,
      isLoading: props.isLoading ?? false,
      isFetching: props.isFetching ?? false,
      error: props.error,
    })
  }, [props.id, props.type, props.isLoading, props.isFetching, props.error])

  // Effect 2: Config updates — run through config pipeline
  useEffect(() => {
    if (props.config) {
      void widgetStoreActions.executeConfigPipeline(props.id, props.config)
    }
  }, [props.id, props.config])

  // Effect 3: Execute tool pipeline when props.data changes
  useEffect(() => {
    void widgetStoreActions.executeToolPipeline(props.id, props.data)
  }, [props.id, props.data])

  // Effect 4: Re-execute pipelines when registered tools change.
  // Uses requestAnimationFrame to coalesce rapid successive registeredTools
  // changes (e.g., 6 action components each calling registerTool on mount)
  // into a single pipeline execution instead of 6 pairs.
  useEffect(() => {
    if (!isMountedRef.current) {
      isMountedRef.current = true
      return
    }

    const rafId = requestAnimationFrame(() => {
      const { executeToolPipeline, executeConfigPipeline } = widgetStoreActions
      void executeToolPipeline(props.id, dataRef.current)
      if (configRef.current) {
        void executeConfigPipeline(props.id, configRef.current)
      }
    })

    return () => cancelAnimationFrame(rafId)
  }, [registeredTools, props.id])

  return props.children
}
