import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import CustomerServiceWorkspaceFrame from '../components/CustomerServiceWorkspaceFrame';
import ConversationList, { CONVERSATION_LIST_SAMPLE_SECTIONS } from '../components/ConversationList';
import InfoDisplayPanel from '../components/InfoDisplayPanel';
import ChatMessage, { useStreamingTaskGroups } from '../components/ChatMessage';
import Button from '../components/Button';
import Icon from '../components/Icon';
import { getTeamMemberByName } from '../teamMembers';
import IMConversationPattern from './IMConversationPattern';
import { useListInitialLoading } from './pageListShared';
import { tfdsResizeHandleLineClass } from '../shared/resize-handle';

const PANEL_OVERLAP = 32;
const DEFAULT_SIDE_WIDTH = 432;
const AVATAR_ONLY_WIDTH = 88;
const CARD_MIN_WIDTH = 333;
const MAIN_PANEL_MIN_WIDTH = 720;
const MAIN_CONTENT_GAP = 16;
const IM_PANEL_MIN_WIDTH = 360;
const INFO_PANEL_MIN_WIDTH = 320;
const INFO_PANEL_DEFAULT_WIDTH = 380;
const MAIN_PANEL_CONTENT = [
  'relative flex size-full min-h-0 min-w-0 items-stretch gap-4 p-4',
].join(' ');
const IM_PANEL = 'flex min-w-0 flex-1 overflow-hidden bg-surface';
const INFO_PANEL_FRAME = 'relative flex h-full min-w-0 shrink-0 items-stretch overflow-visible';
const INFO_PANEL_RESIZE_HANDLE = [
  'group/workspace-info-resize absolute inset-y-0 left-0 z-20 flex w-2 -translate-x-1/2 cursor-col-resize items-stretch justify-center border-0 bg-transparent p-0',
  'touch-none select-none',
  'focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-blueGrey-400',
].join(' ');
const INFO_PANEL_RESIZE_HANDLE_LINE = tfdsResizeHandleLineClass('workspace-info-resize');
const WORKSPACE_CONVERSATION_SECTIONS = CONVERSATION_LIST_SAMPLE_SECTIONS;
const DEFAULT_CONVERSATION_ID = 'pending-1';
const AGENT_AVATAR = getTeamMemberByName('段然');
const SCRIPT_DATE = '2026-04-16';

function ts(hhmm, ss = '00') {
  return `${SCRIPT_DATE} ${hhmm}:${ss}`;
}

