import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import Button from './Button';
import Dropdown from './Dropdown';
import Icon from './Icon';
import Input from './Input';

/**
 * TagBar — B端业务标签栏（按 Figma 导航框架像素还原）
 *
 * 组件覆盖展开态树形标签栏与折叠态图标列，两种状态都保留业务切换、搜索入口和底部折叠按钮。
 * 标签前置图标统一复用 B 端 Icon 库；业务图标使用设计稿中的品牌图。
 *
 * @prop {Array<{id: string, label: string, iconSrc?: string}>|null} [businesses=null] — 业务切换列表
 * @prop {string|null} currentBusinessId — 当前业务 id（受控）
 * @prop {string|null} [defaultBusinessId=null] — 非受控初始业务 id
 * @prop {(businessId: string, meta: { business: {id: string, label: string, iconSrc?: string} | null }) => void} [onBusinessChange=null] — 业务切换回调
 * @prop {Array<{id: string, label: string, iconName?: string, iconBgToken?: string, children?: any[]}>|null} [items=null] — 树形标签数据
 * @prop {string|null} selectedItemId — 当前选中节点 id（受控）
 * @prop {string|null} [defaultSelectedItemId=null] — 非受控初始选中节点 id
 * @prop {(itemId: string, meta: { item: object, depth: number, businessId: string | null }) => void} [onSelect=null] — 节点点击回调
 * @prop {string[]|null} expandedIds — 当前展开节点集合（受控）
 * @prop {string[]} [defaultExpandedIds=[]] — 非受控初始展开节点集合
 * @prop {(expandedIds: string[]) => void} [onExpandedChange=null] — 展开节点变化回调
 * @prop {boolean} [searchable=true] — 是否显示搜索
 * @prop {string} searchValue — 当前搜索关键字（受控）
 * @prop {string} [defaultSearchValue=''] — 非受控初始搜索关键字
 * @prop {(value: string) => void} [onSearchChange=null] — 搜索关键字变化回调
 * @prop {boolean} [collapsible=true] — 是否显示折叠按钮
 * @prop {boolean} collapsed — 当前收起状态（受控）
 * @prop {boolean} [defaultCollapsed=false] — 非受控初始收起状态
 * @prop {(collapsed: boolean) => void} [onCollapsedChange=null] — 收起状态变化回调
 * @prop {boolean} [resizable=false] — 已废弃：TagBar 自身不再绘制右侧拖拽把手；独立白卡场景由外层容器负责拖拽调宽
 * @prop {number} width — 当前宽度（受控）
 * @prop {number} [defaultWidth=240] — 非受控初始宽度
 * @prop {number} [minWidth=160] — 最小展开宽度
 * @prop {number} [maxWidth=360] — 最大宽度
 * @prop {number} [collapseThreshold=130] — 自动切图标态阈值
 * @prop {(width: number) => void} [onWidthChange=null] — 宽度变化回调
 * @prop {string} [className=''] — 附加类名
 * @prop {React.CSSProperties|undefined} [style=undefined] — 附加样式
 */

const DEFAULT_BUSINESS_ICON = '抖音';
const DEFAULT_EXPANDED_WIDTH = 240;
const DEFAULT_COLLAPSED_WIDTH = 72;
const DEFAULT_MIN_WIDTH = 160;
const DEFAULT_MAX_WIDTH = 360;
const DEFAULT_COLLAPSE_THRESHOLD = 130;

export const TAGBAR_SAMPLE_BUSINESSES = [
  { id: 'douyin-community', label: '抖音社区', iconSrc: DEFAULT_BUSINESS_ICON },
  { id: 'douyin-local-services', label: '抖音生活服务', iconSrc: DEFAULT_BUSINESS_ICON },
  { id: 'douyin-ecommerce', label: '抖音电商', iconSrc: DEFAULT_BUSINESS_ICON },
];

function createThirdLevel(prefix, labels) {
  return labels.map((label, index) => ({
    id: `${prefix}-field-${index + 1}`,
    label,
  }));
}

function createSecondLevel(prefix, groups) {
  return groups.map(({ id, label, fields }) => ({
    id: `${prefix}-${id}`,
    label,
    children: createThirdLevel(`${prefix}-${id}`, fields),
  }));
}

