'use client';

import { useCallback, useEffect, useState } from 'react';
import {
  AnchorMode,
  ANCHOR_MODE_META,
  TEMPERATURE_META,
  type AnchorModeValue,
  type TemperatureValue,
} from '@pac/types';
import { plansApi, type PoolMatrix as PoolMatrixData } from '@/components/plans/plans-api';
import { PoolMatrix } from '@/components/plans/pool-matrix';
import { useAssignmentSyncStore } from '@/stores/assignment-sync-store';
import { CohortPreview } from './cohort-preview';
import { useAssistantStore } from '@/stores/assistant-store';
import { cn } from '@/lib/utils';

/**
 * 「分一批新的」—— **常驻面板**，摆在「我分的批次」正上方。
 *
 * 🔴 2026-08-11 产品定：**不再由按钮触发、也不再有第二版**。
 *   在此之前有两版（设计稿 `newBatchMode`）：`window` 居中 modal、`arcade` 里世界打地鼠，
 *   由头部一个「样式1 / 样式2」开关切，点「＋ 分一批新的」才展开。
 *   ⇒ 现在只保留 `window` 那份的内容（矩阵本身），并且**直接摊开**：
 *   矩阵是主管进这一屏的**第一个动作**，藏在按钮后面等于每次都要多点一下才看得到池子。
 *   ⛔ 别再把它收回浮层里 —— 上下结构（挑格子 → 看这些格子分出去之后怎么样）
 *   本身就是这一栏要讲的因果，收起来这条线就断了。
 * ⛔ 里世界那版（传送门 / 锤子光标 / 数据粒子）连同 globals.css 的 `.pac-portal-*`
 *   一起删了 —— 需要考古去翻 git，⛔ 别凭印象重写一份。
 *
 * ⛔ 挑完格子走的仍是**同一条**移交路：`useAssistantStore.handoff()` ——
 *   发数据流 → 粒子飞到助手 → 落地后自动开大窗并说那句话。
 *   ⚠️ 这条链路是现成的，⛔ 别在这儿另写一套移交：移交的时序（动画多长、
 *   什么时候开窗）是舞台层的事，业务侧复制一份必然漂。
 */

type Picked = {
  treatment: string;
  treatmentZh: string;
  temperature: TemperatureValue;
  count: number;
  rect: { x: number; y: number; w: number; h: number };
};

/**
 * 🔴 口径要**写进交给助手的那句话里**。
 *
 * 移交只有一条自然语言通道(模型看不到界面开关),所以「按末诊」这三个字就是
 * 主管选择的**唯一载体** —— `ANCHOR_MODE_TOOL_DESC` 那边的判据也正是"话里带没带这三个字"。
 * ⛔ 别改这句的措辞而不同步改工具说明:改了模型就不传 `anchorMode`,
 *   主管在按末诊那版上点的格子,确认单会按诊断口径捞另一批人 —— 而且不报错(T14)。
 */
function handoff(c: Picked, mode: AnchorModeValue) {
  const tempZh = TEMPERATURE_META[c.temperature].zh;
  const tag = ANCHOR_MODE_META[mode].batchTag;
  const cell = `${c.treatmentZh} · ${tempZh}${tag ? `(${tag})` : ''}`;
  useAssistantStore.getState().handoff({
    text: `帮我给「${cell}」这批患者出一份分配方案`,
    from: c.rect,
    count: c.count,
    treatment: c.treatmentZh,
    temperature: tempZh,
  });
}

/// 中间表要用的那一格(不含 rect —— 粒子起点改成「确定」按钮,见 CohortPreview)
type PickedCell = Omit<Picked, 'rect'>;

