"use client";

import {
  ChevronDownIcon,
  ChevronRightIcon,
  Cog,
  ExternalLinkIcon,
  Heart,
  MessageSquarePlusIcon,
  PanelRightCloseIcon,
  PanelRightOpenIcon,
  PencilIcon,
  PlusIcon,
  RefreshCcwIcon,
  RotateCcwIcon,
  UserRoundPlusIcon,
  XIcon,
} from "lucide-react";
import Link from "next/link";
import { useRouter, useSearchParams } from "next/navigation";
import {
  useCallback,
  useEffect,
  useMemo,
  useState,
  type ReactNode,
} from "react";
import { toast } from "sonner";

import { Badge } from "@/components/ui/badge";
import { Button } from "@/components/ui/button";
import { ButtonGroup } from "@/components/ui/button-group";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import {
  fetchAgentProfilesAll,
  fetchConversationDetail,
  type AdminAgentProfile,
  type AdminConversationDetail,
} from "@/lib/api/admin";
import {
  deleteTicketCollaborator,
  deleteTicketRelation,
  fetchTicketDetail,
  unwatchTicket,
  updateTicket,
  watchTicket,
  type CreateTicketPayload,
  type TicketDetail,
  type TicketItem,
  type UpdateTicketPayload,
} from "@/lib/api/ticket";
import { readSession } from "@/lib/auth";
import { getEnumLabel } from "@/lib/enums";
import {
  IMConversationStatus,
  IMConversationStatusLabels,
} from "@/lib/generated/enums";
import { formatDateTime } from "@/lib/utils";
import { EditDialog } from "../_components/edit";
import { TicketAssignDialog } from "../_components/ticket-assign-dialog";
import { TicketCollaboratorDialog } from "../_components/ticket-collaborator-dialog";
import { TicketCustomerPanel } from "../_components/ticket-customer-panel";
import { TicketPriorityBadge } from "../_components/ticket-priority-badge";
import { TicketReasonDialog } from "../_components/ticket-reason-dialog";
import { TicketRelationDialog } from "../_components/ticket-relation-dialog";
import { TicketReplyDialog } from "../_components/ticket-reply-dialog";
import { TicketSLABadge } from "../_components/ticket-sla-badge";
import {
  TicketStatusBadge,
  ticketStatusLabel,
} from "../_components/ticket-status-badge";
import { TicketStatusDialog } from "../_components/ticket-status-dialog";

function formatTicketSource(source?: string) {
  switch (source) {
    case "conversation":
      return "会话转工单";
    case "manual":
      return "手动创建";
    case "email":
      return "邮件";
    case "api":
      return "API";
    case "system":
      return "系统";
    default:
      return source || "—";
  }
}

function formatSLAStatus(status: string) {
  switch (status) {
    case "running":
      return "进行中";
    case "paused":
      return "已暂停";
    case "completed":
      return "已完成";
    case "breached":
      return "已超时";
    default:
      return status;
  }
}

function ticketSeverityLabel(severity: number) {
  switch (severity) {
    case 1:
      return "轻微";
    case 2:
      return "一般";
    case 3:
      return "严重";
    default:
      return String(severity);
  }
}

function ticketRelationLabel(relationType?: string) {
  switch (relationType) {
    case "duplicate":
      return "重复工单";
    case "related":
      return "相关工单";
    case "parent":
      return "父工单";
    case "child":
      return "子工单";
    default:
      return relationType || "关联工单";
  }
}

function ticketEventLabel(eventType?: string) {
  switch (eventType) {
    case "mentioned":
      return "提及协作人";
    case "internal_noted":
      return "内部备注";
    case "replied":
      return "回复客户";
    case "status_changed":
      return "状态变更";
    case "assigned":
      return "指派工单";
    case "transferred":
      return "转派工单";
    default:
      return eventType || "事件";
  }
}

function getMainSLA(ticket: TicketItem | null) {
  return (
    ticket?.sla?.find((item) => item.slaType === "resolution") ??
    ticket?.sla?.[0] ??
    null
  );
}

function parseMentionUserIds(payload?: string) {
  if (!payload) {
    return [];
  }
  try {
    const data = JSON.parse(payload) as { mentionUserIds?: number[] };
    return Array.isArray(data.mentionUserIds) ? data.mentionUserIds : [];
  } catch {
    return [];
  }
}

function isDoneTicketStatus(status?: string) {
  return status === "resolved" || status === "closed" || status === "cancelled";
}