const WORKSPACE_THREAD_CONTENT = {
  'pending-1': {
    tagLabel: '待干预',
    channel: '抖音生活服务',
    duration: '00:18:57',
    messages: [
      { id: 'pending-1-s1', kind: 'system', text: '已接入AI智能客服', time: ts('14:01') },
      { id: 'pending-1-u1', kind: 'user', text: '我买了海底捞西单店的 2 人餐，到店以后店员说券码暂时核销不了，能帮我看下吗', time: ts('14:02', '12') },
      { id: 'pending-1-b1', kind: 'bot', text: '您好，我是抖音生活服务助手，正在为您查询团购券状态…', time: ts('14:02', '25') },
      { id: 'pending-1-b2', kind: 'bot', text: '请问可以提供下券码或订单号吗？支持直接复制粘贴。', time: ts('14:02', '38') },
      { id: 'pending-1-u2', kind: 'user', text: 'LS20260423-HDL998877，券码尾号 8877。', time: ts('14:03', '04') },
      { id: 'pending-1-b3', kind: 'bot', text: '已查到该订单为“海底捞火锅 2 人餐”，当前券码状态显示待核销，适用门店包含西单大悦城店。', time: ts('14:03', '28') },
      { id: 'pending-1-u3', kind: 'user', text: '朋友都到了，等太久体验很差，如果不能用是不是只能退款？', time: ts('14:04', '03') },
      { id: 'pending-1-b4', kind: 'bot', text: '我会优先帮您确认门店核销链路；如确认为门店同步延迟，可由商家手动核验订单。', time: ts('14:04', '18') },
      { id: 'pending-1-u4', kind: 'user', text: '那你先帮我联系门店吧，不行就转人工处理。', time: ts('14:05', '26') },
      { id: 'pending-1-s2', kind: 'system', text: '已接入人工客服', time: ts('14:06', '32') },
      { id: 'pending-1-a1', kind: 'agent', text: '您好，我是人工客服小语，已看到您的海底捞团购订单。门店反馈当前收银端券码同步延迟，正在手动核验。', time: ts('14:06', '48') },
      { id: 'pending-1-a2', kind: 'agent', text: '我已备注您正在到店等位，并同步门店优先处理；若核销仍失败，会为您申请等位补偿或原路退款。', time: ts('14:07', '02') },
      { id: 'pending-1-u5', kind: 'user', text: '好的，麻烦快一点，我这边还在等叫号。', time: ts('14:07', '18') },
      { id: 'pending-1-a3', kind: 'agent', text: '没问题，我会持续跟进西单店反馈。若 5 分钟内未解决，我会直接帮您升级商家值班人员处理。', time: ts('14:07', '36') },
      { id: 'pending-1-u6', kind: 'user', text: '门店刚刚还是说系统里没刷出来，我这边还要继续等吗？', time: ts('14:08', '04') },
      { id: 'pending-1-a4', kind: 'agent', text: '我已再次催门店值班经理人工核验，建议您先保留当前排队小票，我这边同步加急处理中。', time: ts('14:08', '22') },
      { id: 'pending-1-u7', kind: 'user', text: '如果最后还是不行，退款会退多久？', time: ts('14:08', '46') },
      { id: 'pending-1-a5', kind: 'agent', text: '若最终无法核销，我会优先为您走原路退款，通常 1-3 个工作日到账，具体以支付渠道为准。', time: ts('14:09', '05') },
      { id: 'pending-1-u8', kind: 'user', text: '那补偿大概多久能确认下来？', time: ts('14:09', '28') },
      { id: 'pending-1-a6', kind: 'agent', text: '若门店确认是核销异常导致等待，我会直接在本会话同步补偿方案，通常当场或当天内即可确认。', time: ts('14:09', '47') },
      { id: 'pending-1-u9', kind: 'user', text: '好，那你先按这个方案帮我备注，我这边继续等等。', time: ts('14:10', '11') },
      { id: 'pending-1-a7', kind: 'agent', text: '好的，已补充备注“到店等待中，优先核销；若失败按补偿/退款方案处理”，有进展我马上告诉您。', time: ts('14:10', '34') },
    ],
  },
  'pending-2': {
    tagLabel: '待干预',
    channel: '抖音商城',
    duration: '00:12:54',
    messages: [
      { id: 'pending-2-s1', kind: 'system', text: '已接入AI智能客服', time: ts('12:45') },
      { id: 'pending-2-u1', kind: 'user', text: '我这单一直显示待发货，已经超过承诺时间了，什么时候能发？', time: ts('12:46', '10') },
      { id: 'pending-2-b1', kind: 'bot', text: '您好，我正在为您查询订单发货节点和商家履约承诺。', time: ts('12:46', '24') },
      { id: 'pending-2-b2', kind: 'bot', text: '订单 2918575148391024 当前仍在仓库拣货，系统显示已触发催发提醒。', time: ts('12:46', '58') },
      { id: 'pending-2-u2', kind: 'user', text: '页面说今天前发货，现在还没动静，我明天就要用。', time: ts('12:48', '06') },
      { id: 'pending-2-b3', kind: 'bot', text: '理解您的着急，我先为您转人工核对仓库和商家侧的实际处理进度。', time: ts('12:48', '32') },
      { id: 'pending-2-s2', kind: 'system', text: '已接入人工客服', time: ts('12:49', '04') },
      { id: 'pending-2-a1', kind: 'agent', text: '您好，我已看到您的发货进度咨询。当前订单已进入出库队列，但物流单号还未回传。', time: ts('12:49', '20') },
      { id: 'pending-2-a2', kind: 'agent', text: '我会帮您向商家发起加急催发，并要求在 2 小时内反馈是否能按时寄出。', time: ts('12:50', '01') },
      { id: 'pending-2-u3', kind: 'user', text: '如果今天发不了可以直接退款吗？', time: ts('12:51', '16') },
      { id: 'pending-2-a3', kind: 'agent', text: '可以。如果商家确认今日无法发出，我会协助您走未按约发货退款流程，并同步保留催发记录。', time: ts('12:51', '45') },
      { id: 'pending-2-u4', kind: 'user', text: '商家那边现在有新的回复吗？', time: ts('12:52', '08') },
      { id: 'pending-2-a4', kind: 'agent', text: '刚收到仓库侧反馈，当前包裹已完成分拣，正在等待承运商揽收并回传物流单号。', time: ts('12:52', '34') },
      { id: 'pending-2-u5', kind: 'user', text: '如果两小时内还是发不出来，就直接帮我走退款吧。', time: ts('12:53', '01') },
      { id: 'pending-2-a5', kind: 'agent', text: '可以，我已备注您的优先诉求：若超时未揽收，将按未按约发货为您优先发起退款申请。', time: ts('12:53', '26') },
      { id: 'pending-2-u6', kind: 'user', text: '我明早出门前一定要确认，不然就来不及用了。', time: ts('12:53', '52') },
      { id: 'pending-2-a6', kind: 'agent', text: '理解，我会在今晚 18 点前再次核对是否已揽收，并把结果同步给您，避免您空等。', time: ts('12:54', '18') },
      { id: 'pending-2-u7', kind: 'user', text: '能帮我备注优先联系仓库吗？', time: ts('12:54', '41') },
      { id: 'pending-2-a7', kind: 'agent', text: '已经备注“优先联系仓库核对实际出库进度”，并补充了您明早急用的场景说明。', time: ts('12:55', '03') },
      { id: 'pending-2-u8', kind: 'user', text: '好的，那我等你消息。', time: ts('12:55', '26') },
      { id: 'pending-2-a8', kind: 'agent', text: '好的，有新进展我会第一时间在本会话回复您，不需要您重复催问。', time: ts('12:55', '44') },
    ],
  },
  'pending-3': {
    tagLabel: '待干预',
    channel: '抖音生活服务',
    duration: '00:09:41',
    messages: [
      { id: 'pending-3-s1', kind: 'system', text: '已接入AI智能客服', time: ts('12:32') },
      { id: 'pending-3-u1', kind: 'user', text: '我领的优惠券结算时用不了，页面一直提示核销失败。', time: ts('12:33', '11') },
      { id: 'pending-3-b1', kind: 'bot', text: '您好，我正在核对优惠券状态、适用范围和当前订单金额。', time: ts('12:33', '28') },
      { id: 'pending-3-b2', kind: 'bot', text: '券码记录显示未使用，但当前订单命中的活动规则存在同步异常。', time: ts('12:34', '02') },
      { id: 'pending-3-u2', kind: 'user', text: '我已经重新进了好几次，还是不行，活动马上结束了。', time: ts('12:35', '14') },
      { id: 'pending-3-b3', kind: 'bot', text: '我先帮您保留当前活动权益，并转人工确认是否可补发同等金额优惠。', time: ts('12:35', '33') },
      { id: 'pending-3-s2', kind: 'system', text: '已接入人工客服', time: ts('12:36', '05') },
      { id: 'pending-3-a1', kind: 'agent', text: '您好，我已看到优惠券核销失败记录。当前问题来自活动规则同步延迟，不是您操作导致。', time: ts('12:36', '18') },
      { id: 'pending-3-a2', kind: 'agent', text: '我会先为您登记权益补偿，若原券无法恢复，会补发一张同面额可用券到您的账号。', time: ts('12:37', '01') },
      { id: 'pending-3-u3', kind: 'user', text: '那我今天还能下单吗？', time: ts('12:38', '12') },
      { id: 'pending-3-a3', kind: 'agent', text: '可以，我会在登记中备注“活动期内核销异常”，补偿券会按当前活动权益处理。', time: ts('12:38', '40') },
      { id: 'pending-3-u4', kind: 'user', text: '补发券一般多久会到账号里？', time: ts('12:39', '02') },
      { id: 'pending-3-a4', kind: 'agent', text: '若走补发方案，通常会在核验完成后当天内到账；若活动高峰期，最晚不超过 24 小时。', time: ts('12:39', '27') },
      { id: 'pending-3-u5', kind: 'user', text: '如果我今天就想下单，还有别的处理办法吗？', time: ts('12:39', '51') },
      { id: 'pending-3-a5', kind: 'agent', text: '我可以先为您登记“活动期核销异常”标记，优先争取按照原活动权益补发同等券后再下单使用。', time: ts('12:40', '18') },
      { id: 'pending-3-u6', kind: 'user', text: '这次活动今晚就结束，我怕到时候补偿也变少。', time: ts('12:40', '44') },
      { id: 'pending-3-a6', kind: 'agent', text: '已理解，我会在登记里注明“按活动结束前权益补偿”，避免后续按普通券面额降级处理。', time: ts('12:41', '10') },
      { id: 'pending-3-u7', kind: 'user', text: '如果原券恢复了，是不是我就直接还能用原来的？', time: ts('12:41', '36') },
      { id: 'pending-3-a7', kind: 'agent', text: '是的，若系统先恢复原券状态，您可以直接继续使用原券；补发流程会自动撤销，不会重复占用权益。', time: ts('12:42', '04') },
      { id: 'pending-3-u8', kind: 'user', text: '好，那我先等等你这边结果。', time: ts('12:42', '29') },
      { id: 'pending-3-a8', kind: 'agent', text: '好的，我会持续盯进度，有恢复结果或补发方案落地会第一时间同步您。', time: ts('12:42', '52') },
      { id: 'pending-3-a9', kind: 'agent', text: '如果 30 分钟内仍未恢复，我会直接升级活动运营侧加急核查。', time: ts('12:43', '16') },
    ],
  },
  'managed-1': {
    tagLabel: '托管中',
    channel: '抖音商城',
    duration: '24:30:00',
    messages: [
      { id: 'managed-1-s1', kind: 'system', text: '已进入托管跟进', time: ts('11:06') },
      { id: 'managed-1-u1', kind: 'user', text: '我的包裹物流一直停在转运中心，没有新的更新。', time: ts('11:07', '18') },
      { id: 'managed-1-b1', kind: 'bot', text: '您好，我正在核对订单 2918575148472390 的承运商轨迹。', time: ts('11:07', '35') },
      { id: 'managed-1-b2', kind: 'bot', text: '系统识别到该包裹已超过常规中转时效，建议转人工托管跟进。', time: ts('11:08', '04') },
      { id: 'managed-1-s2', kind: 'system', text: '已接入人工客服', time: ts('11:08', '28') },
      { id: 'managed-1-a1', kind: 'agent', text: '您好，我已看到物流状态异常记录。当前包裹卡在华东转运中心，我会帮您向承运商发起异常件核查。', time: ts('11:09', '12') },
      { id: 'managed-1-u2', kind: 'user', text: '我还需要做什么吗？会不会丢件？', time: ts('11:10', '19') },
      { id: 'managed-1-a2', kind: 'agent', text: '您暂时不需要额外操作。我会持续托管这个会话，承运商反馈前不会让工单自动关闭。', time: ts('11:10', '42') },
      { id: 'managed-1-a3', kind: 'agent', text: '若 24 小时内仍无轨迹更新，我会为您同步发起补寄或退款方案确认。', time: ts('11:11', '18') },
    ],
  },
  'managed-2': {
    tagLabel: '托管中',
    channel: '抖音商城',
    duration: '00:10:18',
    messages: [
      { id: 'managed-2-s1', kind: 'system', text: '已进入退款托管', time: ts('10:08') },
      { id: 'managed-2-u1', kind: 'user', text: '商家说可以退，但我不确定退款金额是不是全额。', time: ts('10:09', '06') },
      { id: 'managed-2-b1', kind: 'bot', text: '我正在核对订单 2918575148421185 的支付金额、优惠抵扣和售后方案。', time: ts('10:09', '24') },
      { id: 'managed-2-b2', kind: 'bot', text: '当前售后记录显示商家拟同意退款，但需要用户确认退回路径。', time: ts('10:09', '58') },
      { id: 'managed-2-s2', kind: 'system', text: '已接入人工客服', time: ts('10:10', '16') },
      { id: 'managed-2-a1', kind: 'agent', text: '您好，我已看到退款方案。该订单实付金额 128 元，商家同意按实付全额原路退回。', time: ts('10:10', '41') },
      { id: 'managed-2-u2', kind: 'user', text: '优惠券会退回来吗？', time: ts('10:11', '23') },
      { id: 'managed-2-a2', kind: 'agent', text: '本单使用的优惠券属于限时活动券，退款后不会恢复；我已帮您申请同等权益补偿券。', time: ts('10:11', '52') },
      { id: 'managed-2-u3', kind: 'user', text: '那就按这个方案处理吧。', time: ts('10:12', '20') },
      { id: 'managed-2-a3', kind: 'agent', text: '好的，我已记录您确认退款方案，后续退款进度会继续在本会话同步。', time: ts('10:12', '43') },
    ],
  },
  'managed-3': {
    tagLabel: '托管中',
    channel: '抖音商城',
    duration: '00:09:46',
    messages: [
      { id: 'managed-3-s1', kind: 'system', text: '已进入售后凭证核验', time: ts('09:37') },
      { id: 'managed-3-u1', kind: 'user', text: '商家让我补凭证，但我不知道还缺什么材料。', time: ts('09:38', '08') },
      { id: 'managed-3-b1', kind: 'bot', text: '我正在查看售后凭证要求和您已上传的图片。', time: ts('09:38', '30') },
      { id: 'managed-3-b2', kind: 'bot', text: '当前缺少商品外包装破损位置的清晰照片，建议转人工说明补传范围。', time: ts('09:39', '02') },
      { id: 'managed-3-s2', kind: 'system', text: '已接入人工客服', time: ts('09:39', '20') },
      { id: 'managed-3-a1', kind: 'agent', text: '您好，我已查看您提交的售后凭证。目前商家需要补充外包装破损处和商品批次码照片。', time: ts('09:39', '48') },
      { id: 'managed-3-u2', kind: 'user', text: '我可以现在拍，是不是拍了就能继续处理？', time: ts('09:40', '30') },
      { id: 'managed-3-a2', kind: 'agent', text: '是的。您补传后我会继续托管跟进，并提醒商家在 24 小时内完成复核。', time: ts('09:40', '58') },
      { id: 'managed-3-a3', kind: 'agent', text: '如果商家仍以凭证不足拒绝，我会帮您整理平台介入所需材料。', time: ts('09:41', '20') },
    ],
  },
  'other-1': {
    tagLabel: '已处理',
    channel: '抖音商城',
    duration: '00:06:12',
    messages: [
      { id: 'other-1-s1', kind: 'system', text: '订单已完成，发起回访', time: ts('16:02') },
      { id: 'other-1-a1', kind: 'agent', text: '您好，看到您之前咨询的订单 2918575148499712 已处理完成，想和您确认一下结果是否符合预期。', time: ts('16:02', '18') },
      { id: 'other-1-u1', kind: 'user', text: '已经收到退款了，处理结果可以。', time: ts('16:03', '01') },
      { id: 'other-1-a2', kind: 'agent', text: '感谢确认。后续如商家再次联系您补充材料，可以直接在本会话回复，我会继续协助。', time: ts('16:03', '24') },
      { id: 'other-1-u2', kind: 'user', text: '好的，谢谢。', time: ts('16:04', '06') },
      { id: 'other-1-a3', kind: 'agent', text: '不客气，祝您生活愉快。', time: ts('16:04', '22') },
    ],
  },
  'other-2': {
    tagLabel: '已处理',
    channel: '抖音生活服务',
    duration: '00:05:40',
    messages: [
      { id: 'other-2-s1', kind: 'system', text: '门店服务评价回访', time: ts('15:12') },
      { id: 'other-2-u1', kind: 'user', text: '我给门店打了低分，主要是排队时间太长。', time: ts('15:13', '08') },
      { id: 'other-2-b1', kind: 'bot', text: '您好，我已收到您的门店服务评价反馈，正在核对门店履约记录。', time: ts('15:13', '22') },
      { id: 'other-2-s2', kind: 'system', text: '已接入人工客服', time: ts('15:14', '02') },
      { id: 'other-2-a1', kind: 'agent', text: '您好，我已看到您对门店排队时间的反馈。平台会将该问题同步给门店运营侧核查。', time: ts('15:14', '25') },
      { id: 'other-2-u2', kind: 'user', text: '我不是要退款，就是希望后面别这样。', time: ts('15:15', '01') },
      { id: 'other-2-a2', kind: 'agent', text: '理解，已按服务体验问题记录，不会发起退款流程。后续门店整改反馈会在评价治理记录中留存。', time: ts('15:15', '30') },
    ],
  },
};

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

