import { useEffect, useMemo, type ComponentType } from 'react'
import { IconButton, type SvgIconProps } from '@mui/material'
import { Percent as PercentIcon } from '@mui/icons-material'
import { Tooltip } from '../../../components'
import {
  getWidgetStore,
  useSingleTransform,
  useWidget,
  useWidgetId,
} from '../../stores'
import { createPercentFormatter, toRelativeData } from './transforms'
import { DEFAULT_RELATIVE_DATA_LABELS, type RelativeDataLabels } from './labels'
import { styles } from './style'

const DATA_DESCRIPTOR = {
  id: 'relative-data',
  type: 'data' as const,
  order: 15,
}

export interface RelativeDataProps {
  initialEnabled?: boolean
  /**
   * BCP-47 locale tag forwarded to `Intl.NumberFormat`. Defaults to the
   * runtime locale.
   */
  locale?: string
  labels?: Partial<RelativeDataLabels>
  icon?: ComponentType<SvgIconProps>
  iconProps?: SvgIconProps
  /**
   * Data-side transform that rewrites the widget's series into their
   * relative (0–100) form. Defaults to {@link toRelativeData}, which
   * handles the canonical `{ name, value }[]` shape (bar / pie /
   * timeseries / category). Widgets with a different data shape pass
   * their own — `toRelativeHistogramData` for histogram's `number[]`,
   * `toRelativeScatterplotData` for scatter's `[x, y][]`.
   *
   * Should be a stable top-level reference; inline `(data) => ...`
   * literals re-register the transform every render.
   */
  transform?: (data: unknown) => unknown
}

/**
 * Toggle between absolute and relative (percentage) values.
 *
 *   - **Data side**: a single data transform ({@link toRelativeData}) registered
 *     through `useSingleTransform`. When disabled, the pipeline skips it and the
 *     derived `data` reverts to `rawData` automatically.
 *   - **Formatter side**: an effect writes directly to `state.formatter`. When
 *     enabled it installs the Intl-based percent formatter; when disabled it
 *     reads `state.rawFormatter` (the consumer-supplied original, always live)
 *     and writes it back to `formatter`. No "original" snapshot is captured —
 *     `rawFormatter` is the original.
 */
export function RelativeData({
  initialEnabled = false,
  locale,
  labels,
  icon: Icon = PercentIcon,
  iconProps,
  transform,
}: RelativeDataProps) {
  const id = useWidgetId()
  const _labels = { ...DEFAULT_RELATIVE_DATA_LABELS, ...labels }

  const { enabled, toggle } = useSingleTransform(
    id,
    DATA_DESCRIPTOR,
    transform ?? toRelativeData,
    { initialEnabled },
  )

  const percentFormatter = useMemo(
    () => createPercentFormatter(locale),
    [locale],
  )

  // Subscribe to rawFormatter so the effect re-runs and re-applies after the
  // consumer's formatter prop changes (Provider syncs the new prop to both
  // rawFormatter and formatter — re-applying restores the percent override
  // when relative is on, and tracks the new rawFormatter when off).
  const rawFormatter = useWidget(id, (s) => s.rawFormatter)

  useEffect(() => {
    getWidgetStore(id).setState({
      formatter: enabled ? percentFormatter : rawFormatter,
    })
  }, [id, enabled, percentFormatter, rawFormatter])

  const tooltip = enabled ? _labels.on : _labels.off

  return (
    <Tooltip title={tooltip}>
      <IconButton
        size='small'
        aria-label={tooltip}
        aria-pressed={enabled}
        onClick={toggle}
        sx={{ ...styles.toggle, ...(enabled && styles.toggleActive) }}
      >
        <Icon fontSize='small' {...iconProps} />
      </IconButton>
    </Tooltip>
  )
}
