import { computed, inject, toValue, watch, type ComputedRef, type MaybeRefOrGetter, type Ref, } from 'vue' import type { SelectOption } from '../types' import { APP_EXPERIMENT_KEY } from './useAppExperiment' import { extractSampleNamesFromDesignData, extractSampleOptionsFromDesignData, unwrapExperimentDesignData, type ExtractExperimentSamplesOptions, } from './experimentDesignData' import { parseExperimentId } from './platformContextHelpers' import { useExperimentData } from './useExperimentData' export type ExperimentIdSource = MaybeRefOrGetter export type ExperimentDesignDataSource = MaybeRefOrGetter | null | undefined> export interface UseExperimentSamplesOptions extends ExtractExperimentSamplesOptions { /** Experiment id to load from `/experiments/{id}/data`; defaults to provided AppExperiment context. */ experimentId?: ExperimentIdSource /** Pre-fetched design_data or full platform design-data response. */ designData?: ExperimentDesignDataSource /** API base URL override for platform calls. */ apiBaseUrl?: string /** Auto-load when an experiment id becomes available. Defaults to true. */ immediate?: boolean /** Enable or disable automatic loading while keeping derived samples reactive. Defaults to true. */ enabled?: MaybeRefOrGetter /** Use `useAppExperiment` provided state when `experimentId` is not set. Defaults to true. */ syncAppExperiment?: boolean } export interface UseExperimentSamplesReturn { /** Experiment id currently used for automatic sample loading. */ experimentId: ComputedRef /** Raw `/experiments/{id}/data` response when loaded through the platform API. */ data: Ref | null> /** Plugin-defined design_data payload from props or platform response. */ designData: ComputedRef | null> /** SampleSelector-compatible string values extracted from design_data. */ samples: ComputedRef /** Select/dropdown-friendly sample options extracted from design_data. */ sampleOptions: ComputedRef[]> isLoading: Ref error: Ref lastLoadedAt: Ref /** Fetch samples for a specific experiment id, defaulting to the resolved id. */ fetch: (experimentId?: number | string | null) => Promise | null> /** Refetch the current experiment id. */ refresh: () => Promise /** Clear fetched platform data. */ clear: () => void } /** Loads experiment design_data and derives samples for SampleSelector-style UIs. */ export function useExperimentSamples( options: UseExperimentSamplesOptions = {}, ): UseExperimentSamplesReturn { const appExperiment = options.syncAppExperiment === false ? undefined : inject(APP_EXPERIMENT_KEY, undefined) const experimentData = useExperimentData({ apiBaseUrl: options.apiBaseUrl }) const experimentId = computed(() => { const explicitId = parseExperimentId(toValue(options.experimentId)) return explicitId ?? parseExperimentId(appExperiment?.experimentId.value) }) const explicitDesignData = computed | null>(() => unwrapExperimentDesignData(toValue(options.designData) ?? null), ) const designData = computed | null>(() => explicitDesignData.value ?? experimentData.designData.value, ) const extractOptions = computed(() => ({ includeControls: options.includeControls, })) const samples = computed(() => extractSampleNamesFromDesignData(designData.value, extractOptions.value), ) const sampleOptions = computed[]>(() => extractSampleOptionsFromDesignData(designData.value, extractOptions.value), ) const enabled = computed(() => toValue(options.enabled) ?? true) async function fetchSamples( id: number | string | null | undefined = experimentId.value, ): Promise | null> { const parsedId = parseExperimentId(id) if (parsedId === undefined) { experimentData.clear() return null } return experimentData.fetch(parsedId) } watch( () => ({ designData: explicitDesignData.value, enabled: enabled.value, experimentId: experimentId.value, }), ({ designData: currentDesignData, enabled: isEnabled, experimentId: currentExperimentId }) => { if (options.immediate === false || !isEnabled) return if (currentDesignData) { experimentData.clear() return } if (currentExperimentId === undefined) { experimentData.clear() return } void experimentData.fetch(currentExperimentId) }, { immediate: options.immediate !== false }, ) return { experimentId, data: experimentData.data, designData, samples, sampleOptions, isLoading: experimentData.isLoading, error: experimentData.error, lastLoadedAt: experimentData.lastLoadedAt, fetch: fetchSamples, refresh: experimentData.refresh, clear: experimentData.clear, } }