import { messageBubbleEnterClass } from '../motion-bubble-enter.js';
import Avatar from './Avatar';
import Icon from './Icon';
import Tooltip from './Tooltip';
/* ── 基础容器 ── */
const BASE = 'tfds-chat-bubble grid w-full items-start gap-x-2';
const BASE_STYLE = {
  gridTemplateColumns: 'var(--spacing-6) minmax(0, 1fr) var(--spacing-6)',
};

/* ── 头像槽 ── */
const AVATAR_SLOT = 'flex h-[var(--spacing-6)] w-[var(--spacing-6)] items-start';
const EMPTY_SLOT = 'h-[var(--spacing-6)] w-[var(--spacing-6)]';

/* ── 角色 → 消息列排列 ── */
const STACK_CLASS = {
  incoming: 'flex min-w-0 flex-col items-start',
  outgoing: 'flex min-w-0 flex-col items-end',
};

/* ── 数量 → 气泡组间距 ── */
const LAYOUT_CLASS = {
  single: 'gap-0',
  multiple: 'gap-1',
};

/* ── 尺寸 → 气泡内边距 ── */
const SIZE_CLASS = {
  default: 'px-3 py-2',
  large: 'px-4 py-3',
};

/* ── 角色 → 单条消息行排列 ── */
const ROW_CLASS = {
  incoming: 'group relative flex max-w-[var(--size-chat-bubble-max)] items-end justify-start',
  outgoing: 'group relative flex max-w-[var(--size-chat-bubble-max)] items-end gap-1',
};

/* ── 气泡底色（左右各自四档：绿 / 白 / 灰 / AI 渐变）── */
const BUBBLE_AI_STYLE = { background: 'var(--bg-chat-outgoing-ai)' };

const INCOMING_TONE_CLASS = {
  default: 'rounded-bl-lg rounded-br-lg rounded-tr-lg bg-chat-incoming',
  white: 'rounded-bl-lg rounded-br-lg rounded-tr-lg bg-surface',
  grey: 'rounded-bl-lg rounded-br-lg rounded-tr-lg bg-fill',
  ai: 'rounded-bl-lg rounded-br-lg rounded-tr-lg',
};

const OUTGOING_TONE_CLASS = {
  default: 'rounded-bl-lg rounded-br-lg rounded-tl-lg bg-chat-outgoing',
  white: 'rounded-bl-lg rounded-br-lg rounded-tl-lg bg-surface',
  grey: 'rounded-bl-lg rounded-br-lg rounded-tl-lg bg-fill',
  ai: 'rounded-bl-lg rounded-br-lg rounded-tl-lg',
};

/* ── 文字 ── */
const TEXT = 'm-0 w-full text-sm font-normal leading-5 text-foreground';
const TIMESTAMP_TOP = 'absolute bottom-full z-10 mb-[var(--spacing-0_5)] flex items-center gap-1 whitespace-nowrap opacity-0 transition-opacity tfds-motion-ui-fast group-hover:opacity-100';
const TIMESTAMP_TOP_ALIGN = {
  incoming: 'left-0 justify-start',
  outgoing: 'right-0 justify-end',
};
const TIMESTAMP_SIDE = 'absolute top-1/2 z-10 flex -translate-y-1/2 items-center gap-1 whitespace-nowrap opacity-0 transition-opacity tfds-motion-ui-fast group-hover:opacity-100';
const TIMESTAMP_SIDE_ALIGN = {
  incoming: 'right-full mr-[var(--spacing-2)] justify-end',
  outgoing: 'left-full ml-[var(--spacing-2)] justify-start',
};
const TIMESTAMP_TEXT = 'text-xs font-normal leading-4 text-foreground-muted';
const TIMESTAMP_DATE = 'inline-flex cursor-default items-center';

/* ── 已读回执 ── */
const RECEIPT_CLASS = {
  hidden: 'hidden',
  read: [
    'inline-flex h-[var(--spacing-3)] w-[var(--spacing-3)] shrink-0 items-center justify-center',
    'text-success',
  ].join(' '),
};

/* ── 头像 → 复用 Avatar 组件的类型映射 ── */
const AVATAR_PROPS = {
  image: { type: 'image' },
  robot: { type: 'robot' },
  ai: { type: 'ai' },
  none: null,
};

const BUBBLE_TONE_KEYS = new Set(['default', 'white', 'grey', 'ai']);

function normalizeBubbleTone(value, fallback) {
  return BUBBLE_TONE_KEYS.has(value) ? value : fallback;
}

