import { useId } from 'react'
import type {
  LegendFillPattern,
  LegendStrokeStyle,
  LegendSwatchShape,
} from '../../stores'

export interface LegendSwatchProps {
  /** Shape to render. Default `'circle'`. */
  shape?: LegendSwatchShape
  /** CSS color string (hex / rgb / named). */
  color: string
  /** Paint the outline/border instead of the fill (square/circle only). */
  isStrokeColor?: boolean
  /** Fill pattern when filled. Default `'solid'`. */
  fillPattern?: LegendFillPattern
  /** Border style (stroked square/circle) or line style (`shape: 'line'`). Default `'solid'`. */
  strokeStyle?: LegendStrokeStyle
  /** Primary dimension in px (square side / circle diameter / line length). Default 12. */
  size?: number
}

/** Default swatch dimension in px — shared with the category renderer. */
export const DEFAULT_SWATCH_SIZE = 12

const STROKE_WIDTH = 1.5
const LINE_WIDTH = 2
const HAIRLINE = 1

/** Dash array for stroked square/circle borders. */
function borderDash(style: LegendStrokeStyle): {
  dash?: string
  round?: boolean
} {
  switch (style) {
    case 'dashed':
      return { dash: '3 2' }
    case 'dotted':
      return { dash: `0.1 ${STROKE_WIDTH * 2}`, round: true }
    // `dash-dot` is line-only and `corners` is handled separately: both
    // degrade to a solid border here.
    default:
      return {}
  }
}

/** Dash array for `shape: 'line'`. */
function lineDash(style: LegendStrokeStyle): {
  dash?: string
  round?: boolean
} {
  switch (style) {
    case 'dashed':
      return { dash: '4 3' }
    case 'dotted':
      return { dash: `0.1 ${LINE_WIDTH * 2}`, round: true }
    case 'dash-dot':
      return { dash: '5 2 0.1 2', round: true }
    // `corners` is square-only → solid line.
    default:
      return {}
  }
}

/** SVG children for a fill `<pattern>` tile, drawn in `color`. */
function patternTile(pattern: LegendFillPattern, color: string) {
  const line = (d: string) => (
    <path d={d} stroke={color} strokeWidth={1.2} fill='none' />
  )
  switch (pattern) {
    case 'horizontal':
      return { size: 4, content: line('M0 2 H4') }
    case 'vertical':
      return { size: 4, content: line('M2 0 V4') }
    case 'diagonal':
      // "/" with wrap segments for a seamless tile.
      return { size: 4, content: line('M0 4 L4 0 M-1 1 L1 -1 M3 5 L5 3') }
    case 'diagonal-alt':
      // "\"
      return { size: 4, content: line('M0 0 L4 4 M-1 3 L1 5 M3 -1 L5 1') }
    case 'cross':
      return {
        size: 4,
        content: line(
          'M0 4 L4 0 M-1 1 L1 -1 M3 5 L5 3 M0 0 L4 4 M-1 3 L1 5 M3 -1 L5 1',
        ),
      }
    case 'dots':
      return { size: 4, content: <circle cx={2} cy={2} r={0.9} fill={color} /> }
    case 'grid':
      return {
        size: 3,
        content: <circle cx={1.5} cy={1.5} r={0.7} fill={color} />,
      }
    default:
      return null
  }
}

/**
 * A single legend swatch rendered as inline SVG: a square, circle, or line,
 * painted as a solid/patterned fill or a styled outline. Pure and theme-free —
 * `color` is a raw CSS color. Decorative (`aria-hidden`); the adjacent label
 * carries the accessible text.
 *
 * @experimental This API is new and may change in a future release.
 */
export function LegendSwatch({
  shape = 'circle',
  color,
  isStrokeColor = false,
  fillPattern = 'solid',
  strokeStyle = 'solid',
  size = DEFAULT_SWATCH_SIZE,
}: LegendSwatchProps) {
  const patternId = useId()

  // ── Line ──────────────────────────────────────────────────────────────────
  if (shape === 'line') {
    const { dash, round } = lineDash(strokeStyle)
    return (
      <svg
        width={size}
        height={size}
        viewBox={`0 0 ${size} ${size}`}
        aria-hidden
        data-shape='line'
        style={{ display: 'block', overflow: 'visible' }}
      >
        <line
          x1={0}
          y1={size / 2}
          x2={size}
          y2={size / 2}
          stroke={color}
          strokeWidth={LINE_WIDTH}
          strokeDasharray={dash}
          strokeLinecap={round ? 'round' : 'butt'}
        />
      </svg>
    )
  }

  // ── Square / circle ────────────────────────────────────────────────────────
  const inset = isStrokeColor ? STROKE_WIDTH / 2 : 0
  const usePattern = !isStrokeColor && fillPattern !== 'solid'
  const tile = usePattern ? patternTile(fillPattern, color) : null

  // corner-marks only apply to a stroked square.
  const isCorners =
    isStrokeColor && strokeStyle === 'corners' && shape === 'square'

  const { dash, round } = isStrokeColor
    ? borderDash(strokeStyle)
    : { dash: undefined, round: false }

  const commonStroke = {
    stroke: color,
    strokeWidth: isStrokeColor ? STROKE_WIDTH : HAIRLINE,
    strokeDasharray: isStrokeColor ? dash : undefined,
    strokeLinecap: round ? ('round' as const) : ('butt' as const),
  }

  const fill = isStrokeColor
    ? 'none'
    : usePattern
      ? `url(#${patternId})`
      : color

  return (
    <svg
      width={size}
      height={size}
      viewBox={`0 0 ${size} ${size}`}
      aria-hidden
      data-shape={shape}
      style={{ display: 'block' }}
    >
      {tile && (
        <defs>
          <pattern
            id={patternId}
            patternUnits='userSpaceOnUse'
            width={tile.size}
            height={tile.size}
          >
            {tile.content}
          </pattern>
        </defs>
      )}

      {isCorners ? (
        <CornerMarks size={size} color={color} />
      ) : shape === 'circle' ? (
        <circle
          cx={size / 2}
          cy={size / 2}
          r={size / 2 - inset}
          fill={fill}
          {...commonStroke}
          // A solid-color filled circle needs no border.
          stroke={isStrokeColor || usePattern ? color : 'none'}
        />
      ) : (
        <rect
          x={inset}
          y={inset}
          width={size - inset * 2}
          height={size - inset * 2}
          fill={fill}
          {...commonStroke}
          stroke={isStrokeColor || usePattern ? color : 'none'}
        />
      )}
    </svg>
  )
}

/** Four L-shaped corner brackets for the `corners` border style (square). */
function CornerMarks({ size, color }: { size: number; color: string }) {
  const arm = Math.max(2, size * 0.3)
  const o = STROKE_WIDTH / 2
  const max = size - o
  const props = {
    stroke: color,
    strokeWidth: STROKE_WIDTH,
    fill: 'none',
  }
  return (
    <>
      <path d={`M${o} ${o + arm} V${o} H${o + arm}`} {...props} />
      <path d={`M${max - arm} ${o} H${max} V${o + arm}`} {...props} />
      <path d={`M${max} ${max - arm} V${max} H${max - arm}`} {...props} />
      <path d={`M${o + arm} ${max} H${o} V${max - arm}`} {...props} />
    </>
  )
}
