'use client';

import { useState } from 'react';
import { ChevronRight, Check, Loader2, Users } from 'lucide-react';
import {
  ASSIGN_STRATEGY_META,
  Permission,
  type AssignmentProposal,
  type AssignStrategy,
} from '@pac/types';
import { Can } from '@/components/can';
import { assignmentsApi } from '@/components/plans/assignments-api';
import { cn } from '@/lib/utils';

/**
 * 全景确认单 —— 主管**一次确认**就落库的那张卡片。
 *
 * ⚠️ 必须是**原生 React 组件**,不能用 `render_artifact`:
 * artifact 跑在 `sandbox="allow-scripts"` 的 iframe 里、CSP `connect-src 'none'`,
 * **卡片内不可能发出写请求**。分岔记牢:只读展示用 artifact,可交互用原生组件。
 *
 * ── 微调**只有两项**(T13)──────────────────────────────────
 * 指定客服、时效。多加一项就直接违 T13 ——
 * 微调项越多,「一次确认」这个设计目标就越不可能实现。
 * 要换人群请回到对话里让助手重新圈,不要在卡片上做筛选。
 *
 * ── 布局 ────────────────────────────────────────────────
 * 助手窗只有 400px 宽,所以**不用 table**(横向撑爆),用卡片列表;
 * 患者明细按客服折叠,展开才看 —— 兼顾"尽明细"与"一眼可确认"。
 */