const DEFAULT_MESSAGES = {
  incoming: {
    single: ['为什么我无法设置点赞列表查看权限'],
    multiple: ['为什么我无法设置点赞列表查看权限', '那怎么看自己的直播回放？'],
  },
  outgoing: {
    single: ['您好，因产品功能更新，“作品点赞信息”设置开关正逐步下线中'],
    multiple: [
      '您好，因产品功能更新，“作品点赞信息”设置开关正逐步下线中',
      '目前不支持设置“作品点赞信息”权限。该功能下线后，您发布的作品的点赞列表仅对您自己可见。',
    ],
  },
};

function pad2(value) {
  return String(value).padStart(2, '0');
}

function createTimestampMeta(rawValue) {
  if (typeof rawValue !== 'string') {
    return null;
  }

  const value = rawValue.trim();
  if (!value) {
    return null;
  }

  const fullDatePatterns = [
    /^(\d{4})年(\d{1,2})月(\d{1,2})日\s*(\d{1,2}):(\d{2})(?::(\d{2}))?$/,
    /^(\d{4})[-/.](\d{1,2})[-/.](\d{1,2})(?:\s+|T)(\d{1,2}):(\d{2})(?::(\d{2}))?$/,
  ];
  const monthDayPattern = /^(\d{1,2})月(\d{1,2})日\s*(\d{1,2}):(\d{2})(?::(\d{2}))?$/;
  const timePattern = /^(\d{1,2}):(\d{2})(?::(\d{2}))?$/;

  for (const pattern of fullDatePatterns) {
    const match = value.match(pattern);
    if (!match) continue;

    const [, year, month, day, hour, minute, second = '00'] = match;
    const monthLabel = `${Number(month)}月${Number(day)}日`;
    const timeLabel = `${pad2(hour)}:${minute}`;
    return {
      summary: `${monthLabel} ${timeLabel}`,
      dateLabel: monthLabel,
      timeLabel,
      detail: `${year}年${Number(month)}月${Number(day)}日 ${pad2(hour)}:${minute}:${pad2(second)}`,
    };
  }

  const monthDayMatch = value.match(monthDayPattern);
  if (monthDayMatch) {
    const [, month, day, hour, minute, second] = monthDayMatch;
    const monthLabel = `${Number(month)}月${Number(day)}日`;
    const timeLabel = `${pad2(hour)}:${minute}`;
    return {
      summary: `${monthLabel} ${timeLabel}`,
      dateLabel: monthLabel,
      timeLabel,
      detail: second ? `${monthLabel} ${pad2(hour)}:${minute}:${pad2(second)}` : null,
    };
  }

  const timeMatch = value.match(timePattern);
  if (timeMatch) {
    const [, hour, minute, second] = timeMatch;
    return {
      summary: `${pad2(hour)}:${minute}`,
      dateLabel: null,
      timeLabel: `${pad2(hour)}:${minute}`,
      detail: second ? `${pad2(hour)}:${minute}:${pad2(second)}` : null,
    };
  }

  return {
    summary: value,
    dateLabel: null,
    timeLabel: value,
    detail: null,
  };
}

function getTimestampPlacement(layout, index) {
  return layout === 'multiple' && index > 0 ? 'side' : 'top';
}

function renderTimestampDetail(text, detail) {
  if (!detail) {
    return <span className={TIMESTAMP_TEXT}>{text}</span>;
  }

  return (
    <Tooltip content={detail} placement="top" triggerClassName={TIMESTAMP_DATE}>
      <span className={TIMESTAMP_TEXT}>{text}</span>
    </Tooltip>
  );
}

function TimestampLabel({ value, align = 'incoming', placement = 'top', showDate = true }) {
  const meta = createTimestampMeta(value);

  if (!meta?.summary) {
    return null;
  }

  const dateLabel = showDate ? meta.dateLabel : null;
  const showDetailTooltip = Boolean(meta.detail && meta.detail !== meta.summary);

  const containerClass = placement === 'side'
    ? [TIMESTAMP_SIDE, TIMESTAMP_SIDE_ALIGN[align]]
    : [TIMESTAMP_TOP, TIMESTAMP_TOP_ALIGN[align]];

  return (
    <div className={containerClass.join(' ')}>
      {dateLabel ? (
        renderTimestampDetail(dateLabel, showDetailTooltip ? meta.detail : null)
      ) : null}

      {meta.timeLabel ? (
        showDetailTooltip && !dateLabel ? (
          renderTimestampDetail(meta.timeLabel, meta.detail)
        ) : (
          <span className={TIMESTAMP_TEXT}>{meta.timeLabel}</span>
        )
      ) : null}
    </div>
  );
}

