import type { ReactNode } from 'react'
import { Box, Typography } from '@mui/material'
import { styles } from './styles'
import { DEFAULT_LEGEND_LABELS, type LegendLabels } from '../../provider/labels'
import type { LegendProportionVariable } from '../../stores'

export interface LegendProportionUIProps {
  data: LegendProportionVariable
  /** Override the renderer's user-facing strings. */
  labels?: Pick<LegendLabels, 'radiusRangeBy' | 'maxPrefix' | 'minPrefix'>
}

const MAX_DIAMETER = 72
const RATIO_FLOOR = 0.3
const MIN_STEPS = 2
const MAX_STEPS = 4

/**
 * Value breaks, largest→smallest. Explicit `stops` (≥2 entries) win, capped at
 * the 4 largest; otherwise breaks are derived evenly from `min`/`max` over
 * `steps` clamped to 2–4. Circles map 1:1 to the breaks.
 */
function resolveBreaks(data: LegendProportionVariable): number[] {
  const explicit = (data.stops ?? []).filter((n) => Number.isFinite(n))
  if (explicit.length >= MIN_STEPS) {
    return explicit.sort((a, b) => b - a).slice(0, MAX_STEPS)
  }
  const { min, max } = data
  if (min === max) return [max]
  const steps = Math.min(
    MAX_STEPS,
    Math.max(MIN_STEPS, data.steps ?? MAX_STEPS),
  )
  return Array.from(
    { length: steps },
    (_, i) => max - ((max - min) * i) / (steps - 1),
  )
}

/** Decorative nested-circle diameter for circle `i` of `n` (i=0 outermost). */
function diameterFor(i: number, n: number): number {
  if (n <= 1) return MAX_DIAMETER
  const ratio = 1 - (1 - RATIO_FLOOR) * (i / (n - 1))
  return MAX_DIAMETER * ratio
}

/**
 * Proportional-symbol legend — a stack of decorative nested circles with the
 * value breaks labelled beside them (largest = MAX, smallest = MIN). Circle
 * sizes are fixed/decorative, not value-derived, so the legend stays readable
 * across very wide ranges.
 *
 * @experimental This API is new and may change in a future release.
 */
export function LegendProportionUI({
  data,
  labels = DEFAULT_LEGEND_LABELS,
}: LegendProportionUIProps) {
  const { attribute, formatValue = String } = data
  const breaks = resolveBreaks(data)
  const n = breaks.length

  const labelFor = (value: number, i: number): ReactNode => {
    const formatted = formatValue(value)
    if (n <= 1) return formatted
    if (i === 0)
      return (
        <>
          {labels.maxPrefix}
          {formatted}
        </>
      )
    if (i === n - 1)
      return (
        <>
          {labels.minPrefix}
          {formatted}
        </>
      )
    return formatted
  }

  return (
    <Box sx={styles.root}>
      {attribute && (
        <Box sx={styles.attribute}>
          <Typography variant='overline' color='text.secondary'>
            {labels.radiusRangeBy}
          </Typography>
          <Typography variant='caption' color='text.primary'>
            {attribute}
          </Typography>
        </Box>
      )}

      <Box sx={styles.body}>
        <Box
          sx={{ ...styles.circles, width: MAX_DIAMETER, height: MAX_DIAMETER }}
        >
          {breaks.map((_, i) => {
            const diameter = diameterFor(i, n)
            return (
              <Box
                key={i}
                aria-hidden
                sx={{ ...styles.circle, width: diameter, height: diameter }}
              />
            )
          })}
        </Box>

        <Box sx={{ ...styles.labels, height: MAX_DIAMETER }}>
          {breaks.map((value, i) => (
            <Typography key={i} variant='caption' color='text.secondary'>
              {labelFor(value, i)}
            </Typography>
          ))}
        </Box>
      </Box>
    </Box>
  )
}
