/**
 * Theme Builder — Marks panel (docs/chart-theming-scope.md §5).
 *
 * The mark defaults a brand tunes, grouped by mark TYPE: writing
 * `config.bar.cornerRadiusEnd` rounds only bars, where `config.mark.cornerRadius`
 * would round everything — the honest brand model is per-type. The default
 * single-series mark color stays in the Color panel (it sits with the palette);
 * everything else mark-shaped lives here.
 *
 * Unlike the scalar panels, this one is an Accordion (one mark type open at a
 * time): a brand styles one or two mark types, not all five, so the per-type
 * groups are "not crucial to read in full" — Carbon's accordion rule (arch 10 →
 * L2 grouping). Each control writes one config path through the shared
 * `useConfigSetter`, deleting the key (and pruning the emptied per-type object)
 * when cleared, so a theme stays a minimal diff against stock.
 */

import type { JsonObject } from '@core/spec-config';
import {
  asBoolean,
  asNumber,
  type ConfigPath,
  countSet,
  enumValue,
  getConfigValue,
} from '@core/theme-controls';
import { useConfigSetter } from '../stores/CustomThemeStore';
import { Accordion, type AccordionSection, NumberRow, SelectRow } from './ThemeFields';
import type { SelectControlOption } from './SelectControl';

// ── Enum option sets ────────────────────────────────────────────────────────
// '' is the unset (theme default) sentinel for every enum; a config value the
// control doesn't enumerate (a hand-edit) reads back as '' so the control shows
// the default rather than misreporting — same convention as the Axes panel.

type Interpolate =
  | ''
  | 'linear'
  | 'monotone'
  | 'basis'
  | 'cardinal'
  | 'natural'
  | 'step'
  | 'step-before'
  | 'step-after';
const interpolateOptions: SelectControlOption<Interpolate>[] = [
  { value: '', label: 'Theme default' },
  { value: 'linear', label: 'Linear' },
  { value: 'monotone', label: 'Monotone' },
  { value: 'basis', label: 'Basis (smooth)' },
  { value: 'cardinal', label: 'Cardinal' },
  { value: 'natural', label: 'Natural' },
  { value: 'step', label: 'Step' },
  { value: 'step-before', label: 'Step before' },
  { value: 'step-after', label: 'Step after' },
];

type Shape = '' | 'circle' | 'square' | 'cross' | 'diamond' | 'triangle-up' | 'triangle-down';
const shapeOptions: SelectControlOption<Shape>[] = [
  { value: '', label: 'Theme default' },
  { value: 'circle', label: 'Circle' },
  { value: 'square', label: 'Square' },
  { value: 'cross', label: 'Cross' },
  { value: 'diamond', label: 'Diamond' },
  { value: 'triangle-up', label: 'Triangle up' },
  { value: 'triangle-down', label: 'Triangle down' },
];

// ── Boolean tri-state row ───────────────────────────────────────────────────

/**
 * A boolean config key as a tri-state select (theme default · true · false).
 * "Unset" is a distinct, meaningful state from either boolean here — filled vs.
 * outlined marks, tooltips on vs. off — so a plain checkbox (which can't express
 * "inherit") would lie. Same shape as the Axes panel's grid-visibility control.
 */
function BoolRow({
  id,
  label,
  value,
  onChange,
  trueLabel,
  falseLabel,
}: {
  id: string;
  label: string;
  value: boolean | undefined;
  onChange: (value: boolean | undefined) => void;
  trueLabel: string;
  falseLabel: string;
}) {
  type S = '' | 'true' | 'false';
  const options: SelectControlOption<S>[] = [
    { value: '', label: 'Theme default' },
    { value: 'true', label: trueLabel },
    { value: 'false', label: falseLabel },
  ];
  const s: S = value === undefined ? '' : value ? 'true' : 'false';
  return (
    <SelectRow
      id={id}
      label={label}
      options={options}
      value={s}
      onSelect={(v) => onChange(v === '' ? undefined : v === 'true')}
    />
  );
}

// Each section's badge counts how many of *its own* controls are set (the uniform
// `countSet` contract — "fields set here"). Listed explicitly, not `Object.keys`
// on the per-type object: `mark.color` lives under `mark` but is owned by the
// Color panel, so a key count would inflate the "All marks" badge.
const MARK_PATHS: ConfigPath[] = [
  ['mark', 'opacity'],
  ['mark', 'filled'],
  ['mark', 'tooltip'],
];
const BAR_PATHS: ConfigPath[] = [
  ['bar', 'cornerRadiusEnd'],
  ['bar', 'discreteBandSize'],
  ['bar', 'opacity'],
];
const LINE_AREA_PATHS: ConfigPath[] = [
  ['line', 'interpolate'],
  ['line', 'strokeWidth'],
  ['line', 'point'],
  ['area', 'opacity'],
  ['area', 'line'],
];
const POINT_PATHS: ConfigPath[] = [
  ['point', 'size'],
  ['point', 'shape'],
  ['point', 'filled'],
  ['point', 'opacity'],
];
const ARC_PATHS: ConfigPath[] = [
  ['arc', 'innerRadius'],
  ['arc', 'cornerRadius'],
  ['arc', 'padAngle'],
];