/**
 * ChatBubble — B端业务 IM 聊天气泡
 * @prop {'incoming'|'outgoing'} [variant='incoming'] — 气泡角色：incoming 为用户左侧，outgoing 为客服右侧
 * @prop {'single'|'multiple'} [layout='single'] — 气泡数量偏好；当传入多条消息时，组件会自动按 multiple 连续气泡布局展示
 * @prop {'image'|'robot'|'ai'|'none'} [avatarType='image'] — 头像类型，支持图片 / 机器人 / AI头像 / 不显示头像
 * @prop {'default'|'large'} [size='default'] — 气泡尺寸：default 为 12px/8px 内边距，large 为 16px/12px 内边距
 * @prop {'default'|'white'|'grey'|'ai'} [incomingTone='default'] — 左侧气泡：default 浅灰透明底，white 白底，grey 为 fill-default 填充底，ai 同右列 AI 渐变
 * @prop {'default'|'white'|'grey'|'ai'} [outgoingTone] — 右侧气泡；未传时回退到 `tone`
 * @prop {'default'|'white'|'grey'|'ai'} [tone='default'] — 兼容旧 API，等价于仅设置右侧 `outgoingTone`
 * @prop {'hidden'|'read'} [receipt='hidden'] — 右侧消息回执：hidden 隐藏，read 显示已读徽标
 * @prop {string[]|null} [messages=null] — 消息文案数组；为空时使用内置示例文案
 * @prop {string[]|null} [timestamps=null] — 消息时间数组；单条和连续首条消息 hover 时显示“月日 + 时间”，连续消息后续条目改为在气泡侧边显示
 * @prop {string|null} [avatarSrc=null] — 图片头像地址，仅 avatarType="image" 时生效；不传时由 Avatar 默认随机使用本地成员头像图片素材
 * @prop {string} [avatarAlt='头像'] — 头像替代文本
 * @prop {string} [className=''] — 附加类名
 * @prop {boolean} [bubbleEnter=true] — 挂载时是否播放气泡入场动效
 * @prop {React.CSSProperties|undefined} [style=undefined] — 附加内联样式
 *
 * 组件复用 Avatar 负责头像本体，复用 Icon 负责已读徽标中的勾形图标；
 * 气泡本体覆盖 Figma 中的左侧浅灰气泡、右侧浅青气泡，以及右侧连续消息的回执布局。
 */