export const TAGBAR_SAMPLE_ITEMS = [
  {
    id: 'account',
    label: '账号',
    iconName: 'user-01-stroked',
    iconBgToken: 'brand-50',
    children: createSecondLevel('account', [
      {
        id: 'registration-login',
        label: '账号注册/登录',
        fields: ['手机号注册失败', '一键登录授权异常', '设备风控触发校验'],
      },
      {
        id: 'realname-security',
        label: '实名认证与安全',
        fields: ['实名认证照片不通过', '人脸校验识别失败', '账号安全二次验证'],
      },
      {
        id: 'recovery-cancel',
        label: '找回与注销',
        fields: ['账号找回申诉进度', '注销冷静期撤回', '原手机号不可用'],
      },
    ]),
  },
  {
    id: 'base-product',
    label: '基础产品',
    iconName: 'layers-two-01-stroked',
    iconBgToken: 'blue-50',
    children: createSecondLevel('base-product', [
      {
        id: 'feed-browse',
        label: '首页浏览',
        fields: ['推荐流刷新卡顿', '视频自动播放异常', '首页缓存清理'],
      },
      {
        id: 'comment-interaction',
        label: '评论互动',
        fields: ['评论发布失败', '置顶评论配置', '评论区折叠异常'],
      },
      {
        id: 'message-center',
        label: '消息中心',
        fields: ['消息红点不同步', '通知聚合异常', '互动消息延迟'],
      },
    ]),
  },
  {
    id: 'social',
    label: '社交',
    iconName: 'heart-circle-stroked',
    iconBgToken: 'pink-50',
    children: createSecondLevel('social', [
      {
        id: 'follow-graph',
        label: '关注关系',
        fields: ['关注失败重试', '粉丝关系同步延迟', '互关状态异常'],
      },
      {
        id: 'private-chat',
        label: '私信会话',
        fields: ['私信发送失败', '消息已读回执异常', '会话置顶失效'],
      },
      {
        id: 'group-interaction',
        label: '群聊互动',
        fields: ['群聊消息免打扰', '群成员邀请失败', '群公告发布异常'],
      },
    ]),
  },
  {
    id: 'contribute',
    label: '投稿',
    iconName: 'edit-01-stroked',
    iconBgToken: 'purple-50',
    children: createSecondLevel('contribute', [
      {
        id: 'video-upload',
        label: '视频上传',
        fields: ['分片上传中断', '封面提取失败', '草稿同步异常'],
      },
      {
        id: 'review-publish',
        label: '审核发布',
        fields: ['发布审核排队', '定时发布取消', '话题挂载失败'],
      },
      {
        id: 'material-edit',
        label: '素材编辑',
        fields: ['字幕模板套用失败', '音乐卡点异常', '滤镜参数未保存'],
      },
    ]),
  },
  {
    id: 'incentive',
    label: '激励',
    iconName: 'gift-01-stroked',
    iconBgToken: 'purple-50',
    children: createSecondLevel('incentive', [
      {
        id: 'creator-task',
        label: '创作任务',
        fields: ['任务报名资格校验', '任务进度回传异常', '任务奖励待发放'],
      },
      {
        id: 'growth-rights',
        label: '成长权益',
        fields: ['成长等级升级延迟', '权益礼包领取失败', '专属标识展示异常'],
      },
      {
        id: 'campaign-reward',
        label: '活动奖励',
        fields: ['挑战赛激励结算', '活动奖池分配异常', '奖励到账提醒缺失'],
      },
    ]),
  },
  {
    id: 'monetization',
    label: '作者变现',
    iconName: 'credit-card-01-stroked',
    iconBgToken: 'orange-50',
    children: createSecondLevel('monetization', [
      {
        id: 'live-commerce',
        label: '直播带货',
        fields: ['直播商品讲解卡', '橱窗商品挂载失败', '讲解口令券发放异常'],
      },
      {
        id: 'xingtu-cooperation',
        label: '星图合作',
        fields: ['商单任务待确认', '合作报价更新异常', '履约进度回传失败'],
      },
      {
        id: 'commission-income',
        label: '分佣收益',
        fields: ['分佣结算待对账', '收益明细导出失败', '提现银行卡校验'],
      },
    ]),
  },
  {
    id: 'content-traffic',
    label: '内容与流量',
    iconName: 'flip-forward-stroked',
    iconBgToken: 'purple-50',
    children: createSecondLevel('content-traffic', [
      {
        id: 'traffic-diagnosis',
        label: '流量诊断',
        fields: ['播放量波动分析', '完播率诊断建议', '流量来源拆解'],
      },
      {
        id: 'distribution-strategy',
        label: '内容分发',
        fields: ['同城流量投放异常', '兴趣人群圈选失败', '分发实验配置冲突'],
      },
      {
        id: 'fan-growth',
        label: '粉丝增长',
        fields: ['涨粉任务追踪', '粉丝画像更新延迟', '回访召回链路异常'],
      },
    ]),
  },
  {
    id: 'search',
    label: '搜索',
    iconName: 'search-lg-stroked',
    iconBgToken: 'cyan-50',
    children: createSecondLevel('search', [
      {
        id: 'search-entry',
        label: '搜索入口',
        fields: ['顶部搜索推荐词', '热榜入口样式异常', '联想词刷新延迟'],
      },
      {
        id: 'result-page',
        label: '搜索结果',
        fields: ['综合排序结果异常', '视频结果召回不足', '筛选项回显错误'],
      },
      {
        id: 'hot-topic',
        label: '热点承接',
        fields: ['热点词配置生效慢', '活动词置顶失效', '趋势内容聚合异常'],
      },
    ]),
  },
  {
    id: 'recommend',
    label: '推荐',
    iconName: 'star-01-stroked',
    iconBgToken: 'yellow-50',
    children: createSecondLevel('recommend', [
      {
        id: 'distribution-engine',
        label: '推荐分发',
        fields: ['首页推荐召回异常', '推荐池流量波动', '实验桶命中错误'],
      },
      {
        id: 'interest-recall',
        label: '兴趣召回',
        fields: ['兴趣标签更新延迟', '相似作者召回不足', '兴趣探索流量异常'],
      },
      {
        id: 'cold-start',
        label: '冷启动扶持',
        fields: ['新作者扶持策略', '冷启动内容曝光不足', '扶持任务统计延迟'],
      },
    ]),
  },
  {
    id: 'commercialization',
    label: '商业化',
    iconName: 'shopping-bag-03-stroked',
    iconBgToken: 'green-50',
    children: createSecondLevel('commercialization', [
      {
        id: 'ads-delivery',
        label: '广告投放',
        fields: ['广告计划审核中', '人群包同步失败', '预算扣减异常'],
      },
      {
        id: 'shop-operation',
        label: '小店经营',
        fields: ['商品上下架失败', '店铺装修配置丢失', '店铺分履约异常'],
      },
      {
        id: 'live-deal',
        label: '直播成交',
        fields: ['直播成交转化下降', '订单核销数据延迟', '直播优惠券失效'],
      },
    ]),
  },
  {
    id: 'ecosystem-governance',
    label: '生态治理',
    iconName: 'globe-01-stroked',
    iconBgToken: 'blue-50',
    children: createSecondLevel('ecosystem-governance', [
      {
        id: 'violation-control',
        label: '违规治理',
        fields: ['违规内容巡检任务', '账号处罚申诉中', '规则命中解释缺失'],
      },
      {
        id: 'copyright-complaint',
        label: '版权投诉',
        fields: ['侵权视频下架流程', '版权白名单维护', '投诉工单回执异常'],
      },
      {
        id: 'risk-inspection',
        label: '风险巡检',
        fields: ['黑产行为识别', '风险账号聚合看板', '风险策略回滚记录'],
      },
    ]),
  },
  {
    id: 'service-guarantee',
    label: '服务保障',
    iconName: 'heart-hand-stroked',
    iconBgToken: 'orange-50',
    children: createSecondLevel('service-guarantee', [
      {
        id: 'ticket-service',
        label: '工单服务',
        fields: ['工单创建失败', '工单流转超时', '客服接单率预警'],
      },
      {
        id: 'after-sale-compensation',
        label: '售后赔付',
        fields: ['退款赔付审核中', '赔付金额校验异常', '售后补贴到账慢'],
      },
      {
        id: 'appeal-support',
        label: '申诉保障',
        fields: ['封禁申诉待补材料', '处罚申诉进度查询', '申诉结果通知缺失'],
      },
    ]),
  },
];