export default function TicketDetailPage() {
  const searchParams = useSearchParams();
  const router = useRouter();
  const ticketId = Number(searchParams.get("id") || 0);
  const [detail, setDetail] = useState<TicketDetail | null>(null);
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [replyDialogOpen, setReplyDialogOpen] = useState(false);
  const [assignDialogOpen, setAssignDialogOpen] = useState(false);
  const [statusDialogOpen, setStatusDialogOpen] = useState(false);
  const [closeDialogOpen, setCloseDialogOpen] = useState(false);
  const [reopenDialogOpen, setReopenDialogOpen] = useState(false);
  const [editDialogOpen, setEditDialogOpen] = useState(false);
  const [relationDialogOpen, setRelationDialogOpen] = useState(false);
  const [collaboratorDialogOpen, setCollaboratorDialogOpen] = useState(false);
  const [collapsedSections, setCollapsedSections] = useState<
    Record<string, boolean>
  >({});
  const [rightPanelCollapsed, setRightPanelCollapsed] = useState(false);
  const [sourceConversation, setSourceConversation] =
    useState<AdminConversationDetail | null>(null);
  const [agents, setAgents] = useState<AdminAgentProfile[]>([]);

  const ticket = detail?.ticket ?? null;
  const currentUserId = readSession()?.user?.id ?? 0;
  const isWatching = Boolean(
    detail?.watchers?.some((item) => item.userId === currentUserId),
  );
  const resolutionSLA = useMemo(() => getMainSLA(ticket), [ticket]);
  const childRelations = useMemo(
    () =>
      (detail?.relatedTickets || []).filter(
        (item) => item.relationType === "child",
      ),
    [detail?.relatedTickets],
  );
  const childProgress = useMemo(() => {
    const total = childRelations.length;
    const completed = childRelations.filter((item) =>
      isDoneTicketStatus(item.relatedTicketStatus),
    ).length;
    return {
      total,
      completed,
      active: total - completed,
    };
  }, [childRelations]);

  const toggleSection = useCallback((sectionKey: string) => {
    setCollapsedSections((current) => ({
      ...current,
      [sectionKey]: !current[sectionKey],
    }));
  }, []);

  const loadDetail = useCallback(async () => {
    if (!ticketId) {
      setDetail(null);
      setLoading(false);
      return;
    }
    setLoading(true);
    try {
      const data = await fetchTicketDetail(ticketId);
      setDetail(data);
    } catch (error) {
      toast.error(error instanceof Error ? error.message : "加载工单详情失败");
    } finally {
      setLoading(false);
    }
  }, [ticketId]);

  useEffect(() => {
    void loadDetail();
  }, [loadDetail]);

  useEffect(() => {
    void (async () => {
      try {
        const data = await fetchAgentProfilesAll();
        setAgents(Array.isArray(data) ? data : []);
      } catch {
        setAgents([]);
      }
    })();
  }, []);

  useEffect(() => {
    if (!ticket?.conversationId) {
      setSourceConversation(null);
      return;
    }
    void (async () => {
      try {
        const data = await fetchConversationDetail(ticket.conversationId);
        setSourceConversation(data);
      } catch {
        setSourceConversation(null);
      }
    })();
  }, [ticket?.conversationId]);

  async function handleWatchToggle() {
    if (!ticket) {
      return;
    }
    setSaving(true);
    try {
      if (isWatching) {
        await unwatchTicket(ticket.id);
        toast.success("已取消关注");
      } else {
        await watchTicket(ticket.id);
        toast.success("已关注工单");
      }
      await loadDetail();
    } catch (error) {
      toast.error(error instanceof Error ? error.message : "更新关注状态失败");
    } finally {
      setSaving(false);
    }
  }

  async function handleEditSubmit(
    payload: CreateTicketPayload | UpdateTicketPayload,
  ) {
    if (!("ticketId" in payload)) {
      toast.error("详情页仅支持编辑现有工单");
      return;
    }
    setSaving(true);
    try {
      await updateTicket(payload);
      toast.success("工单基础信息已更新");
      setEditDialogOpen(false);
      await loadDetail();
    } catch (error) {
      toast.error(error instanceof Error ? error.message : "更新工单失败");
    } finally {
      setSaving(false);
    }
  }

  async function handleDeleteRelation(relationId: number) {
    if (!ticket) {
      return;
    }
    setSaving(true);
    try {
      await deleteTicketRelation({ ticketId: ticket.id, relationId });
      toast.success("关联工单已移除");
      await loadDetail();
    } catch (error) {
      toast.error(error instanceof Error ? error.message : "移除关联工单失败");
    } finally {
      setSaving(false);
    }
  }

  async function handleDeleteCollaborator(collaboratorId: number) {
    if (!ticket) {
      return;
    }
    setSaving(true);
    try {
      await deleteTicketCollaborator({ ticketId: ticket.id, collaboratorId });
      toast.success("协作人已移除");
      await loadDetail();
    } catch (error) {
      toast.error(error instanceof Error ? error.message : "移除协作人失败");
    } finally {
      setSaving(false);
    }
  }

  return (
    <div className="flex h-[calc(100vh-4rem)] min-w-0 overflow-hidden">
      {loading ? (
        <div className="min-w-0 flex-1 p-4 lg:p-6">
          <div className="py-20 text-center text-muted-foreground">
            加载中...
          </div>
        </div>
      ) : ticket ? (
        <>
          <div className="min-w-0 flex-1">
            <div className="flex h-full flex-col">
              {/* 工单header */}
              <div className="border-b border-border/70 bg-muted/10 flex flex-col gap-2.5 px-8 py-2">
                <div className="flex flex-col gap-2.5 xl:flex-row xl:items-start xl:justify-between">
                  <div className="min-w-0 space-y-2">
                    <div className="text-xs font-medium tracking-wide text-muted-foreground">
                      {ticket.ticketNo}
                    </div>
                    <div className="flex min-w-0 flex-wrap items-center gap-2">
                      <TicketStatusBadge status={ticket.status} />
                      <TicketPriorityBadge priority={ticket.priority} priorityName={ticket.priorityName} />
                      <TicketSLABadge ticket={ticket} />
                    </div>
                  </div>
                  <div className="flex flex-wrap justify-end gap-2">
                    <ButtonGroup>
                      <Button
                        size="sm"
                        variant={isWatching ? "default" : "outline"}
                        onClick={() => void handleWatchToggle()}
                        disabled={saving || !ticket}
                      >
                        <Heart className="size-4" />
                        {isWatching ? "已关注" : "关注"}
                      </Button>
                      <Button
                        size="sm"
                        variant="outline"
                        onClick={() => setReplyDialogOpen(true)}
                      >
                        <MessageSquarePlusIcon className="size-4" />
                        回复/备注
                      </Button>
                      {ticket.status === "closed" ? (
                        <Button
                          size="sm"
                          variant="outline"
                          onClick={() => setReopenDialogOpen(true)}
                          disabled={saving}
                        >
                          <RotateCcwIcon className="size-4" />
                          重开工单
                        </Button>
                      ) : (
                        <Button
                          size="sm"
                          variant="outline"
                          onClick={() => setCloseDialogOpen(true)}
                          disabled={saving || !ticket}
                        >
                          <XIcon className="size-4" />
                          关闭工单
                        </Button>
                      )}
                      <Button
                        size="sm"
                        variant="outline"
                        onClick={() => setAssignDialogOpen(true)}
                      >
                        <UserRoundPlusIcon className="size-4" />
                        分配处理人
                      </Button>
                      <Button
                        size="sm"
                        variant="outline"
                        onClick={() => setStatusDialogOpen(true)}
                      >
                        <Cog className="size-4" />
                        变更状态
                      </Button>
                      <Button
                        size="sm"
                        variant="outline"
                        onClick={() => setEditDialogOpen(true)}
                      >
                        <PencilIcon className="size-4" />
                        {/* 编辑 */}
                      </Button>
                      <Button
                        size="sm"
                        variant="outline"
                        onClick={() => void loadDetail()}
                        disabled={loading || saving}
                      >
                        <RefreshCcwIcon className="size-4" />
                        {/* 刷新 */}
                      </Button>
                    </ButtonGroup>
                  </div>
                </div>
              </div>

              <div className="min-h-0 overflow-y-auto">
                <div className="space-y-0">
                  <DetailSection className="px-4 lg:px-6">
                    <div className="space-y-2.5">
                      <h1 className="text-xl font-semibold tracking-tight text-foreground">
                        {ticket.title}
                      </h1>
                      <div className="border-l-2 pl-3 border-border/70 ">
                        <div className="whitespace-pre-wrap break-words text-sm leading-6 text-foreground/80">
                          {ticket.description || "暂无工单描述"}
                        </div>
                      </div>
                    </div>
                  </DetailSection>

                  <DetailSection className="px-4 pt-4 lg:px-6">
                    <Tabs defaultValue="comments" className="gap-3">
                      <TabsList>
                        <TabsTrigger value="comments">回复&备注</TabsTrigger>
                        <TabsTrigger value="events">事件记录</TabsTrigger>
                      </TabsList>
                      <TabsContent value="comments" className="space-y-3">
                        {(detail?.comments?.length || 0) > 0 ? (
                          <div className="relative pl-6">
                            <div className="absolute top-0 bottom-0 left-[0.55rem] w-px bg-border/70" />
                            <div className="space-y-4">
                              {detail?.comments?.map((comment) => (
                                <div
                                  key={`comment-${comment.id}`}
                                  className="relative"
                                >
                                  <div
                                    className={`absolute top-2 -left-6 size-3 rounded-full ring-4 ring-background ${
                                      comment.commentType === "public_reply"
                                        ? "bg-emerald-500"
                                        : "bg-amber-500"
                                    }`}
                                  />
                                  <div className="rounded-lg border border-border/60 bg-muted/15 px-3.5 py-3">
                                    <div className="mb-2 flex items-start justify-between gap-3">
                                      <div className="min-w-0 space-y-1">
                                        <div className="flex flex-wrap items-center gap-2">
                                          <div className="text-sm font-medium">
                                            {comment.authorName ||
                                              `用户#${comment.authorId}`}
                                          </div>
                                          <Badge
                                            variant="outline"
                                            className={
                                              comment.commentType ===
                                              "public_reply"
                                                ? "border-emerald-200 bg-emerald-50 text-emerald-700"
                                                : "border-amber-200 bg-amber-50 text-amber-700"
                                            }
                                          >
                                            {comment.commentType ===
                                            "public_reply"
                                              ? "客户回复"
                                              : "内部备注"}
                                          </Badge>
                                        </div>
                                        <div className="text-xs text-muted-foreground">
                                          {comment.createdAt
                                            ? formatDateTime(comment.createdAt)
                                            : "—"}
                                        </div>
                                      </div>
                                    </div>
                                    {comment.commentType === "internal_note" &&
                                    parseMentionUserIds(comment.payload).length ? (
                                      <div className="mb-2 flex flex-wrap gap-2">
                                        {parseMentionUserIds(comment.payload).map(
                                          (userId) => {
                                            const user = agents.find(
                                              (item) => item.userId === userId,
                                            );
                                            return (
                                              <span
                                                key={`${comment.id}-${userId}`}
                                                className="rounded-full border px-2 py-1 text-xs text-muted-foreground"
                                              >
                                                @
                                                {user?.displayName ||
                                                  user?.nickname ||
                                                  user?.username ||
                                                  `用户#${userId}`}
                                              </span>
                                            );
                                          },
                                        )}
                                      </div>
                                    ) : null}
                                    <div className="whitespace-pre-wrap break-words text-sm leading-6 text-foreground/90">
                                      {comment.content}
                                    </div>
                                  </div>
                                </div>
                              ))}
                            </div>
                          </div>
                        ) : (
                          <div className="text-sm text-muted-foreground">
                            暂无评论记录
                          </div>
                        )}
                      </TabsContent>
                      <TabsContent value="events" className="space-y-2">
                        {(detail?.events?.length || 0) > 0 ? (
                          detail?.events?.map((event) => (
                            <div
                              key={`event-${event.id}`}
                              className={`rounded-lg border px-3 py-2.5 ${
                                event.eventType === "mentioned"
                                  ? "border-amber-200 bg-amber-50/60"
                                  : "border-border/60 bg-muted/20"
                              }`}
                            >
                              <div className="flex items-center justify-between gap-3">
                                <div className="flex items-center gap-2">
                                  {event.eventType === "mentioned" ? (
                                    <Badge
                                      variant="outline"
                                      className="border-amber-300 bg-amber-100 text-amber-800"
                                    >
                                      提及
                                    </Badge>
                                  ) : null}
                                  <div className="text-sm font-medium">
                                    {event.content ||
                                      ticketEventLabel(event.eventType)}
                                  </div>
                                </div>
                                <div className="text-xs text-muted-foreground">
                                  {event.createdAt
                                    ? formatDateTime(event.createdAt)
                                    : "—"}
                                </div>
                              </div>
                              <div className="mt-1 text-xs text-muted-foreground">
                                {event.operatorName ||
                                  `用户#${event.operatorId}`}
                              </div>
                              {event.payload &&
                              event.eventType === "mentioned" &&
                              parseMentionUserIds(event.payload).length ? (
                                <div className="mt-2 flex flex-wrap gap-2">
                                  {parseMentionUserIds(event.payload).map(
                                    (userId) => {
                                      const user = agents.find(
                                        (item) => item.userId === userId,
                                      );
                                      return (
                                        <span
                                          key={`${event.id}-${userId}`}
                                          className="rounded-full border border-amber-200 px-2 py-1 text-xs text-amber-800"
                                        >
                                          @
                                          {user?.displayName ||
                                            user?.nickname ||
                                            user?.username ||
                                            `用户#${userId}`}
                                        </span>
                                      );
                                    },
                                  )}
                                </div>
                              ) : null}
                            </div>
                          ))
                        ) : (
                          <div className="text-sm text-muted-foreground">
                            暂无事件记录
                          </div>
                        )}
                      </TabsContent>
                    </Tabs>
                  </DetailSection>
                </div>
              </div>
            </div>
          </div>

          <div className="relative hidden shrink-0 border-l border-border/70 bg-background lg:block">
            <Button
              variant="outline"
              size="icon"
              className="absolute top-2.5 left-1/2 z-10 size-6.5 -translate-x-1/2 rounded-full shadow-sm"
              onClick={() => setRightPanelCollapsed((value) => !value)}
              aria-label={
                rightPanelCollapsed ? "展开右侧信息面板" : "折叠右侧信息面板"
              }
            >
              {rightPanelCollapsed ? (
                <PanelRightOpenIcon className="size-3.5" />
              ) : (
                <PanelRightCloseIcon className="size-3.5" />
              )}
            </Button>
          </div>

          <div
            className={`min-w-0 shrink-0 overflow-hidden bg-background transition-[width] duration-200 ${
              rightPanelCollapsed ? "w-0" : "w-full lg:w-[360px]"
            }`}
          >
            {rightPanelCollapsed ? null : (
              <div className="h-full">
                <div className="flex h-full flex-col gap-4">
                  <div className="min-h-0 overflow-y-auto">
                    <div className="space-y-0">
                      <DetailSection
                        title="基础信息"
                        collapsible
                        collapsed={Boolean(collapsedSections.basics)}
                        onToggle={() => toggleSection("basics")}
                        className="px-4 pt-3 lg:px-6 lg:pt-4"
                        action={
                          <Button
                            variant="ghost"
                            size="sm"
                            onClick={() => setEditDialogOpen(true)}
                          >
                            <PencilIcon className="size-3.5" />
                            编辑
                          </Button>
                        }
                        contentClassName="space-y-0 text-sm"
                      >
                        <InfoRow label="工单号" value={ticket.ticketNo} />
                        <InfoRow
                          label="状态"
                          value={ticketStatusLabel(ticket.status)}
                        />
                        <InfoRow
                          label="标签"
                          value={
                            ticket.tags && ticket.tags.length > 0
                              ? ticket.tags.map((tag) => tag.name).join(" / ")
                              : "未打标签"
                          }
                        />
                        <InfoRow
                          label="优先级"
                          value={ticket.priorityName || String(ticket.priority)}
                        />
                        <InfoRow
                          label="严重度"
                          value={ticketSeverityLabel(ticket.severity)}
                        />
                        <InfoRow
                          label="处理人"
                          value={ticket.currentAssigneeName || "未指派"}
                        />
                        <InfoRow
                          label="处理团队"
                          value={ticket.currentTeamName || "未分组"}
                        />
                        <InfoRow
                          label="解决时限"
                          value={
                            resolutionSLA
                              ? `${resolutionSLA.targetMinutes} 分钟 / ${formatSLAStatus(resolutionSLA.status)}`
                              : "未设置"
                          }
                        />
                        <InfoRow
                          label="来源"
                          value={formatTicketSource(ticket.source)}
                        />
                        <InfoRow label="渠道" value={ticket.channel || "—"} />
                        <InfoRow
                          label="重开次数"
                          value={String(ticket.reopenedCount)}
                        />
                        <InfoRow
                          label="创建时间"
                          value={
                            ticket.createdAt
                              ? formatDateTime(ticket.createdAt)
                              : "—"
                          }
                        />
                        <InfoRow
                          label="更新时间"
                          value={
                            ticket.updatedAt
                              ? formatDateTime(ticket.updatedAt)
                              : "—"
                          }
                        />
                        <InfoRow
                          label="截止时间"
                          value={
                            ticket.dueAt ? formatDateTime(ticket.dueAt) : "—"
                          }
                        />
                        <InfoRow
                          label="首次响应"
                          value={
                            ticket.firstResponseAt
                              ? formatDateTime(ticket.firstResponseAt)
                              : "—"
                          }
                        />
                      </DetailSection>

                      <DetailSection
                        title="客户信息"
                        collapsible
                        collapsed={Boolean(collapsedSections.customer)}
                        onToggle={() => toggleSection("customer")}
                        className="px-4 pt-4 lg:px-6"
                        contentClassName="text-sm"
                      >
                        <TicketCustomerPanel
                          ticketId={ticket.id}
                          customerId={ticket.customerId}
                          onRefresh={loadDetail}
                        />
                      </DetailSection>

                      <DetailSection
                        title="SLA 信息"
                        collapsible
                        collapsed={Boolean(collapsedSections.sla)}
                        onToggle={() => toggleSection("sla")}
                        className="px-4 pt-4 lg:px-6"
                        contentClassName="space-y-3 text-sm"
                      >
                      {ticket.sla?.length ? (
                        ticket.sla.map((sla) => (
                          <SurfacePanel key={sla.slaType} className="p-2.5">
                            <div className="font-medium">
                              {sla.slaType === "first_response"
                                ? "首次响应"
                                : "解决时效"}
                            </div>
                            <div className="mt-1 text-muted-foreground">
                              目标：{sla.targetMinutes} 分钟
                            </div>
                            <div className="mt-1 text-muted-foreground">
                              状态：{formatSLAStatus(sla.status)}
                            </div>
                            <div className="mt-1 text-muted-foreground">
                              已耗时：{sla.elapsedMin} 分钟
                            </div>
                            {sla.breachedAt ? (
                              <div className="mt-1 text-muted-foreground">
                                超时于：{formatDateTime(sla.breachedAt)}
                              </div>
                            ) : null}
                          </SurfacePanel>
                        ))
                      ) : (
                        <div className="rounded-lg border border-amber-200 bg-amber-50/70 p-3 text-sm text-amber-900">
                          当前工单未生成 SLA 记录，请检查工单 SLA
                          配置；如果系统仍在使用默认策略，也需要确认配置是否已补齐。
                            <Link
                              href="/dashboard/ticket-priorities"
                              className="ml-1 font-medium underline underline-offset-4"
                            >
                              前往配置优先级
                            </Link>
                        </div>
                      )}
                    </DetailSection>

                      <DetailSection
                        title="解决信息"
                        collapsible
                        collapsed={Boolean(collapsedSections.resolution)}
                        onToggle={() => toggleSection("resolution")}
                        className="px-4 pt-4 lg:px-6"
                        contentClassName="space-y-0 text-sm"
                      >
                      <InfoRow
                        label="解决码"
                        value={
                          ticket.resolutionCodeName ||
                          ticket.resolutionCode ||
                          "—"
                        }
                      />
                      <InfoRow
                        label="解决说明"
                        value={ticket.resolutionSummary || "—"}
                      />
                      <InfoRow
                        label="关闭原因"
                        value={ticket.closeReason || "—"}
                      />
                      <InfoRow
                        label="解决时间"
                        value={
                          ticket.resolvedAt
                            ? formatDateTime(ticket.resolvedAt)
                            : "—"
                        }
                      />
                      <InfoRow
                        label="关闭时间"
                        value={
                          ticket.closedAt
                            ? formatDateTime(ticket.closedAt)
                            : "—"
                        }
                      />
                    </DetailSection>

                      <DetailSection
                        title="来源关联"
                        collapsible
                        collapsed={Boolean(collapsedSections.source)}
                        onToggle={() => toggleSection("source")}
                        className="px-4 pt-4 lg:px-6"
                        contentClassName="space-y-3 text-sm"
                      >
                      <InfoRow
                        label="关联会话"
                        value={
                          ticket.conversationId
                            ? `#${ticket.conversationId}`
                            : "未关联"
                        }
                      />
                      {sourceConversation ? (
                        <SurfacePanel className="p-2.5">
                          <div className="text-sm font-medium">
                            {sourceConversation.customerName || "未命名客户"}
                          </div>
                          <div className="mt-2 flex flex-wrap gap-2 text-xs text-muted-foreground">
                            <span>
                              状态：
                              {getEnumLabel(
                                IMConversationStatusLabels,
                                sourceConversation.status as IMConversationStatus,
                              )}
                            </span>
                            <span>
                              处理人：
                              {sourceConversation.currentAssigneeName ||
                                "未指派"}
                            </span>
                          </div>
                          <div className="mt-2 text-xs leading-6 text-muted-foreground">
                            {sourceConversation.lastMessageSummary ||
                              "暂无最近消息摘要"}
                          </div>
                          <div className="mt-2 text-xs text-muted-foreground">
                            最近活跃：
                            {sourceConversation.lastActiveAt
                              ? formatDateTime(sourceConversation.lastActiveAt)
                              : "—"}
                          </div>
                        </SurfacePanel>
                      ) : null}
                      {ticket.conversationId ? (
                        <Button
                          variant="outline"
                          className="w-full"
                          onClick={() => router.push("/dashboard/conversations")}
                        >
                          <RotateCcwIcon className="size-4" />
                          前往会话工作台
                        </Button>
                      ) : null}
                    </DetailSection>

                      <DetailSection
                        title="关联工单"
                        collapsible
                        collapsed={Boolean(collapsedSections.related)}
                        onToggle={() => toggleSection("related")}
                        className="px-4 pt-4 lg:px-6"
                        action={
                        <Button
                          variant="ghost"
                          size="sm"
                          onClick={() => setRelationDialogOpen(true)}
                        >
                          <PlusIcon className="size-4" />
                          新增关联
                        </Button>
                        }
                        contentClassName="space-y-3 text-sm"
                      >
                      {childProgress.total > 0 ? (
                        <div className="rounded-lg border border-blue-200 bg-blue-50/70 p-3">
                          <div className="text-sm font-medium text-blue-900">
                            子工单进展
                          </div>
                          <div className="mt-2 flex flex-wrap gap-2 text-xs text-blue-900">
                            <span>总数：{childProgress.total}</span>
                            <span>已完成：{childProgress.completed}</span>
                            <span>进行中：{childProgress.active}</span>
                          </div>
                        </div>
                      ) : null}
                      {detail?.relatedTickets?.length ? (
                        detail.relatedTickets.map((relation) => (
                          <SurfacePanel key={relation.id} className="p-2.5">
                            <div className="flex items-start justify-between gap-3">
                              <div className="min-w-0 space-y-1">
                                <div className="text-xs text-muted-foreground">
                                  {ticketRelationLabel(relation.relationType)}
                                </div>
                                <div className="truncate font-medium">
                                  {relation.relatedTicketNo ||
                                    `#${relation.relatedTicketId}`}
                                </div>
                                <div className="text-sm text-muted-foreground">
                                  {relation.relatedTicketTitle || "未命名工单"}
                                </div>
                              </div>
                              <div className="flex shrink-0 gap-1">
                                <Button
                                  variant="ghost"
                                  size="icon"
                                  onClick={() =>
                                    router.push(
                                      `/tickets/detail?id=${relation.relatedTicketId}`,
                                    )
                                  }
                                >
                                  <ExternalLinkIcon className="size-4" />
                                </Button>
                                <Button
                                  variant="ghost"
                                  size="icon"
                                  disabled={saving}
                                  onClick={() =>
                                    void handleDeleteRelation(relation.id)
                                  }
                                >
                                  <XIcon className="size-4" />
                                </Button>
                              </div>
                            </div>
                            <div className="mt-2 flex flex-wrap gap-2 text-xs text-muted-foreground">
                              {relation.relationType === "child" ? (
                                <Badge
                                  variant="outline"
                                  className={
                                    isDoneTicketStatus(
                                      relation.relatedTicketStatus,
                                    )
                                      ? "border-emerald-300 bg-emerald-50 text-emerald-800"
                                      : "border-orange-300 bg-orange-50 text-orange-800"
                                  }
                                >
                                  {isDoneTicketStatus(
                                    relation.relatedTicketStatus,
                                  )
                                    ? "子单已完成"
                                    : "子单处理中"}
                                </Badge>
                              ) : null}
                              <span>
                                状态：
                                {ticketStatusLabel(
                                  relation.relatedTicketStatus || "",
                                )}
                              </span>
                              <span>
                                团队：{relation.currentTeamName || "未分组"}
                              </span>
                              <span>
                                处理人：
                                {relation.currentAssigneeName || "未指派"}
                              </span>
                            </div>
                            <div className="mt-2 text-xs text-muted-foreground">
                              最近更新：
                              {relation.updatedAt
                                ? formatDateTime(relation.updatedAt)
                                : "—"}
                            </div>
                          </SurfacePanel>
                        ))
                      ) : (
                        <div className="text-muted-foreground">
                          暂无关联工单
                        </div>
                      )}
                    </DetailSection>

                      <DetailSection
                        title="协作人"
                        collapsible
                        collapsed={Boolean(collapsedSections.collaborators)}
                        onToggle={() => toggleSection("collaborators")}
                        className="px-4 pt-4 lg:px-6"
                        action={
                        <Button
                          variant="ghost"
                          size="sm"
                          onClick={() => setCollaboratorDialogOpen(true)}
                        >
                          <PlusIcon className="size-4" />
                          新增协作人
                        </Button>
                        }
                        contentClassName="space-y-3 text-sm"
                      >
                      {detail?.collaborators?.length ? (
                        detail.collaborators.map((collaborator) => (
                          <SurfacePanel
                            key={collaborator.id}
                            className="flex items-center justify-between gap-3 p-2.5"
                          >
                            <div className="min-w-0">
                              <div className="font-medium">
                                {collaborator.userName ||
                                  `用户#${collaborator.userId}`}
                              </div>
                              <div className="text-xs text-muted-foreground">
                                {collaborator.teamName || "未分组"}
                              </div>
                            </div>
                            <Button
                              variant="ghost"
                              size="icon"
                              disabled={saving}
                              onClick={() =>
                                void handleDeleteCollaborator(collaborator.id)
                              }
                            >
                              <XIcon className="size-4" />
                            </Button>
                          </SurfacePanel>
                        ))
                      ) : (
                        <div className="text-muted-foreground">暂无协作人</div>
                      )}
                    </DetailSection>

                      <DetailSection
                        title="关注人"
                        collapsible
                        collapsed={Boolean(collapsedSections.watchers)}
                        onToggle={() => toggleSection("watchers")}
                        className="px-4 pt-4 lg:px-6"
                        contentClassName="space-y-3 text-sm"
                      >
                      {detail?.watchers?.length ? (
                        detail.watchers.map((watcher) => (
                          <InfoRow
                            key={watcher.id}
                            label={`用户#${watcher.userId}`}
                            value={watcher.userName || "未命名"}
                          />
                        ))
                      ) : (
                        <div className="text-muted-foreground">暂无关注人</div>
                      )}
                      </DetailSection>
                    </div>
                  </div>
                </div>
              </div>
            )}
          </div>
        </>
      ) : (
        <div className="min-w-0 flex-1 p-4 lg:p-6">
          <div className="py-20 text-center text-muted-foreground">
            工单不存在
          </div>
        </div>
      )}
      <TicketReplyDialog
        open={replyDialogOpen}
        ticketId={ticket?.id ?? null}
        onOpenChange={setReplyDialogOpen}
        onSuccess={loadDetail}
      />
      <TicketAssignDialog
        open={assignDialogOpen}
        ticketId={ticket?.id ?? null}
        currentTeamId={ticket?.currentTeamId}
        currentAssigneeId={ticket?.currentAssigneeId}
        onOpenChange={setAssignDialogOpen}
        onSuccess={loadDetail}
      />
      <TicketStatusDialog
        open={statusDialogOpen}
        ticketId={ticket?.id ?? null}
        currentStatus={ticket?.status}
        onOpenChange={setStatusDialogOpen}
        onSuccess={loadDetail}
      />
      <TicketReasonDialog
        open={closeDialogOpen}
        mode="close"
        ticketId={ticket?.id ?? null}
        defaultReason={ticket?.closeReason || "处理完成"}
        onOpenChange={setCloseDialogOpen}
        onSuccess={loadDetail}
      />
      <TicketReasonDialog
        open={reopenDialogOpen}
        mode="reopen"
        ticketId={ticket?.id ?? null}
        defaultReason="客户有新反馈"
        onOpenChange={setReopenDialogOpen}
        onSuccess={loadDetail}
      />
      <EditDialog
        open={editDialogOpen}
        saving={saving}
        itemId={ticket?.id ?? null}
        onOpenChange={setEditDialogOpen}
        onSubmit={handleEditSubmit}
      />
      <TicketRelationDialog
        open={relationDialogOpen}
        ticketId={ticket?.id ?? null}
        onOpenChange={setRelationDialogOpen}
        onSuccess={loadDetail}
      />
      <TicketCollaboratorDialog
        open={collaboratorDialogOpen}
        ticketId={ticket?.id ?? null}
        onOpenChange={setCollaboratorDialogOpen}
        onSuccess={loadDetail}
      />
    </div>
  );
}

