import { Box, Typography } from '@mui/material'
import { Tooltip } from '../../../components/tooltip/tooltip'
import { ErrorOutline as ErrorIcon } from '@mui/icons-material'
import { styles } from './styles'
import { DEFAULT_LEGEND_LABELS, type LegendLabels } from '../../provider/labels'
import type { LegendRampVariable } from '../../stores'

export interface LegendRampUIProps {
  data: LegendRampVariable
  /** Override the renderer's user-facing strings. */
  labels?: Pick<LegendLabels, 'colorBasedOn' | 'colorGradient'>
}

/**
 * Horizontal position (0–100) of `avg` on the bar — linear between the first
 * and last stop values, clamped to the edges. `null` when there's no scale
 * (fewer than 2 stops or a zero-width range).
 */
function avgPercent(data: LegendRampVariable): number | null {
  const { avg, stops } = data
  if (avg == null || stops.length < 2) return null
  const v0 = stops[0]!.value
  const vn = stops[stops.length - 1]!.value
  if (v0 === vn) return null
  const pct = ((avg - v0) / (vn - v0)) * 100
  return Math.min(100, Math.max(0, pct))
}

/**
 * Color-ramp legend. `colors` and `stops` are both ordered low→high and are
 * never reversed, so step colors and their labels stay aligned.
 *
 * Continuous mode renders a gradient with the endpoint labels. Discrete mode
 * **requires `stops.length === colors.length + 1`** — stops are the boundaries
 * between color steps (including both outer edges) and each label is centered
 * on its boundary line. A discrete ramp with any other stop count is an
 * incoherent state and renders a compact inline error instead of the bar.
 *
 * @experimental This API is new and may change in a future release.
 */
export function LegendRampUI({
  data,
  labels = DEFAULT_LEGEND_LABELS,
}: LegendRampUIProps) {
  const { colors, stops, isContinuous, attribute, formatValue = String } = data
  const avgPct = avgPercent(data)

  const first = stops[0]
  const last = stops[stops.length - 1]
  const gradient = `linear-gradient(90deg, ${colors.join(', ')})`

  // Discrete ramps need one stop per color boundary (colors.length + 1).
  // `isBoundary` is therefore equivalent to "valid discrete".
  const isBoundary = !isContinuous && stops.length === colors.length + 1
  const invalidDiscrete = !isContinuous && !isBoundary

  // Boundary range for assistive tech (valid discrete only reaches here).
  const stepAriaLabel = (index: number): string =>
    `${stops[index]?.label ?? ''} – ${stops[index + 1]?.label ?? ''}`

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

  if (invalidDiscrete) {
    return (
      <Box sx={styles.container}>
        {attributeHeader}
        <Box sx={styles.error}>
          <ErrorIcon fontSize='small' />
          <Typography variant='caption' color='error'>
            {`Discrete ramp needs ${colors.length + 1} stops (one per color boundary) but got ${stops.length}.`}
          </Typography>
        </Box>
      </Box>
    )
  }

  return (
    <Box sx={styles.container}>
      {attributeHeader}

      <Box sx={styles.barWrap}>
        <Box sx={styles.bar}>
          {isContinuous ? (
            <Box
              role='img'
              aria-label={labels.colorGradient}
              sx={{ width: '100%', height: '100%', background: gradient }}
            />
          ) : (
            colors.map((color, index) => (
              <Box
                key={index}
                role='img'
                aria-label={stepAriaLabel(index)}
                sx={{ ...styles.step, backgroundColor: color }}
              />
            ))
          )}
        </Box>

        {avgPct != null && data.avg != null && (
          <Tooltip title={formatValue(data.avg)} placement='top' arrow>
            {/* Inline style: per-instance dynamic value, no emotion class churn. */}
            <Box sx={styles.avgLine} style={{ left: `${avgPct}%` }} />
          </Tooltip>
        )}
      </Box>

      {isBoundary ? (
        <Box sx={styles.boundaryLabels}>
          {stops.map((stop, index) => {
            // Interior labels center on their boundary line; the outer two
            // clamp inside the bar (first left-aligned, last right-aligned)
            // so the row never overflows.
            const isLast = index === stops.length - 1
            const transform =
              index === 0
                ? undefined
                : isLast
                  ? 'translateX(-100%)'
                  : 'translateX(-50%)'
            return (
              <Typography
                key={`${stop.label}-${index}`}
                variant='overline'
                color='text.secondary'
                sx={styles.boundaryLabel}
                style={{
                  left: `${(index / colors.length) * 100}%`,
                  transform,
                }}
              >
                {stop.label}
              </Typography>
            )
          })}
        </Box>
      ) : (
        // Continuous gradients show only the range endpoints.
        <Box sx={styles.labels}>
          <Typography variant='overline' color='text.secondary'>
            {first?.label}
          </Typography>
          <Typography variant='overline' color='text.secondary'>
            {last?.label}
          </Typography>
        </Box>
      )}
    </Box>
  )
}