function getConversationItems(sections) {
  return (sections || []).flatMap((section) => section.items || []);
}

function findConversationItem(itemId, sections = WORKSPACE_CONVERSATION_SECTIONS) {
  return getConversationItems(sections).find((item) => item.id === itemId);
}

function getPrimaryTagLabel(item, fallback = '') {
  const tags = Array.isArray(item?.tags) ? item.tags : [];
  const primaryTag = [...tags].reverse().find((tag) => ['red', 'green', 'blue', 'orange'].includes(tag?.variant));
  return primaryTag?.label || tags[0]?.label || fallback;
}

function buildThreadContext(item) {
  const resolvedItem = item || findConversationItem(DEFAULT_CONVERSATION_ID);
  const content = WORKSPACE_THREAD_CONTENT[resolvedItem?.id] || {};

  return {
    id: resolvedItem?.id || DEFAULT_CONVERSATION_ID,
    title: resolvedItem?.title || '当前会话',
    tags: Array.isArray(resolvedItem?.tags) ? resolvedItem.tags : [],
    tagLabel: content.tagLabel || getPrimaryTagLabel(resolvedItem),
    userId: resolvedItem?.orderId || '未知会话',
    channel: content.channel || '抖音',
    duration: content.duration || '00:00:00',
    userName: resolvedItem?.userName || '用户',
    userAvatarSrc: resolvedItem?.avatarSrc,
    agentName: AGENT_AVATAR?.name || '客服',
    agentAvatarSrc: AGENT_AVATAR?.avatarSrc,
  };
}