function InfoRow({ label, value }: { label: string; value: string }) {
  return (
    <div className="grid grid-cols-[78px_minmax(0,1fr)] items-start gap-3 py-1">
      <span className="text-sm text-muted-foreground">{label}</span>
      <span className="text-right text-sm text-foreground wrap-break-word">
        {value}
      </span>
    </div>
  );
}

function DetailSection({
  title,
  description,
  action,
  collapsible,
  collapsed,
  onToggle,
  className,
  contentClassName,
  children,
}: {
  title?: string;
  description?: string;
  action?: ReactNode;
  collapsible?: boolean;
  collapsed?: boolean;
  onToggle?: () => void;
  className?: string;
  contentClassName?: string;
  children: ReactNode;
}) {
  return (
    <section
      className={`border-b border-border/70 py-4 last:border-b-0 ${className ?? ""}`}
    >
      {title || description || action ? (
        <div className="flex flex-wrap items-center justify-between gap-3">
          <div className="min-w-0 space-y-1">
            <div className="flex items-center gap-2">
              {collapsible ? (
                <Button
                  type="button"
                  variant="ghost"
                  size="icon"
                  className="size-6 shrink-0"
                  onClick={onToggle}
                  aria-label={collapsed ? "展开面板" : "折叠面板"}
                >
                  {collapsed ? (
                    <ChevronRightIcon className="size-4" />
                  ) : (
                    <ChevronDownIcon className="size-4" />
                  )}
                </Button>
              ) : null}
              {title ? <h2 className="text-base font-medium">{title}</h2> : null}
            </div>
            {description ? (
              <p className="text-sm leading-5 text-muted-foreground">
                {description}
              </p>
            ) : null}
          </div>
          {action ? <div className="shrink-0">{action}</div> : null}
        </div>
      ) : null}
      {collapsed ? null :<div className="pt-3"><div className={contentClassName}>{children}</div></div>}
    </section>
  );
}

function SurfacePanel({
  className,
  children,
}: {
  className?: string;
  children: ReactNode;
}) {
  return (
    <div
      className={`rounded-lg border border-border/60 bg-muted/20 ${className ?? ""}`}
    >
      {children}
    </div>
  );
}