const ROOT_BASE = 'tfds-tag-bar relative flex h-full min-h-0 shrink-0 flex-col select-none transition-[width] tfds-motion-ui-normal';
const MAIN_WRAP = 'flex min-h-0 flex-1 flex-col gap-3';
const SCROLL_AREA = 'flex min-h-0 flex-1 flex-col gap-1 overflow-y-auto';
const ICON_BUTTON = 'inline-flex h-9 w-9 items-center justify-center rounded-[8px] text-foreground-muted transition-colors tfds-motion-ui-fast hover:bg-fill focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-200';
const ROW_BASE = 'group relative flex w-full items-center rounded-[8px] text-left text-foreground focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-200';
const ROW_BG_WRAP_SECONDARY = 'relative flex h-full w-full min-w-0 flex-1 pl-1 pr-1 -ml-1';
const ROW_BG_LAYER_SECONDARY = 'pointer-events-none absolute inset-0 rounded-[8px] transition-colors tfds-motion-ui-fast group-hover:bg-fill';
const ROW_BG_WRAP_PRIMARY = 'flex h-9 w-full min-w-0 overflow-hidden rounded-[8px] transition-colors tfds-motion-ui-fast group-hover:bg-fill';
const ROW_BG_WRAP_TERTIARY = 'flex h-full w-full min-w-0 flex-1 overflow-hidden rounded-[8px] transition-colors tfds-motion-ui-fast group-hover:bg-fill';
const ROW_CONTENT = 'relative z-10 flex h-full w-full min-w-0 flex-1 items-center gap-1';
const ROW_CONTENT_PRIMARY = 'flex h-9 w-full min-w-0 items-center gap-1 px-3';
const ROW_CONTENT_TERTIARY = 'flex h-full w-full min-w-0 flex-1 items-center gap-1 px-2';
const BUSINESS_ROW = 'group flex h-9 w-full items-center gap-2 rounded-[8px] px-3 transition-colors tfds-motion-ui-fast';
const CHEVRON_BUTTON_CLASS = 'inline-flex h-3 w-4 shrink-0 items-center justify-center rounded-[4px] text-foreground-muted focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-200';

const ICON_BG_CLASS = {
  'brand-50': 'bg-brand-50 text-brand-500',
  'blueGrey-100': 'bg-blueGrey-100 text-blueGrey-700',
  'blue-50': 'bg-blue-50 text-blue-600',
  'pink-50': 'bg-pink-50 text-pink-600',
  'purple-50': 'bg-purple-50 text-purple-600',
  'orange-50': 'bg-orange-50 text-orange-600',
  'cyan-50': 'bg-cyan-50 text-cyan-600',
  'yellow-50': 'bg-yellow-50 text-yellow-600',
  'green-50': 'bg-green-50 text-green-600',
};