export function MarksControls({ config }: { config: JsonObject }) {
  const set = useConfigSetter();

  const num = (path: ConfigPath) => asNumber(getConfigValue(config, path));
  const bool = (path: ConfigPath) => asBoolean(getConfigValue(config, path));

  const sections: AccordionSection[] = [
    {
      id: 'all',
      title: 'All marks',
      badge: countSet(config, MARK_PATHS),
      children: (
        <>
          <NumberRow
            label="Opacity"
            name="Mark opacity"
            value={num(['mark', 'opacity'])}
            min={0}
            max={1}
            step={0.1}
            onChange={(n) => set(['mark', 'opacity'], n)}
          />
          <BoolRow
            id="marks-filled"
            label="Fill style"
            value={bool(['mark', 'filled'])}
            onChange={(b) => set(['mark', 'filled'], b)}
            trueLabel="Filled"
            falseLabel="Outlined"
          />
          <BoolRow
            id="marks-tooltip"
            label="Tooltips"
            value={bool(['mark', 'tooltip'])}
            onChange={(b) => set(['mark', 'tooltip'], b)}
            trueLabel="On"
            falseLabel="Off"
          />
        </>
      ),
    },
    {
      id: 'bar',
      title: 'Bars',
      badge: countSet(config, BAR_PATHS),
      children: (
        <>
          <NumberRow
            label="Corner radius"
            name="Bar corner radius"
            value={num(['bar', 'cornerRadiusEnd'])}
            min={0}
            unit="px"
            onChange={(n) => set(['bar', 'cornerRadiusEnd'], n)}
          />
          <NumberRow
            label="Bar width"
            name="Bar width"
            // discreteBandSize = bar thickness on a categorical axis (the common
            // case, and what the gallery's nominal-axis bars show); continuous-axis
            // bars (continuousBandSize) stay in the JSON.
            value={num(['bar', 'discreteBandSize'])}
            min={0}
            unit="px"
            onChange={(n) => set(['bar', 'discreteBandSize'], n)}
          />
          <NumberRow
            label="Opacity"
            name="Bar opacity"
            value={num(['bar', 'opacity'])}
            min={0}
            max={1}
            step={0.1}
            onChange={(n) => set(['bar', 'opacity'], n)}
          />
        </>
      ),
    },
    {
      id: 'lineArea',
      title: 'Lines & areas',
      badge: countSet(config, LINE_AREA_PATHS),
      children: (
        <>
          <SelectRow
            id="marks-line-interpolate"
            label="Line curve"
            options={interpolateOptions}
            value={enumValue(getConfigValue(config, ['line', 'interpolate']), interpolateOptions)}
            onSelect={(v) => set(['line', 'interpolate'], v === '' ? undefined : v)}
          />
          <NumberRow
            label="Line width"
            name="Line width"
            value={num(['line', 'strokeWidth'])}
            min={0}
            unit="px"
            onChange={(n) => set(['line', 'strokeWidth'], n)}
          />
          <BoolRow
            id="marks-line-point"
            label="Line markers"
            value={bool(['line', 'point'])}
            onChange={(b) => set(['line', 'point'], b)}
            trueLabel="Show"
            falseLabel="Hide"
          />
          <NumberRow
            label="Area opacity"
            name="Area opacity"
            value={num(['area', 'opacity'])}
            min={0}
            max={1}
            step={0.1}
            onChange={(n) => set(['area', 'opacity'], n)}
          />
          <BoolRow
            id="marks-area-line"
            label="Area outline"
            value={bool(['area', 'line'])}
            onChange={(b) => set(['area', 'line'], b)}
            trueLabel="Show"
            falseLabel="Hide"
          />
        </>
      ),
    },
    {
      id: 'point',
      title: 'Points',
      badge: countSet(config, POINT_PATHS),
      children: (
        <>
          <NumberRow
            label="Size"
            name="Point size"
            value={num(['point', 'size'])}
            min={0}
            onChange={(n) => set(['point', 'size'], n)}
          />
          <SelectRow
            id="marks-point-shape"
            label="Shape"
            name="Point shape"
            options={shapeOptions}
            value={enumValue(getConfigValue(config, ['point', 'shape']), shapeOptions)}
            onSelect={(v) => set(['point', 'shape'], v === '' ? undefined : v)}
          />
          <BoolRow
            id="marks-point-filled"
            label="Fill style"
            value={bool(['point', 'filled'])}
            onChange={(b) => set(['point', 'filled'], b)}
            trueLabel="Filled"
            falseLabel="Outlined"
          />
          <NumberRow
            label="Opacity"
            name="Point opacity"
            value={num(['point', 'opacity'])}
            min={0}
            max={1}
            step={0.1}
            onChange={(n) => set(['point', 'opacity'], n)}
          />
        </>
      ),
    },
    {
      id: 'arc',
      title: 'Arc (pie & donut)',
      badge: countSet(config, ARC_PATHS),
      children: (
        <>
          <NumberRow
            label="Donut hole"
            name="Donut hole radius"
            value={num(['arc', 'innerRadius'])}
            min={0}
            unit="px"
            onChange={(n) => set(['arc', 'innerRadius'], n)}
          />
          <NumberRow
            label="Corner radius"
            name="Arc corner radius"
            value={num(['arc', 'cornerRadius'])}
            min={0}
            unit="px"
            onChange={(n) => set(['arc', 'cornerRadius'], n)}
          />
          <NumberRow
            label="Pad angle"
            name="Arc pad angle"
            value={num(['arc', 'padAngle'])}
            min={0}
            step={0.01}
            onChange={(n) => set(['arc', 'padAngle'], n)}
          />
        </>
      ),
    },
  ];

  return <Accordion sections={sections} idPrefix="marks" />;
}