export function AssignmentConfirmSheet({
  requestId,
  sheet,
  state,
  assignmentId,
  onConfirmed,
}: {
  requestId: string;
  sheet: AssignmentProposal;
  state: 'pending' | 'confirmed' | 'cancelled';
  assignmentId?: string;
  /** 落库成功 → 交给外层把卡片切终态 + 往消息流注入一条文本(补模型记忆) */
  onConfirmed: (assignmentId: string, summary: string) => void;
}) {
  const [expiresInDays, setExpiresInDays] = useState(3);
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [open, setOpen] = useState<Set<string>>(new Set());

  const done = state === 'confirmed';

  const confirm = async () => {
    setSubmitting(true);
    setError(null);
    try {
      const res = await assignmentsApi.create({
        // ⭐ requestId **原样回传**服务端铸造的那个 —— 幂等键。
        //   前端自己生成挡不住 SSE 重连与重试。
        requestId,
        clinicId: sheet.clinicId,
        // 初筛条件快照:批次要能解释"当时按什么圈的"
        criteria: {
          potentialTreatment: sheet.potentialTreatment,
          candidateTotal: sheet.candidateTotal,
          target: sheet.target,
          selectionNote: sheet.selectionNote,
        },
        expiresInDays,
        // ⚠️ 落库的是**卡片当前值**,不是模型原提案 —— 主管改了时效就得按改后的走
        items: sheet.items.map((it) => ({
          planId: it.planId,
          assigneeUserId: it.assigneeUserId,
          assignStrategy: it.assignStrategy,
          selectionMode: it.selectionMode,
        })),
      });
      const summary =
        `已确认分配:批次 #${res.assignmentId.slice(0, 8)} · ` +
        (sheet.potentialTreatment ? `${sheet.potentialTreatment} · ` : '') +
        `${res.assigned} 条 · ${sheet.byAgent.length} 位客服 · ${expiresInDays} 天有效` +
        (res.skipped.length ? ` · ${res.skipped.length} 条未落(确认期间被他人认领)` : '');
      onConfirmed(res.assignmentId, summary);
    } catch (e) {
      setError(e instanceof Error ? e.message : '确认失败');
    } finally {
      setSubmitting(false);
    }
  };

  const byPlan = new Map(sheet.items.map((i) => [i.planId, i]));

  return (
    <div className="rounded-lg border border-brand-100 bg-white text-[12px] shadow-sm">
      {/* ① 汇总 */}
      <div className="flex items-center gap-2 border-b border-slate-100 bg-brand-50/50 px-3 py-2">
        <Users className="h-3.5 w-3.5 flex-none text-brand-600" />
        <span className="font-semibold text-slate-800">
          拟分 {sheet.placed} 人 · {sheet.byAgent.length} 位客服
        </span>
        {sheet.unplaced > 0 && (
          <span className="rounded bg-amber-100 px-1.5 py-0.5 text-[10.5px] text-amber-700">
            {sheet.unplaced} 人分不下去
          </span>
        )}
      </div>

      {/* ② 按客服(产能视图)—— 这一层是主管真正在看的 */}
      <div className="divide-y divide-slate-50">
        {sheet.byAgent.map((a) => {
          const isOpen = open.has(a.userId);
          const mine = sheet.items.filter((i) => i.assigneeUserId === a.userId);
          return (
            <div key={a.userId}>
              <button
                type="button"
                onClick={() =>
                  setOpen((s) => {
                    const n = new Set(s);
                    if (n.has(a.userId)) n.delete(a.userId);
                    else n.add(a.userId);
                    return n;
                  })
                }
                className="flex w-full items-center gap-2 px-3 py-2 text-left hover:bg-slate-50"
              >
                <ChevronRight
                  className={cn('h-3 w-3 flex-none text-slate-400 transition-transform', isOpen && 'rotate-90')}
                />
                {/* 姓名解析不出来时退回 id 前缀 —— ⛔ 不显示完整 uuid,400px 卡片会撑爆 */}
                <span className="flex-1 truncate font-medium text-slate-700">
                  {a.name ?? `#${a.userId.slice(0, 8)}`}
                </span>
                <span className="flex-none tabular-nums text-slate-500">
                  {a.count} 条
                  {a.inHandBefore > 0 && (
                    <span className="ml-1 text-[10.5px] text-slate-400">(在手 {a.inHandBefore})</span>
                  )}
                </span>
                {/* ⭐ T15:溢出转铺平必须**显式可见** —— 主管要看见发生了什么,而不是只看到结果 */}
                {a.spread > 0 && (
                  <span className="flex-none rounded bg-slate-100 px-1.5 py-0.5 text-[10px] text-slate-600">
                    铺平 {a.spread}
                  </span>
                )}
              </button>
              {isOpen && (
                <ul className="space-y-0.5 bg-slate-50/60 px-3 pb-2 pl-8 text-[11px] text-slate-500">
                  {mine.slice(0, 30).map((i) => (
                    <li key={i.planId} className="flex items-center gap-1.5">
                      <span className="truncate">#{i.patientId.slice(0, 8)}</span>
                      <span className="text-[10px] text-slate-400">
                        {ASSIGN_STRATEGY_META[i.assignStrategy as AssignStrategy]?.groupZh}
                      </span>
                      {i.selectionMode === 'explore' && (
                        <span className="rounded bg-violet-100 px-1 text-[10px] text-violet-600">探索</span>
                      )}
                    </li>
                  ))}
                  {mine.length > 30 && (
                    <li className="text-[10px] text-slate-400">…另有 {mine.length - 30} 条</li>
                  )}
                </ul>
              )}
            </div>
          );
        })}
      </div>

      {/* 已满被跳过的人 —— 仍然列出来:主管要看见"他不是被漏了,是已经满了" */}
      {sheet.skippedAgents.length > 0 && (
        <div className="border-t border-slate-100 px-3 py-1.5 text-[10.5px] text-slate-400">
          已满未分配:{sheet.skippedAgents.map((a) => `${a.name ?? a.userId}(${a.inHand})`).join('、')}
        </div>
      )}

      {/* ③ 微调 —— **只有两项**(T13)。多加一项就违教条,评审按这条卡 */}
      <div className="space-y-1.5 border-t border-slate-100 px-3 py-2">
        <div className="flex items-center gap-2">
          <span className="flex-none text-slate-500">时效</span>
          {[3, 5, 7].map((d) => (
            <button
              key={d}
              type="button"
              disabled={done}
              onClick={() => setExpiresInDays(d)}
              className={cn(
                'rounded border px-2 py-0.5 text-[11px] transition-colors',
                expiresInDays === d
                  ? 'border-brand-500 bg-brand-50 font-medium text-brand-700'
                  : 'border-slate-200 text-slate-500 hover:bg-slate-50',
                done && 'opacity-60',
              )}
            >
              {d} 天
            </button>
          ))}
          {/* ⭐ T14:没有历史结案数据,这个 3 天就是默认值,必须当场标明 */}
          <span className="text-[10px] text-slate-400">默认 3 天(暂无历史结案数据)</span>
        </div>
        <p className="text-[10.5px] leading-relaxed text-slate-400">
          指定客服请在对话里说(如「王强这批给李莉」);换人群也回对话让助手重新圈。
        </p>
      </div>

      {/* 依据 —— 成品句子原样展示,不在前端改写 */}
      <div className="space-y-0.5 border-t border-slate-100 bg-slate-50/50 px-3 py-2 text-[10.5px] leading-relaxed text-slate-500">
        <p>{sheet.selectionNote}</p>
        <p>{sheet.capacityNote}</p>
        <p>{sheet.rosterNote}</p>
      </div>

      {/* ④ 确认 */}
      <div className="border-t border-slate-100 px-3 py-2">
        {error && <p className="mb-1.5 text-[11px] text-rose-600">{error}</p>}
        {done ? (
          <div className="flex items-center gap-1.5 text-[11.5px] font-medium text-emerald-700">
            <Check className="h-3.5 w-3.5" />
            已分配 · 批次 #{assignmentId?.slice(0, 8)}
          </div>
        ) : (
          <Can
            perm={Permission.PLAN_DISPATCH}
            fallback={<p className="text-[11px] text-slate-400">你没有分配权限,请让主管确认</p>}
          >
            <button
              type="button"
              disabled={submitting || sheet.placed === 0}
              onClick={() => void confirm()}
              className="flex w-full items-center justify-center gap-1.5 rounded-md bg-brand-600 px-3 py-1.5 text-[12px] font-medium text-white transition-colors hover:bg-brand-700 disabled:opacity-50"
            >
              {submitting ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Check className="h-3.5 w-3.5" />}
              确认分配 {sheet.placed} 条
            </button>
          </Can>
        )}
      </div>
    </div>
  );
}