function flattenIds(items, bucket = []) {
  items.forEach((item) => {
    bucket.push(item.id);
    if (Array.isArray(item.children) && item.children.length > 0) flattenIds(item.children, bucket);
  });
  return bucket;
}

function buildTopLevelSelectionMap(items, bucket = new Map(), topLevelItem = null) {
  items.forEach((item) => {
    const resolvedTopLevelItem = topLevelItem || item;
    bucket.set(item.id, resolvedTopLevelItem);
    if (Array.isArray(item.children) && item.children.length > 0) {
      buildTopLevelSelectionMap(item.children, bucket, resolvedTopLevelItem);
    }
  });
  return bucket;
}

function normalizeSearchText(value) {
  return String(value || '').trim().toLowerCase().replace(/\s+/g, '');
}

function renderHighlightedLabel(label, keyword) {
  const text = String(label || '');
  const normalizedKeyword = normalizeSearchText(keyword);

  if (!normalizedKeyword) return text;

  const normalizedChars = [];
  for (let index = 0; index < text.length; index += 1) {
    const currentChar = text[index];
    if (/\s/.test(currentChar)) continue;
    normalizedChars.push({
      originalIndex: index,
      char: currentChar.toLowerCase(),
    });
  }

  const normalizedText = normalizedChars.map((item) => item.char).join('');
  const matchStart = normalizedText.indexOf(normalizedKeyword);
  if (matchStart === -1) return text;

  const matchEnd = matchStart + normalizedKeyword.length - 1;
  const startIndex = normalizedChars[matchStart]?.originalIndex ?? 0;
  const endIndex = normalizedChars[matchEnd]?.originalIndex ?? (text.length - 1);

  return (
    <>
      {startIndex > 0 ? text.slice(0, startIndex) : null}
      <span className="text-brand-500">{text.slice(startIndex, endIndex + 1)}</span>
      {endIndex + 1 < text.length ? text.slice(endIndex + 1) : null}
    </>
  );
}

function filterTreeItems(items, keyword) {
  if (!keyword) return items;

  return items.reduce((acc, item) => {
    const labelMatched = normalizeSearchText(item.label).includes(keyword);
    const children = Array.isArray(item.children) ? item.children : [];
    const nextChildren = children.length > 0 ? filterTreeItems(children, keyword) : [];

    if (labelMatched) {
      acc.push({ ...item, children });
      return acc;
    }

    if (nextChildren.length > 0) {
      acc.push({ ...item, children: nextChildren });
    }

    return acc;
  }, []);
}

function collectExpandableIds(items, bucket = []) {
  items.forEach((item) => {
    if (Array.isArray(item.children) && item.children.length > 0) {
      bucket.push(item.id);
      collectExpandableIds(item.children, bucket);
    }
  });
  return bucket;
}

function toggleId(list, id) {
  return list.includes(id) ? list.filter((item) => item !== id) : [...list, id];
}

function clampWidth(value, min, max) {
  return Math.min(Math.max(value, min), max);
}

function getBusiness(businesses, businessId) {
  return businesses.find((item) => item.id === businessId) || businesses[0] || { id: 'douyin-community', label: '抖音社区', iconSrc: DEFAULT_BUSINESS_ICON };
}

function getRowMetrics(depth) {
  if (depth <= 0) return { height: 36, paddingLeft: 12, fontClass: 'text-sm [font-weight:var(--font-semibold)] leading-5', textWidth: 'min-w-0 truncate' };
  if (depth === 1) return { height: 32, paddingLeft: 36, fontClass: 'text-sm font-normal leading-5', textWidth: 'min-w-0 flex-1 truncate' };
  return { height: 32, paddingLeft: 60, fontClass: 'text-xs font-normal leading-4', textWidth: 'min-w-0 flex-1 truncate' };
}

function getGuidePositions(depth) {
  if (depth === 1) return [19.5];
  if (depth >= 2) return [19.5, 43.5];
  return [];
}

/** 子树容器内连续竖线（与各行 padding 对齐；高度随子块内容自适应，避免逐行 border 拼接缝） */
function TreeChildBlockGuides({ parentDepth }) {
  const childDepth = parentDepth + 1;
  const positions = getGuidePositions(childDepth);
  if (positions.length === 0) return null;

  return (
    <div className="pointer-events-none absolute inset-0 z-0 overflow-hidden" aria-hidden>
      {positions.map((left, index) => (
        <span
          key={`tree-guide-${parentDepth}-${index}`}
          className="absolute top-0 bottom-0 border-l border-border-default"
          style={{ left: `${left}px` }}
        />
      ))}
    </div>
  );
}

function BusinessIcon({ iconName }) {
  return (
    <span className="inline-flex h-5 w-5 shrink-0 items-center justify-center overflow-hidden rounded-[4px] bg-grey-950">
      <Icon name={iconName || DEFAULT_BUSINESS_ICON} size="sm" aria-hidden="true" />
    </span>
  );
}

