/**
 * Checkbox / CheckboxGroup — B端设计系统 (Tailwind 内联)
 *
 * **选型**：同一维度多选、权限/标签、同意条款 → Checkbox；互斥单选 → `RadioGroup`；单开闭且立即生效 → `Switch`；多选且极省空间 → `Select` mode=tag。
 * 视觉参考 HIUIpackage 内 @douyinfe/semi-ui 的 `.semi-checkbox`（umd/view.css）：
 * 16×16 指示框、圆角 extra-small 映射为 `--radius-sm`、未选用 inset 1px 描边（对齐 semi text-3）、
 * hover 为 fill-0 + inset focus 色、active 为 fill-1、选中为实心主色 + 白勾/横杠、indeterminate 与选中同底、
 * 禁用未选 disabled-fill + border、禁用已选 primary-disabled + opacity 0.75。
 * CheckboxGroup 纵向 row-gap 12px、横向 gap 16px（对齐 `.semi-checkboxGroup`）。
 * 颜色与间距全部使用本系统 tokens.js 变量，不引用 Semi CSS 变量名。
 *
 * @component CheckboxGroup
 * @prop {'brand'|'black'} [variant='brand'] — 色系：brand 选中绿色、black 选中深灰
 * @prop {'sm'|'md'} [size='md'] — 尺寸：sm 14px 指示框 / md 16px 指示框
 * @prop {'vertical'|'horizontal'} [layout='vertical'] — 布局：vertical 纵向 12px / horizontal 横向 16px
 * @prop {boolean} [disabled=false] — 禁用整组
 * @prop {Array} [value] — 受控选中值数组
 * @prop {Array} [defaultValue] — 非受控初始值数组
 * @prop {(nextValues, event?) => void} [onChange] — 选中回调
 * @prop {string} [className='']
 *
 * @component Checkbox（可单独使用，也可作为 CheckboxGroup 子项）
 * @prop {'brand'|'black'} [variant] — 覆盖组色系
 * @prop {'sm'|'md'} [size] — 覆盖组尺寸
 * @prop {boolean} [disabled=false] — 单项禁用
 * @prop {string} [value] — 组内选项标识（组内必填）
 * @prop {boolean} [checked] — 受控选中（单独使用时）
 * @prop {boolean} [defaultChecked=false] — 非受控初始选中
 * @prop {boolean} [indeterminate=false] — 半选态（树形父级），视觉优先于 checked
 * @prop {(event) => void} [onChange] — 变更回调（单独使用时）
 * @prop {string} [className='']
 * @prop {ReactNode} children — 文案
 */

import {
  createContext,
  useContext,
  useId,
  useMemo,
  useState,
  useCallback,
  useRef,
  useEffect,
} from 'react';

const GroupContext = createContext(null);

/* ── 布局方向 → 纵向组距 12px / 横向组距 16px ── */
const LAYOUT_CLASS = {
  vertical: 'flex flex-col gap-3',
  horizontal: 'inline-flex flex-row flex-wrap items-start gap-4',
};

/* ── 基础 → 组容器字体继承 ── */
const GROUP_BASE = '[font-family:inherit]';

/* ── 项行 → 图文横排、间距 8px、选中区域 ── */
const LABEL_ROW = 'group inline-flex items-start gap-2 select-none rounded-sm';

/* ── 指示器 → 内包裹层（含垂直居中，md 20×16 / sm 18×14） ── */
const INNER_WRAP = {
  sm: 'relative inline-flex h-[18px] w-[14px] shrink-0 items-center justify-center',
  md: 'relative inline-flex h-[20px] w-[16px] shrink-0 items-center justify-center',
};

/* ── 指示框 → 方框基础样式（border-radius sm、1px solid） ── */
const BOX_BASE = [
  'relative box-border flex shrink-0 items-center justify-center rounded-sm',
  'border border-solid transition-colors tfds-motion-ui-fast',
].join(' ');

const BOX_SIZE = {
  sm: 'size-[14px]',
  md: 'size-[16px]',
};

