/**
 * TagInput — 标签输入框（Tailwind 内联）
 *
 * 容器视觉对齐 Figma taginput / Select：36px 白底、8px 圆角、1px 默认描边。
 * 已选标签、+N 折叠项与 Tooltip 内标签都必须复用平台现有 Tag 组件；
 * TagInput 只负责容器状态、宽度测量、折叠和删除逻辑。
 *
 * @prop {'default'|'error'} [status='default'] — 状态
 * @prop {boolean} [disabled=false] — 是否禁用
 * @prop {string} [placeholder='请输入'] — 空态占位
 * @prop {Array<string|{value:string,label:string,variant?:string}>} value — 当前标签（受控）
 * @prop {Array<string|{value:string,label:string,variant?:string}>} [defaultValue=null] — 非受控初始标签
 * @prop {(nextTags:Array, removedTag?:object) => void} [onChange=null] — 标签列表变化
 * @prop {(tag:object) => void} [onRemove=null] — 删除标签回调
 * @prop {boolean} [closable=true] — 标签是否可关闭
 * @prop {'brand'|'red'|'orange'|'yellow'|'green'|'cyan'|'blue'|'purple'|'pink'|'teal'|'grey'|'white'} [tagVariant='grey'] — 默认 Tag 颜色
 * @prop {'s'|'m'|'l'} [tagSize='m'] — Tag 尺寸
 * @prop {string} [className=''] — 类名
 */

import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';
import Tag from './Tag';
import { DEFAULT_TAG_RADIUS, DEFAULT_TAG_SIZE, DEFAULT_TAG_VARIANT } from './tagShared';
import Tooltip from './Tooltip';

/* ── 根容器基础样式 ── */
const BASE = [
  'tfds-tag-input',
  'group relative inline-flex w-full min-w-0 max-w-full items-center',
  'border border-solid rounded-md',
  'transition-colors tfds-motion-ui-fast',
  'outline-none [font-family:inherit]',
].join(' ');

/* ── 状态 → 容器背景 / 边框 ── */
const STATUS_CLASS = {
  default: [
    'bg-surface border-border',
    'hover:border-blueGrey-400',
    'focus-within:border-primary',
    'focus-within:hover:border-primary',
  ].join(' '),
  error: [
    'bg-red-50 border-border',
    'hover:bg-red-100',
    'focus-within:border-red-500',
    'focus-within:hover:border-red-500 focus-within:hover:bg-red-50',
  ].join(' '),
};

/* ── 固定 MD 尺寸 → 高度 / 内边距 / 字号 ── */
const SIZE_CLASS = 'min-h-9 pl-3 pr-2 py-[6px] text-sm';

/* ── 禁用态 ── */
const DISABLED_CLASS = 'bg-disabled border-border cursor-not-allowed opacity-60';

/* ── 内容行 / 输入占位 ── */
const CONTENT_ROW = 'flex min-w-0 flex-1 items-center gap-1 overflow-hidden';
const PLACEHOLDER = 'min-w-0 truncate text-sm leading-5 text-foreground-muted';
const MEASURE_ROW = 'pointer-events-none invisible absolute left-0 top-0 flex items-center gap-1 whitespace-nowrap';

function normalizeTag(item, index, tagVariant) {
  if (item && typeof item === 'object') {
    const label = String(item.label ?? item.value ?? '');
    const fallbackValue = `${label || 'tag'}-${index}`;
    return {
      ...item,
      value: String(item.value ?? fallbackValue),
      label,
      variant: item.variant || tagVariant,
      dedupeKey: String(item.value ?? label).trim().toLowerCase(),
    };
  }
  const label = String(item ?? '');
  return {
    value: `${label || 'tag'}-${index}`,
    label,
    variant: tagVariant,
    dedupeKey: label.trim().toLowerCase(),
  };
}

function normalizeList(raw, tagVariant) {
  return (Array.isArray(raw) ? raw : [])
    .map((item, index) => normalizeTag(item, index, tagVariant))
    .filter((tag) => tag.label.length > 0);
}

function getVisibleCount(tagWidths, moreWidth, availableWidth) {
  if (tagWidths.length === 0) return 0;
  const gap = 4;
  let used = 0;
  for (let i = 0; i < tagWidths.length; i += 1) {
    const remaining = tagWidths.length - i - 1;
    const reserveMore = remaining > 0 ? moreWidth + gap : 0;
    const nextUsed = used + tagWidths[i] + (i > 0 ? gap : 0);
    if (nextUsed + reserveMore <= availableWidth) {
      used = nextUsed;
      continue;
    }
    return Math.max(0, i);
  }
  return tagWidths.length;
}