export function NewBatchPanel({ clinicId }: { clinicId: string | null }) {
  const [matrix, setMatrix] = useState<PoolMatrixData | null>(null);
  const [error, setError] = useState<string | null>(null);

  /**
   * 🔴 档位口径 —— **这是口径开关,不是样式开关**(⛔ 别拿它跟当年那个「样式1/样式2」类比)。
   *   它决定分到的是哪批人:同一个「三个月内」,按诊断和按末诊是两批完全不同的人。
   *
   * ⚠️ 记进 localStorage 是为了**别让他每次进来都要重切**(主管多半长期用同一版);
   *   但切换那一刻必须**看得见**(下面开关旁边那句说明),⛔ 不能像样式开关那样悄悄记住。
   */
  const [mode, setMode] = useState<AnchorModeValue>(AnchorMode.DIAGNOSIS);
  useEffect(() => {
    const v = localStorage.getItem('pac.anchorMode');
    if (v === AnchorMode.LAST_VISIT || v === AnchorMode.DIAGNOSIS) setMode(v);
  }, []);
  const pickMode = (m: AnchorModeValue) => {
    setMode(m);
    localStorage.setItem('pac.anchorMode', m);
  };

  /**
   * 换诊所 / 换口径先清空 —— ⛔ 不能让上一版的人数在新标题下面多留一帧
   * (那一帧里主管点下去,拿到的就是另一批人)。
   * ⚠️ 只挂 `clinicId` / `mode`:下面那次「分完重拉」不走这里，否则每分一批矩阵都要闪一下骨架。
   */
  useEffect(() => {
    setMatrix(null);
    setError(null);
  }, [clinicId, mode]);

  /**
   * 取矩阵。**助手那边确认/撤销之后也要重拉**（`syncSeq`）：
   * 刚分走的人就是从这些格子里出去的，不重拉的话主管会对着**已经分掉的数**再点一次。
   */
  const syncSeq = useAssignmentSyncStore((s) => s.seq);
  useEffect(() => {
    if (!clinicId) return;
    let alive = true;
    plansApi
      .matrix(clinicId, mode)
      .then((r) => alive && setMatrix(r))
      .catch((e) => alive && setError(e instanceof Error ? e.message : '取矩阵失败'));
    return () => {
      alive = false;
    };
  }, [clinicId, syncSeq, mode]);

  /**
   * ⚠️ 口径取**服务端回声**(`matrix.anchorMode`)而不是本地 state:
   *   两者理论上一致,但万一请求慢/失败留着旧数据,回声能保证"这批数是按哪版算的"跟着数据走,
   *   ⛔ 不会出现"开关已经切了、格子还是旧那版、点下去按新那版圈人"这种错位。
   */
  const dataMode = matrix?.anchorMode ?? mode;

  /**
   * 🔴 点格子**不再直接移交**(2026-08-11 产品定):先弹中间表,让主管看清这批人是谁,
   *   点「确定」才交给助手。
   * ⛔ 别为了"少一步"把它改回一点就走 —— 这一步换来的是主管对这批人**看过一眼**,
   *   而分配是他签字的动作。
   * ⚠️ 只存格子语义,⛔ 不存点击时的 rect:粒子起点已改成中间表上那个「确定」按钮
   *   (从一个已经被弹窗盖住的格子起飞,看起来像凭空出现)。
   */
  const [preview, setPreview] = useState<PickedCell | null>(null);
  const pick = useCallback((c: Picked) => {
    const { rect: _rect, ...cell } = c;
    setPreview(cell);
  }, []);

  return (
    <div className="flex w-full flex-col overflow-hidden rounded-lg border bg-white">
      <div className="flex flex-none flex-wrap items-center gap-x-2 gap-y-1 border-b px-3.5 py-2.5">
        <span className="text-[13.5px] font-semibold text-slate-900">分一批新的</span>
        <span className="text-[11px] text-slate-400">
          点一格 = 把这批人交给助手，助手出确认单
        </span>
        {/* ⭐ 口径开关 —— 挨着列头放，主管点格子之前就知道这些数是按什么算的。
            ⚠️ 旁边那句 axisZh 不是装饰：列头写的是「三个月内」，而"什么的三个月"
              只有这一处说得清；⛔ 别为了省空间把它收进 tooltip。 */}
        <span className="ml-auto flex items-center gap-2">
          <span className="hidden text-[11px] text-slate-400 sm:inline">
            {ANCHOR_MODE_META[dataMode].axisZh}
          </span>
          <span className="inline-flex rounded-lg bg-slate-100 p-0.5">
            {([AnchorMode.DIAGNOSIS, AnchorMode.LAST_VISIT] as const).map((m) => (
              <button
                key={m}
                type="button"
                title={ANCHOR_MODE_META[m].hint}
                onClick={() => pickMode(m)}
                className={cn(
                  'rounded-md px-2.5 py-1 text-[11.5px] transition-colors',
                  mode === m
                    ? 'bg-white text-slate-900 shadow-sm'
                    : 'text-slate-500 hover:text-slate-700',
                )}
              >
                {ANCHOR_MODE_META[m].zh}
              </button>
            ))}
          </span>
        </span>
      </div>
      {/* ⚠️ 小屏兜底:矩阵有六列,窄了就横向滚,⛔ 别让它压缩到看不清数字 */}
      <div className="overflow-x-auto">
        {!clinicId ? (
          <p className="py-8 text-center text-[12px] text-slate-400">先选一家诊所</p>
        ) : error ? (
          <p className="py-8 text-center text-[12px] text-rose-600">{error}</p>
        ) : matrix ? (
          /* ⚠️ `w-full` 覆盖组件默认的 560px（那是当年给左栏 popover 的）——
             不覆盖的话这一栏有一千多像素宽而矩阵只占 560，右边一大片空白。
             `min-w-[34rem]` 保证再窄也不把数字挤糊，由外层横向滚兜住。 */
          <PoolMatrix
            data={matrix}
            selected={null}
            onPick={pick}
            anchorMode={dataMode}
            className="w-full min-w-[34rem]"
          />
        ) : (
          <div className="space-y-2 p-3.5">
            {Array.from({ length: 6 }).map((_, i) => (
              <div key={i} className="h-6 animate-pulse rounded bg-slate-100" />
            ))}
          </div>
        )}
      </div>

      {preview && clinicId && (
        <CohortPreview
          clinicId={clinicId}
          cell={preview}
          anchorMode={dataMode}
          onCancel={() => setPreview(null)}
          onConfirm={(from) => {
            setPreview(null);
            handoff({ ...preview, rect: from }, dataMode);
          }}
        />
      )}
    </div>
  );
}