function buildConversationCardPreviewMessages(messages = []) {
  return messages
    .filter((message) => message?.kind !== 'system')
    .map((message) => ({
      id: `${message.id}-preview`,
      role: message.kind === 'user'
        ? 'user'
        : (message.kind === 'bot' ? 'bot' : 'agent'),
      text: message.text,
    }));
}

/**
 * 客服托管助手 Mock：
 * 完整还原一轮"用户进线 → AI 思考 → 推荐回复"的流程：
 *   1. 顶部展示一条与左侧 IM 完全一致的用户消息气泡（取剧本中最近一条 user 消息）
 *   2. 中部执行流（理解 → 检索 → 生成）按 600ms 节奏流式刷出
 *   3. 末尾推荐回复文本与该用户消息之后客服在左侧实际发出的回复保持一致
 */

function buildTrustModeTaskGroups(latestUserText) {
  const summary = (latestUserText || '').trim();
  const truncated = summary.length > 36 ? `${summary.slice(0, 36)}…` : summary;
  const firstStepDetail = truncated
    ? `User_Intent_Agent /msg "${truncated}"`
    : 'User_Intent_Agent /msg/latest';
  return [
    {
      id: 'trust-understand',
      title: '理解用户最新消息与诉求',
      status: 'completed',
      defaultExpanded: true,
      steps: [
        {
          id: 'trust-understand-parse',
          title: '解析用户最新消息中的核心诉求与情绪',
          actionLabel: '正在调用意图识别 Agent',
          actionDetail: firstStepDetail,
          actionIconName: 'message-question-circle-stroked',
        },
      ],
    },
    {
      id: 'trust-retrieve',
      title: '检索规则、订单上下文与会话历史',
      status: 'completed',
      defaultExpanded: true,
      steps: [
        {
          id: 'trust-retrieve-rules',
          title: '匹配平台售后规则与商家补充条款',
          actionLabel: '正在调用知识库 Agent',
          actionDetail: 'Knowledge_Agent /policy/match',
          actionIconName: 'line-chart-up-04-stroked',
        },
        {
          id: 'trust-retrieve-context',
          title: '汇总订单状态与近 N 轮会话上下文',
          actionLabel: '正在调用上下文 Agent',
          actionDetail: 'Context_Agent /thread/recent',
          actionIconName: 'line-chart-up-04-stroked',
        },
      ],
    },
    {
      id: 'trust-compose',
      title: '生成推荐回复口径',
      status: 'processing',
      defaultExpanded: true,
      steps: [
        {
          id: 'trust-compose-tone',
          title: '校准回复语气：先安抚 → 同步处理 → 给确定方案',
          actionLabel: '正在调用话术 Agent',
          actionDetail: 'Tone_Agent /reply/calibrate',
          actionIconName: 'sticker-square-stroked',
        },
        {
          id: 'trust-compose-draft',
          title: '生成可一键发送的推荐回复文本',
          actionLabel: '正在调用生成 Agent',
          actionDetail: 'Reply_Agent /reply/draft',
          actionIconName: 'code-square-01-stroked',
        },
      ],
    },
  ];
}