function SearchField({ value, onChange, autoFocus = false }) {
  return (
    <Input
      className="!w-full"
      prefix={<Icon name="search-lg-stroked" size="sm" aria-hidden="true" />}
      placeholder="搜索标签"
      value={value}
      onChange={onChange}
      autoFocus={autoFocus}
      aria-label="搜索标签"
    />
  );
}

function BusinessSwitcher({
  businesses,
  currentBusinessId,
  selected,
  onSelect,
  onBusinessChange,
}) {
  const currentBusiness = getBusiness(businesses, currentBusinessId);
  const items = useMemo(
    () => businesses.map((business) => ({
      id: business.id,
      value: business.id,
      label: business.label,
      iconName: business.iconSrc,
    })),
    [businesses],
  );

  return (
    <div className="relative" data-tfds-component="TagBar.BusinessSwitcher">
      <div className={`${BUSINESS_ROW} ${selected ? 'bg-fill' : 'hover:bg-fill'}`}>
        <button
          type="button"
          className="flex min-w-0 flex-1 items-center gap-2 text-left"
          aria-label={currentBusiness.label}
          aria-pressed={selected}
          onClick={onSelect}
          data-tfds-component="TagBar.BusinessSwitcher"
        >
          <BusinessIcon iconName={currentBusiness.iconSrc} />
          <span className="min-w-0 truncate text-sm [font-weight:var(--font-semibold)] leading-5 text-foreground">{currentBusiness.label}</span>
        </button>

        <Dropdown
          className="inline-flex shrink-0"
          selectionIndicator="leading-check"
          showIcon
          selectedValue={currentBusiness.id}
          items={items}
          onItemSelect={(item) => onBusinessChange(item.value ?? item.id)}
          trigger={(
            <Button
              variant="ghost-black"
              size="sm"
              radius="rounded"
              iconOnly
              icon={<Icon name="switch-horizontal-01-stroked" size="xs" className="text-foreground-muted" aria-hidden="true" />}
              className="shrink-0"
              tooltip="切换业务线"
              aria-label="切换业务线"
              data-tfds-component="TagBar.BusinessToggle"
            />
          )}
        />
      </div>
    </div>
  );
}

function TagBarNode({
  item,
  depth,
  collapsed,
  expandedIdSet,
  selectedItemId,
  highlightedKeyword,
  onToggleExpand,
  onSelect,
}) {
  const metrics = getRowMetrics(depth);
  const hasChildren = Array.isArray(item.children) && item.children.length > 0;
  const isExpanded = expandedIdSet.has(item.id);
  const showChevron = depth <= 1;
  const chevronName = hasChildren && isExpanded ? 'chevron-down-stroked' : 'chevron-up-stroked';
  const iconTone = ICON_BG_CLASS[item.iconBgToken] || ICON_BG_CLASS['blueGrey-100'];
  const isSelected = selectedItemId === item.id;
  const isSecondary = depth === 1;
  const rowBgWrapClass = depth === 0 ? ROW_BG_WRAP_PRIMARY : (depth >= 2 ? ROW_BG_WRAP_TERTIARY : ROW_BG_WRAP_SECONDARY);
  const rowContentClass = depth === 0 ? ROW_CONTENT_PRIMARY : (depth >= 2 ? ROW_CONTENT_TERTIARY : ROW_CONTENT);
  const chevronAriaLabel = isExpanded ? `收起${item.label}` : `展开${item.label}`;

  if (collapsed) {
    return (
      <button
        type="button"
        className={`inline-flex h-8 w-9 items-center justify-center rounded-[8px] transition-colors tfds-motion-ui-fast hover:bg-fill focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-200 ${isSelected ? 'bg-fill' : ''}`}
        aria-label={item.label}
        title={item.label}
        onClick={() => onSelect(item, depth)}
        data-tfds-component="TagBar.Node"
      >
        {depth === 0 ? (
          <span className={`inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-[4px] ${iconTone}`}>
            <Icon name={item.iconName || 'globe-01-stroked'} size="xs" aria-hidden="true" />
          </span>
        ) : null}
      </button>
    );
  }

  return (
    <div className="relative z-[1] flex flex-col gap-[2px]">
      <button
        type="button"
        className={ROW_BASE}
        style={{
          height: `${metrics.height}px`,
          paddingLeft: depth === 0 ? '0px' : `${metrics.paddingLeft}px`,
          paddingRight: depth === 0 ? '0px' : '0px',
        }}
        aria-expanded={hasChildren ? isExpanded : undefined}
        aria-label={item.label}
        onClick={() => onSelect(item, depth)}
        data-tfds-component="TagBar.Node"
      >
        <span className={`${rowBgWrapClass} ${!isSecondary && isSelected ? 'bg-fill' : ''}`}>
          {isSecondary ? <span className={`${ROW_BG_LAYER_SECONDARY} ${isSelected ? 'bg-fill' : ''}`} aria-hidden="true" /> : null}
          <span className={rowContentClass}>
            {showChevron ? (
              hasChildren ? (
                <button
                  type="button"
                  className={CHEVRON_BUTTON_CLASS}
                  aria-label={chevronAriaLabel}
                  aria-expanded={isExpanded}
                  onClick={(event) => {
                    event.preventDefault();
                    event.stopPropagation();
                    onToggleExpand(item.id);
                  }}
                  data-tfds-component="TagBar.Expand"
                >
                  <Icon name={chevronName} size="xs" aria-hidden="true" />
                </button>
              ) : (
                <span className={CHEVRON_BUTTON_CLASS} aria-hidden="true">
                  <Icon name={chevronName} size="xs" aria-hidden="true" />
                </span>
              )
            ) : null}

            {depth === 0 && item.iconName ? (
              <span className={`inline-flex h-5 w-5 shrink-0 items-center justify-center rounded-[4px] ${iconTone}`}>
                <Icon name={item.iconName} size="xs" aria-hidden="true" />
              </span>
            ) : null}

            <span className={`${metrics.textWidth} ${metrics.fontClass}`}>{renderHighlightedLabel(item.label, highlightedKeyword)}</span>
          </span>
        </span>
      </button>

      {hasChildren && isExpanded ? (
        <div className="relative flex flex-col gap-[2px]">
          <TreeChildBlockGuides parentDepth={depth} />
          {item.children.map((child) => (
            <TagBarNode
              key={child.id}
              item={child}
              depth={depth + 1}
              collapsed={collapsed}
              expandedIdSet={expandedIdSet}
              selectedItemId={selectedItemId}
              highlightedKeyword={highlightedKeyword}
              onToggleExpand={onToggleExpand}
              onSelect={onSelect}
            />
          ))}
        </div>
      ) : null}
    </div>
  );
}