export default function TagInput({
  size: _size = 'md',
  status = 'default',
  disabled = false,
  placeholder = '请输入',
  value,
  defaultValue = null,
  onChange = null,
  onRemove = null,
  closable = true,
  tagVariant = DEFAULT_TAG_VARIANT,
  tagSize = DEFAULT_TAG_SIZE,
  className = '',
  ...rest
}) {
  const rootRef = useRef(null);
  const measureRef = useRef(null);
  const moreMeasureRef = useRef(null);
  const isControlled = value !== undefined;
  const [innerValue, setInnerValue] = useState(() => normalizeList(defaultValue, tagVariant));
  const [visibleCount, setVisibleCount] = useState(null);

  useEffect(() => {
    if (!isControlled) setInnerValue(normalizeList(defaultValue, tagVariant));
  }, [defaultValue, isControlled, tagVariant]);

  const tags = useMemo(
    () => (isControlled ? normalizeList(value, tagVariant) : innerValue),
    [innerValue, isControlled, tagVariant, value],
  );

  const measure = useCallback(() => {
    const root = rootRef.current;
    const row = measureRef.current;
    if (!root || !row) return;
    const availableWidth = Math.max(0, root.clientWidth - 20);
    const tagWidths = Array.from(row.querySelectorAll('[data-measure-tag]'))
      .map((node) => Math.ceil(node.getBoundingClientRect().width));
    const moreWidth = Math.ceil(moreMeasureRef.current?.getBoundingClientRect().width || 34);
    setVisibleCount(getVisibleCount(tagWidths, moreWidth, availableWidth));
  }, []);

  useLayoutEffect(() => {
    measure();
  }, [measure, tags, tagSize, status]);

  useEffect(() => {
    if (!rootRef.current || typeof ResizeObserver === 'undefined') return undefined;
    const observer = new ResizeObserver(() => measure());
    observer.observe(rootRef.current);
    return () => observer.disconnect();
  }, [measure]);

  const commit = useCallback((next, removedTag) => {
    if (!isControlled) setInnerValue(next);
    onChange?.(next, removedTag);
  }, [isControlled, onChange]);

  const handleRemove = useCallback((tag) => {
    if (disabled) return;
    const next = tags.filter((item) => item.value !== tag.value);
    commit(next, tag);
    onRemove?.(tag);
  }, [commit, disabled, onRemove, tags]);

  const safeVisibleCount = visibleCount == null ? tags.length : Math.min(visibleCount, tags.length);
  const visibleTags = tags.slice(0, safeVisibleCount);
  const hiddenTags = tags.slice(safeVisibleCount);

  const rootCls = [
    BASE,
    disabled ? DISABLED_CLASS : (STATUS_CLASS[status] || STATUS_CLASS.default),
    SIZE_CLASS,
    className,
  ].filter(Boolean).join(' ');

  const hiddenContent = hiddenTags.length > 0 ? (
    <div className="flex max-w-[248px] flex-wrap gap-1">
      {hiddenTags.map((tag) => (
        <Tag key={tag.value} variant={tag.variant || tagVariant} size={tagSize} radius={DEFAULT_TAG_RADIUS}>
          {tag.label}
        </Tag>
      ))}
    </div>
  ) : null;

  const renderTag = (tag, tagProps = {}) => (
    <Tag
      key={tag.value}
      variant={tag.variant || tagVariant}
      size={tagSize}
      radius={DEFAULT_TAG_RADIUS}
      {...tagProps}
    >
      {tag.label}
    </Tag>
  );

  return (
    <div ref={rootRef} className={rootCls} aria-disabled={disabled || undefined} data-tfds-component="TagInput" {...rest}>
      <div className={CONTENT_ROW}>
        {tags.length === 0 ? (
          <span className={PLACEHOLDER}>{placeholder}</span>
        ) : (
          <>
            {visibleTags.map((tag) => (
              renderTag(tag, {
                closable: closable && !disabled,
                onClose: closable && !disabled ? () => handleRemove(tag) : undefined,
              })
            ))}
            {hiddenTags.length > 0 ? (
              <Tooltip
                content={hiddenContent}
                tone="light"
                placement="top"
                triggerClassName="inline-flex shrink-0"
                className="!max-w-[280px]"
              >
                <Tag variant={tagVariant} size={tagSize} radius={DEFAULT_TAG_RADIUS}>+{hiddenTags.length}</Tag>
              </Tooltip>
            ) : null}
          </>
        )}
      </div>

      <div ref={measureRef} className={MEASURE_ROW} aria-hidden>
        {tags.map((tag) => (
          <span key={tag.value} data-measure-tag>
            {renderTag(tag, { closable: closable && !disabled })}
          </span>
        ))}
        <span ref={moreMeasureRef}>
          <Tag variant={tagVariant} size={tagSize} radius={DEFAULT_TAG_RADIUS}>+99</Tag>
        </span>
      </div>
    </div>
  );
}