/**
 * 找到剧本中最近一条 user 消息，并取其后第一条 agent 回复作为"客服侧已发出的回复"
 * （保证托管助手推荐文案与左侧 IM 客服气泡完全一致）。
 */
function getLatestUserTurn(messages = []) {
  for (let i = messages.length - 1; i >= 0; i -= 1) {
    if (messages[i]?.kind === 'user') {
      const userText = messages[i].text || '';
      let agentReply = '';
      for (let j = i + 1; j < messages.length; j += 1) {
        if (messages[j]?.kind === 'agent') {
          agentReply = messages[j].text || '';
          break;
        }
      }
      return { userText, agentReply };
    }
  }
  return { userText: '', agentReply: '' };
}

const TRUST_MODE_FALLBACK_REPLY = '已读取最新一条用户消息并完成上下文检索，建议先安抚情绪、确认核心诉求，再按平台规则给出统一处理口径。';

function isStreamingFinished(streamed, fullGroups) {
  if (!Array.isArray(streamed) || !Array.isArray(fullGroups)) return false;
  if (streamed.length !== fullGroups.length) return false;
  const lastStreamed = streamed[streamed.length - 1];
  const lastFull = fullGroups[fullGroups.length - 1];
  if (!lastStreamed || !lastFull) return false;
  return (
    lastStreamed.status === 'completed'
    && (lastStreamed.steps?.length ?? 0) >= (lastFull.steps?.length ?? 0)
  );
}

