/**
 * ColorField — the app's color input (arch 09 §5, component primitives).
 *
 * A token-styled native color swatch bound to a hex value, optionally paired
 * with a copyable/editable hex text field. The single place the `type="color"`
 * chrome reset and the hex-entry behavior live: the Theme Builder swatches
 * (`hex`) and the Chart Builder constant-colour binding (`size="sm"`, no hex)
 * both use it. Removal is the caller's concern — it differs per context (a
 * per-swatch button vs. the pill's own remove) — so this renders only the
 * input(s).
 */

import { useState } from 'react';
import styles from './ColorField.module.css';

/** True for a `#rrggbb` string — what the native picker accepts. */
function isHexColor(c: string): boolean {
  return /^#[0-9a-f]{6}$/i.test(c);
}

/** `#rrggbb` (lowercased) from loose input, or null if not six hex digits. */
function normalizeHexColor(raw: string): string | null {
  const v = raw.trim().replace(/^#/, '');
  return /^[0-9a-f]{6}$/i.test(v) ? `#${v.toLowerCase()}` : null;
}

export interface ColorFieldProps {
  value: string;
  onChange: (hex: string) => void;
  /** Accessible name for the swatch; the hex field derives its name from it. */
  label: string;
  /** Also render the editable, copyable hex text field beside the swatch. */
  hex?: boolean;
  /** `md` (32px, default) for forms; `sm` (compact) to sit inside a pill. */
  size?: 'md' | 'sm';
  /** Extra class merged onto the swatch (layout only — e.g. a pill's margin). */
  className?: string;
}

export function ColorField({
  value,
  onChange,
  label,
  hex,
  size = 'md',
  className,
}: ColorFieldProps) {
  // (hex field) Local text so a partially-typed hex isn't rejected mid-keystroke;
  // commits on a valid value, reverts to the committed color on blur. Resync to a
  // value changed from the outside (picker, materialize, scheme) but not to this
  // field's own commit — the render-time adjustment is React's alternative to a
  // sync effect.
  const [text, setText] = useState(value);
  const [synced, setSynced] = useState(value);
  if (hex && value !== synced) {
    setSynced(value);
    if (normalizeHexColor(text) !== value) setText(value);
  }

  const swatch = (
    <input
      type="color"
      className={`${styles.swatch} ${size === 'sm' ? styles.sm : ''} ${className ?? ''}`}
      aria-label={label}
      value={isHexColor(value) ? value : '#000000'}
      onChange={(e) => onChange(e.target.value)}
    />
  );

  if (!hex) return swatch;

  return (
    <span className={styles.field}>
      {swatch}
      <input
        type="text"
        className={styles.hex}
        aria-label={`${label} hex value`}
        spellCheck={false}
        value={text}
        onChange={(e) => {
          setText(e.target.value);
          const norm = normalizeHexColor(e.target.value);
          if (norm) onChange(norm);
        }}
        onBlur={() => setText(value)}
      />
    </span>
  );
}