export default function ChatBubble({
  variant = 'incoming',
  layout = 'single',
  avatarType = 'image',
  size = 'default',
  incomingTone = 'default',
  outgoingTone,
  tone = 'default',
  receipt = 'hidden',
  messages = null,
  timestamps = null,
  avatarSrc = null,
  avatarAlt = '头像',
  className = '',
  bubbleEnter = true,
  style,
}) {
  const resolvedVariant = variant === 'outgoing' ? 'outgoing' : 'incoming';
  const preferredLayout = Object.prototype.hasOwnProperty.call(LAYOUT_CLASS, layout) ? layout : 'single';
  const resolvedSize = Object.prototype.hasOwnProperty.call(SIZE_CLASS, size) ? size : 'default';
  const resolvedReceipt = Object.prototype.hasOwnProperty.call(RECEIPT_CLASS, receipt) ? receipt : 'hidden';
  const preferredAvatarType = Object.prototype.hasOwnProperty.call(AVATAR_PROPS, avatarType) ? avatarType : 'image';
  const resolvedIncomingTone = normalizeBubbleTone(incomingTone, 'default');
  const resolvedOutgoingTone = normalizeBubbleTone(outgoingTone ?? tone, 'default');
  /* 非 none：显式 AI 头像或 AI 色气泡时，头像与渐变底联动；none 时不升档为 AI 头像 */
  let effIncoming = resolvedIncomingTone;
  let effOutgoing = resolvedOutgoingTone;
  if (resolvedVariant === 'outgoing') {
    effOutgoing = (preferredAvatarType === 'ai' || resolvedOutgoingTone === 'ai') ? 'ai' : resolvedOutgoingTone;
  } else {
    effIncoming = (preferredAvatarType === 'ai' || resolvedIncomingTone === 'ai') ? 'ai' : resolvedIncomingTone;
  }
  /* AI 色气泡可与 AI 头像联动；显式隐藏头像时不展示头像，仅保留渐变底与空槽位对齐 */
  const resolvedAvatarType = preferredAvatarType === 'none'
    ? 'none'
    : (effOutgoing === 'ai' && resolvedVariant === 'outgoing')
      || (effIncoming === 'ai' && resolvedVariant === 'incoming')
      ? 'ai'
      : preferredAvatarType === 'ai'
        ? 'ai'
        : preferredAvatarType;

  const hasCustomMessages = Array.isArray(messages) && messages.length > 0;
  const actualLayout = hasCustomMessages && messages.length > 1 ? 'multiple' : preferredLayout;
  const fallbackMessages = DEFAULT_MESSAGES[resolvedVariant][actualLayout];
  const sourceMessages = hasCustomMessages ? messages : fallbackMessages;
  const resolvedMessages = actualLayout === 'single' ? sourceMessages.slice(0, 1) : sourceMessages;
  const resolvedTimestamps = Array.isArray(timestamps) ? timestamps : [];
  const avatarConfig = AVATAR_PROPS[resolvedAvatarType];
  const showReceipt = resolvedVariant === 'outgoing' && resolvedReceipt === 'read';
  const mergedStyle = { ...BASE_STYLE, ...style };
  const bubbleEnterClass = bubbleEnter !== false
    ? messageBubbleEnterClass(resolvedVariant)
    : '';
  const avatarNode = avatarConfig ? (
    <Avatar
      size="xs"
      type={avatarConfig.type}
      src={resolvedAvatarType === 'image' ? avatarSrc : null}
      alt={avatarAlt}
    />
  ) : null;

  return (
    <div className={[BASE, bubbleEnterClass, className].filter(Boolean).join(' ')} style={mergedStyle} data-tfds-component="ChatBubble">
      {resolvedVariant === 'incoming' ? (
        <>
          <div className={AVATAR_SLOT}>
            {avatarNode}
          </div>

          <div className={[STACK_CLASS.incoming, LAYOUT_CLASS[actualLayout]].join(' ')}>
            {resolvedMessages.map((message, index) => {
              const placement = getTimestampPlacement(actualLayout, index);
              const showDate = index === 0;
              return (
                <div
                  key={`${message}-${index}`}
                  className={ROW_CLASS.incoming}
                  data-tfds-component="ChatBubble.Message"
                >
                  <TimestampLabel
                    value={resolvedTimestamps[index]}
                    align="incoming"
                    placement={placement}
                    showDate={showDate}
                  />
                  <div
                    className={[INCOMING_TONE_CLASS[effIncoming], SIZE_CLASS[resolvedSize]].join(' ')}
                    style={effIncoming === 'ai' ? BUBBLE_AI_STYLE : undefined}
                    data-tfds-component="ChatBubble.Bubble"
                  >
                    <p className={TEXT}>{message}</p>
                  </div>
                </div>
              );
            })}
          </div>

          <div className={EMPTY_SLOT} aria-hidden="true" />
        </>
      ) : (
        <>
          <div className={EMPTY_SLOT} aria-hidden="true" />

          <div className={[STACK_CLASS.outgoing, LAYOUT_CLASS[actualLayout]].join(' ')}>
            {resolvedMessages.map((message, index) => {
              const placement = getTimestampPlacement(actualLayout, index);
              const showDate = index === 0;
              return (
                <div
                  key={`${message}-${index}`}
                  className={ROW_CLASS.outgoing}
                  data-tfds-component="ChatBubble.Message"
                >
                  <TimestampLabel
                    value={resolvedTimestamps[index]}
                    align="outgoing"
                    placement={placement}
                    showDate={showDate}
                  />
                  {showReceipt ? (
                    <span className={RECEIPT_CLASS.read} aria-label="已读" data-tfds-component="ChatBubble.Receipt">
                      <Icon name="check-circle-stroked" size="xs" />
                    </span>
                  ) : null}

                  <div
                    className={[OUTGOING_TONE_CLASS[effOutgoing], SIZE_CLASS[resolvedSize]].join(' ')}
                    style={effOutgoing === 'ai' ? BUBBLE_AI_STYLE : undefined}
                    data-tfds-component="ChatBubble.Bubble"
                  >
                    <p className={TEXT}>{message}</p>
                  </div>
                </div>
              );
            })}
          </div>

          <div className={AVATAR_SLOT}>
            {avatarNode}
          </div>
        </>
      )}
    </div>
  );
}