function TrustModeAssistantMessage({ threadId, latestUserText, recommendedReply, onCopyToInput, onSendToUser }) {
  const fullTaskGroups = useMemo(
    () => buildTrustModeTaskGroups(latestUserText),
    [latestUserText],
  );
  const streamed = useStreamingTaskGroups(fullTaskGroups, { intervalMs: 600 });
  const finished = isStreamingFinished(streamed, fullTaskGroups);

  return (
    <div className="flex flex-col gap-3" key={threadId}>
      {latestUserText ? (
        <ChatMessage
          role="user"
          userBubbleTone="fill"
          userContent={[{ type: 'text', value: latestUserText }]}
        />
      ) : null}
      <ChatMessage
        role="ai"
        header={{ name: 'HiAI' }}
        title=""
        steps={null}
        taskGroups={streamed}
      />
      {finished ? (
        <ChatMessage
          role="ai"
          title=""
          steps={null}
          resultText={recommendedReply}
        />
      ) : null}
      {finished ? (
        <div className="flex items-center gap-1">
          <Button
            type="button"
            variant="ghost-black"
            size="sm"
            iconOnly
            tooltip="复制到输入框"
            icon={<Icon name="copy-06-stroked" size="sm" color="currentColor" aria-hidden="true" />}
            onClick={() => onCopyToInput?.(recommendedReply)}
          />
          <Button
            type="button"
            variant="ghost-black"
            size="sm"
            iconOnly
            tooltip="发送给用户"
            icon={<Icon name="send-03-stroked" size="sm" color="currentColor" aria-hidden="true" />}
            onClick={() => onSendToUser?.(recommendedReply)}
          />
        </div>
      ) : null}
    </div>
  );
}

function buildWorkspaceConversationSections(sections = WORKSPACE_CONVERSATION_SECTIONS) {
  return sections.map((section) => ({
    ...section,
    items: (section.items || []).map((item) => {
      const threadContent = WORKSPACE_THREAD_CONTENT[item.id] || {};
      return {
        ...item,
        messages: buildConversationCardPreviewMessages(threadContent.messages || []),
        status: 'replied',
        draftText: '',
      };
    }),
  }));
}

