import { IconButton } from '@mui/material'
import { PercentOutlined } from '@mui/icons-material'
import { useCallback, useEffect, useRef } from 'react'
import { widgetStoreActions } from '../../stores/widget-store'
import { useWidgetSelector } from '../../stores/use-widget-selector'
import type { RelativeDataProps, RelativeDataState } from './types'
import { actionButtonStyles } from '../shared/styles'
import { Tooltip } from '../../../components'
import { calculateTotal, toRelativeData } from './utils'
import type { EchartWidgetData } from '../../../widgets/echart'

export const RELATIVE_DATA_TOOL_ID = 'relative-data'
export const RELATIVE_DATA_CONFIG_TOOL_ID = 'relative-data-config'

/**
 * Widget action to toggle between relative (percentage) and absolute data display.
 *
 * Registers two transformation tools in the widget pipeline when mounted:
 * - A data tool that converts values to percentages (enabled/disabled via store)
 * - A config tool that is **always enabled** and reads `isRelative` from the store
 *   to decide whether to apply the percentage formatter or restore the original one.
 *   The config tool must always participate because the original formatter may have
 *   been set via `setWidget` (not in the base config), so disabling the tool would
 *   leave the percentage formatter stuck on the widget.
 *
 * @example
 * ```tsx
 * <RelativeData
 *   id="my-widget"
 *   order={20}
 *   defaultIsRelative={false}
 * />
 * ```
 */
export function RelativeData({
  id,
  order = 10,
  defaultIsRelative = false,
  labels,
  Icon,
  IconButtonProps,
}: RelativeDataProps) {
  const percentFormatterRef = useRef<((value: number) => string) | undefined>(
    undefined,
  )

  // Read isRelative from widget store root — single source of truth
  const { isRelative } = useWidgetSelector(id, (w) => ({
    isRelative:
      (w as RelativeDataState | undefined)?.isRelative ?? defaultIsRelative,
  }))

  // Initialize store with default value on mount.
  // When defaultIsRelative=true, capture originals so toggling OFF can restore them.
  useEffect(() => {
    const current = widgetStoreActions.getWidget<RelativeDataState>(id)
    if (current?.isRelative === undefined) {
      if (defaultIsRelative) {
        widgetStoreActions.setWidget(id, {
          isRelative: true,
          originalFormatter: current?.formatter,
          originalMax: (current as unknown as Record<string, number>)?.max,
        })
      } else {
        widgetStoreActions.setWidget(id, { isRelative: defaultIsRelative })
      }
    }
  }, [id, defaultIsRelative])

  // Register data tool once — fn has no closure deps
  useEffect(() => {
    widgetStoreActions.registerTool(id, {
      id: RELATIVE_DATA_TOOL_ID,
      order,
      enabled: defaultIsRelative,
      fn: (data) => {
        const echartData = data as EchartWidgetData
        const total = calculateTotal(echartData)
        return toRelativeData(echartData, total)
      },
    })

    return () => widgetStoreActions.unregisterTool(id, RELATIVE_DATA_TOOL_ID)
  }, [id, order, defaultIsRelative])

  // Sync data tool enabled — lightweight, no re-registration
  useEffect(() => {
    widgetStoreActions.setToolEnabled(id, RELATIVE_DATA_TOOL_ID, isRelative)
  }, [id, isRelative])

  // Register config tool — ALWAYS enabled.
  // Reads isRelative, originalFormatter, originalMax from the store at execution time:
  // - isRelative=true  → applies percentage formatter and max=100
  // - isRelative=false → restores original formatter/max from store
  useEffect(() => {
    widgetStoreActions.registerTool(id, {
      id: RELATIVE_DATA_CONFIG_TOOL_ID,
      type: 'config',
      order,
      enabled: true,
      fn: (currentConfig: unknown) => {
        const config = currentConfig as Record<string, unknown>
        const widget = widgetStoreActions.getWidget<RelativeDataState>(id)

        const hasSourceData =
          widget?.sourceData != null &&
          !(Array.isArray(widget.sourceData) && widget.sourceData.length === 0)

        if (widget?.isRelative) {
          // Don't apply percentage formatter when there's no source data
          if (!hasSourceData) return config

          if (!percentFormatterRef.current) {
            const locale = widget?.locale
            percentFormatterRef.current = (value: number) =>
              new Intl.NumberFormat(locale, {
                style: 'percent',
                minimumFractionDigits: 1,
                maximumFractionDigits: 1,
              }).format(value / 100)
          }
          return { ...config, formatter: percentFormatterRef.current, max: 100 }
        }

        // Not in relative mode — restore originals from store if captured.
        // Use `in` because originalFormatter may have been captured as undefined
        // (widget had no formatter before toggling relative on).
        percentFormatterRef.current = undefined
        if (widget != null && 'originalFormatter' in widget) {
          return {
            ...config,
            formatter: widget.originalFormatter,
            max: widget.originalMax,
          }
        }
        return config
      },
    })
    return () => {
      // Restore original formatter/max if unmounting while in relative mode
      const widget = widgetStoreActions.getWidget<RelativeDataState>(id)
      if (widget?.isRelative && 'originalFormatter' in widget) {
        widgetStoreActions.setWidget(id, {
          formatter: widget.originalFormatter,
          max: widget.originalMax,
        })
      }
      percentFormatterRef.current = undefined
      widgetStoreActions.unregisterTool(id, RELATIVE_DATA_CONFIG_TOOL_ID)
    }
  }, [id, order, defaultIsRelative])

  const handleToggle = useCallback(() => {
    const newIsRelative = !isRelative
    percentFormatterRef.current = undefined

    if (newIsRelative) {
      // Capture current formatter/max in store before switching to relative
      const widget = widgetStoreActions.getWidget(id)
      widgetStoreActions.setWidget(id, {
        isRelative: true,
        originalFormatter: widget?.formatter,
        originalMax: (widget as unknown as Record<string, unknown>)?.max,
      })
    } else {
      widgetStoreActions.setWidget(id, { isRelative: false })
    }
  }, [isRelative, id])

  const tooltipLabel = isRelative
    ? (labels?.absolute ?? 'Show absolute values')
    : (labels?.relative ?? 'Show relative values')

  return (
    <Tooltip title={tooltipLabel}>
      <IconButton
        size='small'
        aria-label={labels?.ariaLabel ?? tooltipLabel}
        onClick={handleToggle}
        sx={actionButtonStyles.trigger}
        data-active={isRelative}
        {...IconButtonProps}
      >
        {Icon ?? <PercentOutlined />}
      </IconButton>
    </Tooltip>
  )
}
