'use client';

import { useEffect, useState } from 'react';
import { Loader2 } from 'lucide-react';
import { potentialTreatmentItemName, temperatureWindowHint, type TemperatureValue } from '@pac/types';
import { cn } from '@/lib/utils';
import { plansApi, type PoolMatrix, type PoolMatrixRow } from './plans-api';

/**
 * PoolMatrix — 初选矩阵(8 潜在治疗 × 3 窗口温度)。
 *
 * 生产线的**第一环**:主管在这里点一格,就完成了「初选」,人群随即交给助手出确认单。
 * 矩阵是召回池的一个**视图模式**(列表 ⇄ 矩阵),⛔ 不新开路由 ——
 * 主管本质也是客服、也要执行,割裂成两个页面会把他劈成两个身份(T16)。
 *
 * ═══ 三条配色纪律(五之三)═══════════════════════════════════════
 * ⛔ **数量不参与配色**,底色**按列固定**。
 *   否则「这格橙是因为热、还是因为人多」分不清 —— 实现上列头决定 class,
 *   cell 一个字都不参与计算。⛔ 别写任何 `count > N ? ... : ...`。
 * ⛔ **热不用红**:红太冲,是"出事了";橙才是"该动手了"。
 * ⛔ **别用 brand-***:品牌蓝 #0032A0 比冷档的 blue-100 深太多,混用会让「冷」看着像选中态。
 *
 * ⚠️ 三档之外还有一列**「待重算」** —— 上线到全量重算跑完之间,老画像没有窗口边界。
 *   把它们并进「冷」能让行合计好看,但那是**假分布**(T14)。宁可多一列让主管问一句。
 */

/**
 * 列定义:底色写死在这里,**与任何数字无关**(见文件头配色纪律)。
 * `scale` 是列头那条**温度计刻度**的颜色 —— 与格子底色同一族,让人一眼看出这是一条冷热轴。
 */
const COLUMNS = [
  { key: 'hot', icon: '🔥', zh: '热', cell: 'bg-orange-400 text-white hover:bg-orange-500', scale: 'from-orange-500 to-orange-400', hint: '还在该治疗自己的黄金期内 —— 医生刚说过,患者还记得' },
  { key: 'warm', icon: '🌡', zh: '温', cell: 'bg-amber-100 text-amber-900 hover:bg-amber-200', scale: 'from-orange-300 to-amber-200', hint: '过了黄金期但没出临床周期 —— 还来得及,话术要给个理由' },
  { key: 'cold', icon: '❄️', zh: '冷', cell: 'bg-blue-100 text-blue-900 hover:bg-blue-200', scale: 'from-amber-100 to-blue-200', hint: '超出该治疗的临床周期 —— 情况可能已经变了,先问近况' },
] as const;

type TempKey = (typeof COLUMNS)[number]['key'];

export function PoolMatrix({
  clinicId,
  selected,
  onPick,
  filtered,
}: {
  clinicId?: string;
  /** 当前选中的格子(回显用) */
  selected?: { treatment: string; temperature: TempKey } | null;
  /** 点格子 → 初选完成,把这一格的人群交出去 */
  onPick: (cell: { treatment: string; treatmentZh: string; temperature: TempKey; count: number }) => void;
  /**
   * 「按当前筛选的人群移交」的兜底入口 —— 主管用左栏筛选标签自己圈的那一批。
   * ⭐ 收进浮层而不是常驻一条横幅:入口只留**一个**(「分配」),
   *   但这条路不能丢 —— 矩阵只有「治疗项 × 温度」两轴,主管想按别的条件圈人时走它。
   */
  filtered?: { count: number; onHandoff: () => void };
}) {
  const [data, setData] = useState<PoolMatrix | null>(null);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    let alive = true;
    setError(null);
    plansApi
      .matrix(clinicId)
      .then((d) => alive && setData(d))
      .catch((e: unknown) => alive && setError(e instanceof Error ? e.message : String(e)));
    return () => {
      alive = false;
    };
  }, [clinicId]);

  if (error) {
    return <div className="p-3 text-[12px] text-rose-600">矩阵加载失败:{error}</div>;
  }
  if (!data) {
    return (
      <div className="flex items-center justify-center gap-2 p-6 text-[12px] text-slate-400">
        <Loader2 className="h-3.5 w-3.5 animate-spin" />
        正在数人…
      </div>
    );
  }

  return (
    <div className="flex-1 overflow-y-auto p-2">
      <table className="w-full border-separate border-spacing-[3px] text-[12px]">
        <thead>
          <tr>
            <th className="w-[66px] text-left font-normal text-slate-400">潜在治疗</th>
            {COLUMNS.map((c) => (
              <th key={c.key} title={c.hint} className="font-medium leading-tight text-slate-600">
                {c.icon} {c.zh}
              </th>
            ))}
            {data.unknownTotal > 0 && (
              <th
                className="font-normal text-slate-400"
                title="画像还没算出窗口边界,温度待重算。⛔ 没有并进「冷」——并进去数字好看,但那是假分布。"
              >
                待重算
              </th>
            )}
          </tr>
          {/* ⭐ 温度计刻度 —— **一条连续的**渐变横跨三列(不是三段各画各的)。
              温度是连续量,切成三段独立色块就退化成"三个分类",冷热轴的意思就没了。
              ⚠️ 用 colSpan 跨列 + 取消该行的 border-spacing,段与段之间才不会裂开。 */}
          <tr>
            <th className="p-0" />
            <th colSpan={3} className="p-0 pb-1">
              <div className="h-1.5 rounded-full bg-gradient-to-r from-orange-500 via-amber-200 to-blue-200" />
            </th>
            {data.unknownTotal > 0 && <th className="p-0" />}
          </tr>
        </thead>
        <tbody>
          {data.rows.map((row) => (
            <MatrixRow
              key={row.key}
              row={row}
              showUnknown={data.unknownTotal > 0}
              selected={selected}
              onPick={onPick}
            />
          ))}
        </tbody>
      </table>

      {/* ⚠️ 口径(「数字是去重患者数、点一格即移交助手」)不再占一段说明文字 ——
          它已经落在**每个格子的 hover** 里(见 MatrixRow 的 title),那里才是主管会去看的地方。
          常驻一段小字既挤地方又没人读,反而把矩阵本身冲淡了。 */}
      {data.note && (
        <p className="mt-1 rounded bg-amber-50 px-2 py-1.5 text-[10.5px] leading-relaxed text-amber-800">
          {data.note}
        </p>
      )}
      {filtered && filtered.count > 0 && (
        <button
          type="button"
          onClick={filtered.onHandoff}
          className="mt-2 w-full rounded border border-dashed border-slate-200 py-1.5 text-[11.5px] text-slate-500 transition-colors hover:border-brand-200 hover:bg-brand-50/40 hover:text-brand-700"
        >
          或者:按左栏当前筛选的 {filtered.count} 人移交
        </button>
      )}
    </div>
  );
}