/**
 * CustomerServiceWorkspaceFramePattern — 客服工作台框架
 *
 * 页面级模板：顶部在线 Agent 状态栏 + 指标工具区 + 基础/托管模式切换
 * + 半透明工作区 + 右侧主白卡。模板只定义框架，业务内容通过插槽填充。
 * 在客服工作台语义里，左侧会话列表是当前处理上下文选择器；
 * 右侧主白卡（IM 对话区 + InfoDisplayPanel 信息区）应整体跟随左侧当前选中会话联动变化。
 */

export default function CustomerServiceWorkspaceFramePattern() {
  const listLoading = useListInitialLoading();
  const mainPanelRef = useRef(null);
  const [sideWidth, setSideWidth] = useState(DEFAULT_SIDE_WIDTH);
  const [leftContentMinWidth, setLeftContentMinWidth] = useState(AVATAR_ONLY_WIDTH);
  const [mainPanelWidth, setMainPanelWidth] = useState(0);
  const [infoPanelWidth, setInfoPanelWidth] = useState(INFO_PANEL_DEFAULT_WIDTH);
  const [activeConversationId, setActiveConversationId] = useState(DEFAULT_CONVERSATION_ID);
  const workspaceSections = useMemo(
    () => buildWorkspaceConversationSections(WORKSPACE_CONVERSATION_SECTIONS),
    [],
  );
  const activeConversationItem = useMemo(
    () => findConversationItem(activeConversationId, workspaceSections) || findConversationItem(DEFAULT_CONVERSATION_ID, workspaceSections),
    [activeConversationId, workspaceSections],
  );
  const activeThread = useMemo(
    () => buildThreadContext(activeConversationItem),
    [activeConversationItem],
  );
  const activeMessages = WORKSPACE_THREAD_CONTENT[activeThread.id]?.messages || [];
  const { userText: latestUserText, agentReply: scriptAgentReply } = useMemo(
    () => getLatestUserTurn(activeMessages),
    [activeMessages],
  );
  const recommendedReply = scriptAgentReply || TRUST_MODE_FALLBACK_REPLY;
  const [composerPrefill, setComposerPrefill] = useState({ text: '', seed: 0 });
  const [autoSendSignal, setAutoSendSignal] = useState({ text: '', seed: 0 });

  /* 切换会话时清空待回填/待发送信号，避免历史信号影响新会话 */
  useEffect(() => {
    setComposerPrefill({ text: '', seed: 0 });
    setAutoSendSignal({ text: '', seed: 0 });
  }, [activeThread.id]);

  const handleAssistantCopy = useCallback((text) => {
    setComposerPrefill((prev) => ({ text: text || '', seed: prev.seed + 1 }));
  }, []);
  const handleAssistantSend = useCallback((text) => {
    setAutoSendSignal((prev) => ({ text: text || '', seed: prev.seed + 1 }));
  }, []);

  const infoPanels = useMemo(
    () => [
      {
        id: 'assistant',
        tabs: [{ id: 'assistant', label: '托管助手' }],
        content: (
          <TrustModeAssistantMessage
            threadId={activeThread.id}
            latestUserText={latestUserText}
            recommendedReply={recommendedReply}
            onCopyToInput={handleAssistantCopy}
            onSendToUser={handleAssistantSend}
          />
        ),
      },
      {
        id: 'tickets',
        tabs: [
          { id: 'history', label: '历史工单' },
          { id: 'logs', label: '工单日志' },
          { id: 'tools', label: '信息工具' },
        ],
      },
      {
        id: 'info',
        tabs: [
          { id: 'video', label: '视频信息' },
          { id: 'user', label: '用户命中实验' },
          { id: 'records', label: '沟通记录' },
        ],
      },
    ],
    [activeThread.id, latestUserText, recommendedReply, handleAssistantCopy, handleAssistantSend],
  );

  const handleConversationLayoutWidthRequest = useCallback((contentWidth) => {
    setSideWidth(contentWidth + PANEL_OVERLAP);
  }, []);

  const handleConversationVariantChange = useCallback((nextVariant) => {
    const nextMinWidth = nextVariant === 'card' ? CARD_MIN_WIDTH : AVATAR_ONLY_WIDTH;
    setLeftContentMinWidth(nextMinWidth);
    setSideWidth((currentWidth) => Math.max(currentWidth, nextMinWidth + PANEL_OVERLAP));
  }, []);

  useEffect(() => {
    const node = mainPanelRef.current;
    if (!node) return undefined;

    function updateWidth() {
      setMainPanelWidth(node.getBoundingClientRect().width);
    }

    updateWidth();
    const observer = typeof ResizeObserver !== 'undefined' ? new ResizeObserver(updateWidth) : null;
    observer?.observe(node);
    window.addEventListener('resize', updateWidth);

    return () => {
      observer?.disconnect();
      window.removeEventListener('resize', updateWidth);
    };
  }, []);

  const maxInfoPanelWidth = Number.isFinite(mainPanelWidth) && mainPanelWidth > 0
    ? Math.max(INFO_PANEL_MIN_WIDTH, mainPanelWidth - MAIN_CONTENT_GAP - IM_PANEL_MIN_WIDTH)
    : INFO_PANEL_DEFAULT_WIDTH;
  const currentInfoPanelWidth = clamp(infoPanelWidth, INFO_PANEL_MIN_WIDTH, maxInfoPanelWidth);

  useEffect(() => {
    setInfoPanelWidth((currentWidth) => clamp(currentWidth, INFO_PANEL_MIN_WIDTH, maxInfoPanelWidth));
  }, [maxInfoPanelWidth]);

  const updateInfoPanelWidth = useCallback((nextWidth) => {
    setInfoPanelWidth(clamp(nextWidth, INFO_PANEL_MIN_WIDTH, maxInfoPanelWidth));
  }, [maxInfoPanelWidth]);

  const handleInfoPanelResizePointerDown = useCallback((event) => {
    event.preventDefault();
    const startX = event.clientX;
    const startWidth = currentInfoPanelWidth;
    const previousCursor = document.body.style.cursor;
    const previousUserSelect = document.body.style.userSelect;
    document.body.style.cursor = 'col-resize';
    document.body.style.userSelect = 'none';

    function handlePointerMove(moveEvent) {
      const delta = moveEvent.clientX - startX;
      updateInfoPanelWidth(startWidth - delta);
    }

    function handlePointerUp() {
      document.body.style.cursor = previousCursor;
      document.body.style.userSelect = previousUserSelect;
      window.removeEventListener('pointermove', handlePointerMove);
      window.removeEventListener('pointerup', handlePointerUp);
    }

    window.addEventListener('pointermove', handlePointerMove);
    window.addEventListener('pointerup', handlePointerUp);
  }, [currentInfoPanelWidth, updateInfoPanelWidth]);

  const handleInfoPanelResizeKeyDown = useCallback((event) => {
    if (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight' && event.key !== 'Home' && event.key !== 'End') return;
    event.preventDefault();

    if (event.key === 'Home') {
      updateInfoPanelWidth(maxInfoPanelWidth);
      return;
    }

    if (event.key === 'End') {
      updateInfoPanelWidth(INFO_PANEL_MIN_WIDTH);
      return;
    }

    const delta = event.shiftKey ? 32 : 16;
    updateInfoPanelWidth(currentInfoPanelWidth + (event.key === 'ArrowLeft' ? delta : -delta));
  }, [currentInfoPanelWidth, maxInfoPanelWidth, updateInfoPanelWidth]);

  const leftPanel = (
    <ConversationList
      title="会话列表"
      sections={workspaceSections}
      loading={listLoading}
      defaultActiveTab="all"
      activeItemId={activeThread.id}
      onItemClick={(item) => setActiveConversationId(item.id)}
      resizable={false}
      collapsible
      autoCollapseOnNarrow
      onLayoutWidthRequest={handleConversationLayoutWidthRequest}
      onVariantChange={handleConversationVariantChange}
      className="h-full"
      style={{ width: '100%', height: '100%' }}
    />
  );

  const mainPanel = (
    <div ref={mainPanelRef} className={MAIN_PANEL_CONTENT}>
      <div className={IM_PANEL}>
        <IMConversationPattern
          key={activeThread.id}
          className="h-full"
          style={{ height: '100%' }}
          pagePadding={0}
          thread={activeThread}
          messages={activeMessages}
          composerPrefill={composerPrefill}
          autoSendSignal={autoSendSignal}
        />
      </div>
      <div className={INFO_PANEL_FRAME} style={{ width: `${currentInfoPanelWidth}px` }}>
        <button
          type="button"
          className={INFO_PANEL_RESIZE_HANDLE}
          aria-label="调整右侧信息面板宽度"
          aria-orientation="vertical"
          aria-valuemin={INFO_PANEL_MIN_WIDTH}
          aria-valuemax={Math.round(maxInfoPanelWidth)}
          aria-valuenow={Math.round(currentInfoPanelWidth)}
          onPointerDown={handleInfoPanelResizePointerDown}
          onKeyDown={handleInfoPanelResizeKeyDown}
        >
          <span className={INFO_PANEL_RESIZE_HANDLE_LINE} aria-hidden="true" />
        </button>
        <InfoDisplayPanel
          panels={infoPanels}
          className="!h-full w-full"
          style={{ height: '100%' }}
        />
      </div>
    </div>
  );

  return (
    <div
      className="tfds-page-customer-service-workspace flex h-full min-h-0 min-w-0 w-full items-stretch overflow-hidden"
      style={{
        background: 'var(--color-blueGrey-200, #F2F4F7)',
        borderRadius: 0,
      }}
    >
      <CustomerServiceWorkspaceFrame
        sideWidth={sideWidth}
        minSideWidth={100}
        leftContentMinWidth={leftContentMinWidth}
        mainMinWidth={MAIN_PANEL_MIN_WIDTH}
        onSideWidthChange={setSideWidth}
        leftPanel={leftPanel}
        mainPanel={mainPanel}
      />
    </div>
  );
}