/* ── 标签文字 → 字号与行高 ── */
const LABEL_TEXT = {
  sm: 'text-xs leading-[20px]',
  md: 'text-sm leading-[20px]',
};

/* ── 聚焦环 → 变体色 focus-visible outline ── */
const FOCUS_OUTLINE = {
  brand: 'has-[:focus-visible]:outline has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-brand-200',
  black: 'has-[:focus-visible]:outline has-[:focus-visible]:outline-2 has-[:focus-visible]:outline-offset-2 has-[:focus-visible]:outline-grey-400',
};

function buildBoxClass(checked, indeterminate, disabled, variant, sizeKey) {
  const sz = BOX_SIZE[sizeKey] || BOX_SIZE.md;
  const filled = checked || indeterminate;

  if (disabled) {
    if (filled) {
      if (variant === 'black') {
        return [
          BOX_BASE, sz,
          'opacity-75 bg-blueGrey-400 border-blueGrey-400 text-white',
        ].join(' ');
      }
      return [
        BOX_BASE, sz,
        'opacity-75 bg-brand-200 border-brand-200 text-white',
      ].join(' ');
    }
    return [
      BOX_BASE, sz,
      'bg-disabled border-border-default',
    ].join(' ');
  }

  if (filled) {
    if (variant === 'black') {
      return [
        BOX_BASE, sz,
        'bg-grey-950 border-grey-950 text-white',
        'group-hover:bg-grey-900 group-hover:border-grey-900',
        'group-active:bg-grey-800 group-active:border-grey-800',
      ].join(' ');
    }
    return [
      BOX_BASE, sz,
      'bg-brand-500 border-brand-500 text-white',
      'group-hover:bg-brand-600 group-hover:border-brand-600',
      'group-active:bg-brand-700 group-active:border-brand-700',
    ].join(' ');
  }

  return [
    BOX_BASE, sz,
    'border-foreground-muted bg-transparent',
    'group-hover:bg-fill group-hover:border-border-brand',
    'group-active:bg-blueGrey-100',
  ].join(' ');
}

function IconCheck({ className }) {
  return (
    <svg className={className} viewBox="0 0 12 12" fill="none" aria-hidden>
      <path
        d="M2.5 6L5 8.5L9.5 3.5"
        stroke="currentColor"
        strokeWidth="1.5"
        strokeLinecap="round"
        strokeLinejoin="round"
      />
    </svg>
  );
}

function IconIndeterminate({ className }) {
  return (
    <svg className={className} viewBox="0 0 12 12" fill="none" aria-hidden>
      <path d="M2.5 6H9.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" />
    </svg>
  );
}

const ICON_SIZE = { sm: 'size-[10px]', md: 'size-[12px]' };