function MatrixRow({
  row,
  showUnknown,
  selected,
  onPick,
}: {
  row: PoolMatrixRow;
  showUnknown: boolean;
  selected?: { treatment: string; temperature: TempKey } | null;
  onPick: (cell: { treatment: string; treatmentZh: string; temperature: TempKey; count: number }) => void;
}) {
  // ⛔ 中文一律查 labels.ts,别在组件里另写一张表 —— 改措辞要即时生效、且全站一处。
  //   这里用**项目名**(种植 / 早矫 / 拔牙)而不是卡片措辞(种植治疗):
  //   矩阵是一张 8×3 的表,每行都拖着「治疗」两个字纯属噪声,而且挤掉了列宽。
  const zh = potentialTreatmentItemName(row.key);
  return (
    <tr>
      <th
        scope="row"
        title={row.hint}
        className="truncate text-left text-[11.5px] font-normal text-slate-600"
      >
        {zh}
      </th>
      {COLUMNS.map((c) => {
        const n = row[c.key];
        const isSelected = selected?.treatment === row.key && selected.temperature === c.key;
        return (
          <td key={c.key} className="p-0">
            <button
              type="button"
              disabled={n === 0}
              onClick={() => onPick({ treatment: row.key, treatmentZh: zh, temperature: c.key, count: n })}
              // ⭐ hover 说清**凭什么算这一档** —— 天数来自 DiagnosisTreatmentMap 现算,
              //   多码标签(拔牙/牙周)给的是区间,因为每条 gap 按自己那个码判档,
              //   本来就不存在"标签级的那一个天数"(见 @pac/types temperatureWindowHint)。
              title={
                n === 0
                  ? `${zh}·${c.zh}:暂无人\n${temperatureWindowHint(row.key, c.key as TemperatureValue)}`
                  : `${zh}·${c.zh}:${n} 位患者(已去重)\n${temperatureWindowHint(row.key, c.key as TemperatureValue)}\n点击即把这批人交给助手出分配方案`
              }
              className={cn(
                'h-7 w-full rounded text-[12px] tabular-nums transition-colors',
                // ⭐ 底色只看列(c.cell),**不看 n** —— 见文件头配色纪律
                n === 0
                  ? 'cursor-default bg-slate-50 text-slate-300'
                  : cn(c.cell, 'cursor-pointer font-medium'),
                isSelected && 'ring-2 ring-slate-800 ring-offset-1',
              )}
            >
              {n}
            </button>
          </td>
        );
      })}
      {showUnknown && (
        <td className="p-0">
          <div
            className="flex h-7 items-center justify-center rounded bg-slate-50 text-[11.5px] tabular-nums text-slate-400"
            title="温度待重算 —— 画像还没算出窗口边界。这是重算进度,不是数据缺失。"
          >
            {row.unknown || '—'}
          </div>
        </td>
      )}
    </tr>
  );
}