export default function TagBar({
  businesses = null,
  currentBusinessId,
  defaultBusinessId = null,
  onBusinessChange = null,
  items = null,
  selectedItemId,
  defaultSelectedItemId = 'account-registration-login-field-1',
  onSelect = null,
  expandedIds,
  defaultExpandedIds = ['account', 'account-registration-login'],
  onExpandedChange = null,
  searchable = true,
  searchValue,
  defaultSearchValue = '',
  onSearchChange = null,
  collapsible = true,
  collapsed,
  defaultCollapsed = false,
  onCollapsedChange = null,
  width,
  defaultWidth = DEFAULT_EXPANDED_WIDTH,
  minWidth = DEFAULT_MIN_WIDTH,
  maxWidth = DEFAULT_MAX_WIDTH,
  collapseThreshold = DEFAULT_COLLAPSE_THRESHOLD,
  onWidthChange = null,
  className = '',
  style,
}) {
  const resolvedBusinesses = Array.isArray(businesses) && businesses.length > 0 ? businesses : TAGBAR_SAMPLE_BUSINESSES;
  const resolvedItems = Array.isArray(items) && items.length > 0 ? items : TAGBAR_SAMPLE_ITEMS;
  const validItemIds = useMemo(() => flattenIds(resolvedItems, []), [resolvedItems]);
  const topLevelSelectionMap = useMemo(() => buildTopLevelSelectionMap(resolvedItems), [resolvedItems]);

  const isBusinessControlled = currentBusinessId !== undefined;
  const isSelectedControlled = selectedItemId !== undefined;
  const isExpandedControlled = expandedIds !== undefined;
  const isSearchControlled = searchValue !== undefined;
  const isCollapsedControlled = collapsed !== undefined;
  const isWidthControlled = width !== undefined;
  const normalizedMaxWidth = Math.max(maxWidth, minWidth, DEFAULT_COLLAPSED_WIDTH);
  const normalizedCollapseThreshold = Math.min(Math.max(collapseThreshold, DEFAULT_COLLAPSED_WIDTH), normalizedMaxWidth);
  const normalizedDefaultWidth = clampWidth(defaultWidth, DEFAULT_COLLAPSED_WIDTH, normalizedMaxWidth);

  const [innerBusinessId, setInnerBusinessId] = useState(defaultBusinessId || resolvedBusinesses[0]?.id || null);
  const [innerSelectedItemId, setInnerSelectedItemId] = useState(defaultSelectedItemId);
  const [innerExpandedIds, setInnerExpandedIds] = useState(Array.isArray(defaultExpandedIds) ? defaultExpandedIds : ['account', 'account-registration-login']);
  const [innerSearchValue, setInnerSearchValue] = useState(defaultSearchValue);
  const [innerManualCollapsed, setInnerManualCollapsed] = useState(defaultCollapsed);
  const [innerAutoCollapsed, setInnerAutoCollapsed] = useState(false);
  const [innerWidth, setInnerWidth] = useState(normalizedDefaultWidth);
  const [lastExpandedWidth, setLastExpandedWidth] = useState(Math.max(normalizedDefaultWidth, minWidth));
  const [focusSearchOnExpand, setFocusSearchOnExpand] = useState(false);

  const resolvedBusinessId = isBusinessControlled ? currentBusinessId : innerBusinessId;
  const resolvedSelectedItemId = isSelectedControlled ? selectedItemId : innerSelectedItemId;
  const resolvedExpandedIds = isExpandedControlled ? (Array.isArray(expandedIds) ? expandedIds : []) : innerExpandedIds;
  const resolvedSearchValue = isSearchControlled ? searchValue : innerSearchValue;
  const resolvedTargetWidth = isWidthControlled ? clampWidth(width, DEFAULT_COLLAPSED_WIDTH, normalizedMaxWidth) : innerWidth;
  const isCollapsed = isCollapsedControlled ? collapsed : (innerManualCollapsed || innerAutoCollapsed);
  const renderedWidth = isCollapsed ? DEFAULT_COLLAPSED_WIDTH : resolvedTargetWidth;

  useEffect(() => {
    if (!resolvedBusinesses.some((item) => item.id === resolvedBusinessId) && !isBusinessControlled) {
      setInnerBusinessId(resolvedBusinesses[0]?.id || null);
    }
  }, [resolvedBusinesses, resolvedBusinessId, isBusinessControlled]);

  useEffect(() => {
    const validSelectableIds = new Set([...validItemIds, ...resolvedBusinesses.map((item) => item.id)]);
    if (resolvedSelectedItemId && !validSelectableIds.has(resolvedSelectedItemId) && !isSelectedControlled) {
      setInnerSelectedItemId(defaultSelectedItemId);
    }
  }, [resolvedSelectedItemId, validItemIds, resolvedBusinesses, isSelectedControlled, defaultSelectedItemId]);

  useEffect(() => {
    if (!isCollapsed && focusSearchOnExpand) {
      setFocusSearchOnExpand(false);
    }
  }, [isCollapsed, focusSearchOnExpand]);

  useEffect(() => {
    if (resolvedTargetWidth > normalizedCollapseThreshold) {
      setLastExpandedWidth((prev) => (prev === resolvedTargetWidth ? prev : resolvedTargetWidth));
    }
  }, [resolvedTargetWidth, normalizedCollapseThreshold]);

  const normalizedKeyword = searchable ? normalizeSearchText(resolvedSearchValue) : '';

  const visibleItems = useMemo(() => (normalizedKeyword ? filterTreeItems(resolvedItems, normalizedKeyword) : resolvedItems), [resolvedItems, normalizedKeyword]);
  const effectiveExpandedIds = useMemo(() => (normalizedKeyword ? collectExpandableIds(visibleItems, []) : resolvedExpandedIds), [normalizedKeyword, visibleItems, resolvedExpandedIds]);
  const expandedIdSet = useMemo(() => new Set(effectiveExpandedIds), [effectiveExpandedIds]);
  const businessIdSet = useMemo(() => new Set(resolvedBusinesses.map((item) => item.id)), [resolvedBusinesses]);

  const currentBusiness = getBusiness(resolvedBusinesses, resolvedBusinessId);
  const collapsedTreeSelectedItemId = useMemo(() => {
    if (!resolvedSelectedItemId || businessIdSet.has(resolvedSelectedItemId)) return null;
    return topLevelSelectionMap.get(resolvedSelectedItemId)?.id || null;
  }, [resolvedSelectedItemId, businessIdSet, topLevelSelectionMap]);
  const renderedSelectedItemId = isCollapsed ? collapsedTreeSelectedItemId : resolvedSelectedItemId;
  const isCollapsedBusinessSelected = isCollapsed && resolvedSelectedItemId === currentBusiness.id;

  const updateWidth = useCallback((nextWidth) => {
    const clampedWidth = clampWidth(nextWidth, DEFAULT_COLLAPSED_WIDTH, normalizedMaxWidth);
    if (!isWidthControlled) {
      setInnerWidth(clampedWidth);
    }
    if (clampedWidth > normalizedCollapseThreshold) {
      setLastExpandedWidth(clampedWidth);
    }
    onWidthChange?.(clampedWidth);
    return clampedWidth;
  }, [isWidthControlled, normalizedCollapseThreshold, normalizedMaxWidth, onWidthChange]);

  const updateCollapsed = useCallback((nextCollapsed, options = {}) => {
    const { source = 'manual', restoreWidth: shouldRestoreWidth = true } = options;
    if (!isCollapsedControlled) {
      if (nextCollapsed) {
        setInnerManualCollapsed(source === 'manual');
        setInnerAutoCollapsed(source === 'auto');
      } else {
        setInnerManualCollapsed(false);
        setInnerAutoCollapsed(false);
      }
    }
    if (!nextCollapsed && shouldRestoreWidth) {
      const nextExpandedWidth = Math.max(lastExpandedWidth, minWidth);
      updateWidth(nextExpandedWidth);
    }
    onCollapsedChange?.(nextCollapsed);
  }, [isCollapsedControlled, lastExpandedWidth, minWidth, onCollapsedChange, updateWidth]);

  const handleSearchChange = useCallback((event) => {
    const nextValue = event.target.value;
    if (!isSearchControlled) setInnerSearchValue(nextValue);
    onSearchChange?.(nextValue);
  }, [isSearchControlled, onSearchChange]);

  const handleBusinessSelect = useCallback((businessId) => {
    const business = resolvedBusinesses.find((item) => item.id === businessId) || null;
    if (!isBusinessControlled) setInnerBusinessId(businessId);
    if (!isSelectedControlled) setInnerSelectedItemId(businessId);
    onBusinessChange?.(businessId, { business });
  }, [resolvedBusinesses, isBusinessControlled, isSelectedControlled, onBusinessChange]);

  const handleSelectBusinessTag = useCallback(() => {
    if (!isSelectedControlled) {
      setInnerSelectedItemId(resolvedBusinessId);
    }
    onSelect?.(resolvedBusinessId, {
      item: getBusiness(resolvedBusinesses, resolvedBusinessId),
      depth: 0,
      businessId: resolvedBusinessId || null,
    });
  }, [isSelectedControlled, onSelect, resolvedBusinesses, resolvedBusinessId]);

  const handleSelectItem = useCallback((item, depth) => {
    if (!isSelectedControlled) {
      setInnerSelectedItemId(item.id);
    }
    onSelect?.(item.id, { item, depth, businessId: resolvedBusinessId || null });

  }, [isSelectedControlled, onSelect, resolvedBusinessId, resolvedExpandedIds, isExpandedControlled, onExpandedChange]);

  const handleToggleExpand = useCallback((itemId) => {
    const nextExpandedIds = toggleId(resolvedExpandedIds, itemId);
    if (!isExpandedControlled) setInnerExpandedIds(nextExpandedIds);
    onExpandedChange?.(nextExpandedIds);
  }, [resolvedExpandedIds, isExpandedControlled, onExpandedChange]);

  const handleSearchButtonClick = useCallback(() => {
    if (isCollapsed) {
      setFocusSearchOnExpand(true);
      updateCollapsed(false);
    }
  }, [isCollapsed, updateCollapsed]);

  return (
    <aside
      className={`${ROOT_BASE} bg-transparent ${className}`.trim()}
      style={{ width: `${renderedWidth}px`, padding: '16px', ...style }}
      aria-label="标签栏"
      data-tfds-component="TagBar"
    >
      <div className={MAIN_WRAP}>
        {isCollapsed && searchable ? (
          <button type="button" className={`${ICON_BUTTON} mt-0`} aria-label="搜索标签" onClick={handleSearchButtonClick} data-tfds-component="TagBar.Search">
            <Icon name="search-lg-stroked" size="sm" aria-hidden="true" />
          </button>
        ) : searchable ? (
          <SearchField value={resolvedSearchValue || ''} onChange={handleSearchChange} autoFocus={focusSearchOnExpand} />
        ) : null}

        <div className={SCROLL_AREA}>
          <div className="relative">
            {isCollapsed ? (
              <button
                type="button"
                className={`inline-flex h-9 w-9 items-center justify-center rounded-[8px] transition-colors tfds-motion-ui-fast ${isCollapsedBusinessSelected ? 'bg-fill' : 'hover:bg-fill'}`}
                aria-label={currentBusiness.label}
                aria-pressed={isCollapsedBusinessSelected}
                onClick={handleSelectBusinessTag}
                data-tfds-component="TagBar.BusinessSwitcher"
              >
                <BusinessIcon iconName={currentBusiness.iconSrc} />
              </button>
            ) : (
              <BusinessSwitcher
                businesses={resolvedBusinesses}
                currentBusinessId={currentBusiness.id}
                selected={resolvedSelectedItemId === currentBusiness.id}
                onSelect={handleSelectBusinessTag}
                onBusinessChange={handleBusinessSelect}
              />
            )}
          </div>

          {visibleItems.length > 0 ? (
            <div className="flex flex-col gap-[2px]">
              {visibleItems.map((item) => (
                <TagBarNode
                  key={item.id}
                  item={item}
                  depth={0}
                  collapsed={isCollapsed}
                  expandedIdSet={expandedIdSet}
                  selectedItemId={renderedSelectedItemId}
                  highlightedKeyword={normalizedKeyword}
                  onToggleExpand={handleToggleExpand}
                  onSelect={handleSelectItem}
                />
              ))}
            </div>
          ) : (
            <div className={`${isCollapsed ? 'h-8 w-9' : 'px-3 py-6'} flex items-center justify-center rounded-[8px] bg-fill text-center text-[12px] leading-4 text-foreground-muted`}>
              {isCollapsed ? <Icon name="search-lg-stroked" size="xs" aria-hidden="true" /> : '未找到匹配标签'}
            </div>
          )}
        </div>
      </div>

      {collapsible ? (
        <div className="mt-2">
          <Button
            variant="ghost-black"
            size="md"
            radius="rounded"
            iconOnly
            icon={<Icon name="layout-left-stroked" size="sm" aria-hidden="true" />}
            tooltip={isCollapsed ? '展开标签栏' : '收起标签栏'}
            aria-label={isCollapsed ? '展开标签栏' : '收起标签栏'}
            onClick={() => updateCollapsed(!isCollapsed)}
            data-tfds-component="TagBar.CollapseToggle"
          />
        </div>
      ) : null}
    </aside>
  );
}