export function Checkbox({
  variant: variantProp,
  size: sizeProp,
  disabled: itemDisabled = false,
  value: optionValue,
  checked: checkedProp,
  defaultChecked = false,
  indeterminate = false,
  onChange,
  className = '',
  children,
  ...rest
}) {
  const ctx = useContext(GroupContext);
  const inputRef = useRef(null);

  const inGroup = ctx != null;
  const variant = variantProp ?? ctx?.variant ?? 'brand';
  const size = sizeProp ?? ctx?.size ?? 'md';
  const groupDisabled = ctx?.groupDisabled ?? false;
  const disabled = groupDisabled || itemDisabled;

  const isControlledStandalone = !inGroup && checkedProp !== undefined;
  const [innerChecked, setInnerChecked] = useState(defaultChecked);

  const selectedInGroup = inGroup && optionValue !== undefined
    && ctx.selected.some((x) => String(x) === String(optionValue));
  const checked = inGroup ? selectedInGroup : (isControlledStandalone ? checkedProp : innerChecked);

  useEffect(() => {
    if (inputRef.current) {
      inputRef.current.indeterminate = Boolean(indeterminate);
    }
  }, [indeterminate, checked]);

  const fo = FOCUS_OUTLINE[variant] || FOCUS_OUTLINE.brand;
  const boxCls = buildBoxClass(checked, indeterminate, disabled, variant, size === 'sm' ? 'sm' : 'md');
  const wrapCls = INNER_WRAP[size === 'sm' ? 'sm' : 'md'];
  const textCls = LABEL_TEXT[size === 'sm' ? 'sm' : 'md'];
  const iconSz = ICON_SIZE[size === 'sm' ? 'sm' : 'md'];

  const handleChange = useCallback((e) => {
    const next = e.target.checked;
    if (inGroup) {
      ctx.toggleValue(optionValue, next, e);
      return;
    }
    if (!isControlledStandalone) setInnerChecked(next);
    onChange?.(e);
  }, [inGroup, ctx, optionValue, isControlledStandalone, onChange]);

  const showCheck = checked;
  const showBar = indeterminate && !checked;

  if (inGroup && optionValue === undefined) return null;

  return (
    <label
      className={[`tfds-checkbox`, [LABEL_ROW, fo, disabled ? 'cursor-not-allowed' : 'cursor-pointer', className].filter(Boolean).join(' ')].filter(Boolean).join(' ')}
      data-tfds-component="Checkbox"
    >
      <span className={wrapCls}>
        <input
          ref={inputRef}
          type="checkbox"
          className="sr-only"
          checked={checked}
          disabled={disabled}
          onChange={handleChange}
          aria-checked={indeterminate ? 'mixed' : checked}
          {...rest}
        />
        <span className={boxCls} aria-hidden>
          {showCheck ? <IconCheck className={iconSz} /> : null}
          {showBar ? <IconIndeterminate className={iconSz} /> : null}
        </span>
      </span>
      {children != null && children !== false ? (
        <span
          className={[
            textCls,
            disabled ? 'text-foreground-disabled' : 'text-foreground',
          ].filter(Boolean).join(' ')}
        >
          {children}
        </span>
      ) : null}
    </label>
  );
}

function normalizeValues(arr) {
  if (!Array.isArray(arr)) return [];
  return arr.map((x) => x);
}

export default function CheckboxGroup({
  variant = 'brand',
  size = 'md',
  layout = 'vertical',
  disabled = false,
  value,
  defaultValue,
  onChange,
  className = '',
  children,
  ...rest
}) {
  const isControlled = value !== undefined;
  const [inner, setInner] = useState(() => normalizeValues(defaultValue));

  const selected = isControlled ? normalizeValues(value) : inner;

  const toggleValue = useCallback((v, isOn, e) => {
    const key = String(v);
    if (isControlled) {
      const cur = normalizeValues(value);
      const next = isOn
        ? (cur.some((x) => String(x) === key) ? cur : [...cur, v])
        : cur.filter((x) => String(x) !== key);
      onChange?.(next, e);
      return;
    }
    setInner((prev) => {
      const cur = normalizeValues(prev);
      const next = isOn
        ? (cur.some((x) => String(x) === key) ? cur : [...cur, v])
        : cur.filter((x) => String(x) !== key);
      onChange?.(next, e);
      return next;
    });
  }, [isControlled, value, onChange]);

  const ctxValue = useMemo(() => ({
    selected,
    toggleValue,
    groupDisabled: disabled,
    variant,
    size,
  }), [selected, toggleValue, disabled, variant, size]);

  const layoutCls = LAYOUT_CLASS[layout] || LAYOUT_CLASS.vertical;
  const sizeText = size === 'sm' ? 'text-xs' : 'text-sm';

  return (
    <GroupContext.Provider value={ctxValue}>
      <div
        role="group"
        className={[`tfds-checkbox`, [GROUP_BASE, sizeText, 'leading-[20px]', layoutCls, className].filter(Boolean).join(' ')].filter(Boolean).join(' ')}
        {...rest}
        data-tfds-component="Checkbox"
      >
        {children}
      </div>
    </GroupContext.Provider>
  );
}
