'use client';

import { useState } from 'react';
import {
  EXECUTION_OUTCOME_META,
  EXECUTION_OUTCOME_GROUP_META,
  ABANDON_REASON_META,
  abandonReasonsFor,
  SUPPRESS_PERMANENT_DAYS,
  type ExecutionOutcome,
  type ExecutionOutcomeGroup,
} from '@pac/types';
import { cn } from '@/lib/utils';
import { tone } from './shared';
import { DatePicker } from '@/components/ui/date-picker';
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select';

// 回访时间:时(00–23)+ 分(00–59)两个下拉,每分钟粒度,默认 00:00
const HOURS = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, '0'));
const MINUTES = Array.from({ length: 60 }, (_, i) => String(i).padStart(2, '0'));

/// 本地日期 ⇄ 'yyyy-MM-dd' 字符串(避免 new Date(ISO) 的时区偏移)
const toYMD = (d: Date) =>
  `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
const fromYMD = (s: string) => {
  const [y, m, d] = s.split('-').map(Number);
  return new Date(y ?? 1970, (m ?? 1) - 1, d ?? 1);
};

/// outcome 选项从 @pac/types EXECUTION_OUTCOME_META 派生(单一真理源,跟后端共享)
/// 改 enum / label / group / 状态机映射只在 packages/types 一处改,前后端同步生效
/// 2 级分组:按 group 归类(成功 / 不成功 / 保持),hiddenInForm 的历史值不展示
const OUTCOME_GROUPS = (
  Object.keys(EXECUTION_OUTCOME_GROUP_META) as ExecutionOutcomeGroup[]
)
  .sort((a, b) => EXECUTION_OUTCOME_GROUP_META[a].order - EXECUTION_OUTCOME_GROUP_META[b].order)
  .map((g) => ({
    key: g,
    label: EXECUTION_OUTCOME_GROUP_META[g].labelZh,
    options: (
      Object.entries(EXECUTION_OUTCOME_META) as Array<
        [ExecutionOutcome, (typeof EXECUTION_OUTCOME_META)[ExecutionOutcome]]
      >
    )
      .filter(([, meta]) => meta.group === g && !meta.hiddenInForm)
      .map(([value, meta]) => ({ value, label: meta.labelZh, tone: meta.tone, drives: meta.drivesStatus })),
  }))
  .filter((grp) => grp.options.length > 0);

/**
 * 「触达方式」选择器 —— 暂时隐藏(2026-08-04 产品走查)。置 `true` 即恢复。
 *
 * ⚠️ 隐藏的只是**选择器**:`channel` 仍然照常提交(取 `defaultChannel`,即 'phone'),
 *   服务端那一列 NOT NULL,不传会 400。
 * ⚠️ 于是这段时间落库的 channel **全是 'phone'** —— 触达方式的分布在此期间**不可用**,
 *   ⛔ 别拿它出报表(会得出"全都是打电话"的假结论)。要恢复分析就得先把这个选择器放回来。
 */
const CHANNEL_PICKER_VISIBLE: boolean = false;

const CHANNELS = [
  {
    value: 'phone',
    label: '电话',
    tone: 'sky',
    d: 'M2.5 4.5c0-1.1.9-2 2-2h2.3c.9 0 1.7.6 1.9 1.5l.8 3a2 2 0 0 1-.6 2L7.4 10a12 12 0 0 0 5.6 5.6l1-1.5a2 2 0 0 1 2-.6l3 .8c.9.2 1.5 1 1.5 1.9V18.5c0 1.1-.9 2-2 2A18 18 0 0 1 2.5 4.5z',
  },
  {
    value: 'wecom',
    label: '企微',
    tone: 'emerald',
    d: 'M21 11.5a8.38 8.38 0 0 1-.9 3.8 8.5 8.5 0 0 1-7.6 4.7 8.38 8.38 0 0 1-3.8-.9L3 21l1.9-5.7a8.38 8.38 0 0 1-.9-3.8 8.5 8.5 0 0 1 4.7-7.6 8.38 8.38 0 0 1 3.8-.9h.5a8.48 8.48 0 0 1 8 8v.5z',
  },
  {
    value: 'sms',
    label: '短信',
    tone: 'violet',
    d: 'M4 5a2 2 0 0 1 2-2h12a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H8l-4 4V5z',
  },
];

/**
 * 「点了这个会怎么样」的一句话 —— **必须同时看 drivesStatus 和 suppressDays**。
 *
 * 🔴 由来(2026-08-06 走查):原来只按 `drivesStatus` 查表,于是「秒挂」显示
 *   「本次任务保留,等下次跟进」—— 而它刚被配上 30 天冷静期,**下次跟进是一个月以后**。
 *   客服看到"等下次跟进"会以为过两天还能再打,实际这单一个月不会再出现。
 *   ⛔ 别再退回只看 drivesStatus 的写法:抑制期是**用户能感知**的后果,不说就是骗人(T14)。
 *
 * ⚠️ `abandoned` 也不是铁板一块:带有限抑制期的(如「再考虑」7 天)其实会**自己回来**,
 *   说成"将关闭"会让客服以为这人废了,于是白白再手动跟一遍。
 *
 * @param drives   completed(结案)/ abandoned(关闭)/ keep(保留)
 * @param days     抑制天数;null = 不固定(看约定回访日)
 * @param scheduled 是否是「约定下次回访」—— 它的抑制点是主管填的那个日期,不是天数
 */
function stateHint(drives: string, days: number | null, scheduled: boolean): string {
  if (drives === 'completed') return '本次任务将结案';
  if (drives === 'abandoned') {
    // 永久档(外院治疗 / 无效)用的是一个极大的天数,不是"多少天后回来"
    return days != null && days < SUPPRESS_PERMANENT_DAYS
      ? `本次任务将关闭,${days} 天后若仍有需要会重新出现`
      : '本次任务将关闭(不再召回)';
  }
  if (scheduled) return '本次任务保留,到您约定的日期再跟进';
  return days != null
    ? `本次任务保留,但 ${days} 天内不再出现`
    : '本次任务保留,等下次跟进';
}

// 放弃原因:取 ABANDON_REASON_META 的 form 子集(跟宿主关闭弹窗共用一套 key、各显示各的文案)。
// 原本这里是写死的中文数组 + 一张中译 enum 的映射表,已删 —— 映射表里「已转介他人→treated_elsewhere」
// 之类的语义错位就是那么来的;现在 key 和 label 同源,不会再漂。
const ABANDON_REASONS = abandonReasonsFor('form').map((key) => ({
  key,
  labelZh: ABANDON_REASON_META[key].labelZh,
}));

/** 分组标题(成功 / 不成功 / 保持)是否显示 —— 见下方渲染处的注释 */
const OUTCOME_GROUP_TITLE_VISIBLE: boolean = false;

export function OutcomeForm({
  plan,
  onSubmit,
  defaultChannel = 'phone',
}: {
  plan: { contactAttempts: number; status?: string };
  onSubmit: (data: {
    channel: string;
    outcome: string;
    notes: string;
    scheduledNextAt: string;
    abandonReasons: string[];
  }) => void;
  defaultChannel?: string;
}) {
  const [channel, setChannel] = useState(defaultChannel);
  const [outcome, setOutcome] = useState<string | null>(null);
  const [notes, setNotes] = useState('');
  const [scheduledNextAt, setScheduledNextAt] = useState('');
  const [abandonReasons, setAbandonReasons] = useState<string[]>([]);
  const [submitted, setSubmitted] = useState(false);

  const curMeta = outcome ? EXECUTION_OUTCOME_META[outcome as ExecutionOutcome] : null;
  const cur = curMeta
    ? { tone: curMeta.tone, drives: curMeta.drivesStatus, suppressDays: curMeta.suppressDays }
    : null;
  const needsScheduledNext = outcome === 'scheduled_next';
  /**
   * 放弃原因子选项**只挂在「放弃」下**(2026-08-05 从「明确拒绝」移过来)。
   *
   * ⚠️ 为什么移:子选项(号码空号 / 迁居外地 / 已转介 / 治疗机会判断不对 / 治疗中 / 已跟进)
   *   描述的全是**客服对这条召回的处置判断**,不是患者的态度。挂在「明确拒绝」下会逼客服
   *   为了填原因而去选一个不符合事实的结果项 —— 号码是空号,他哪知道患者拒不拒绝。
   * ⚠️ 两者的 drivesStatus 都是 abandoned,所以这次移动**不改变任何单子的最终状态**,
   *   只是把"为什么放弃"问到了对的那个选项下面。
   */
  const needsAbandonReasons = !!outcome && outcome === 'abandoned';
  // 终态(已结案/已放弃/已被替代)不可再写 execution(后端也会拒);提交按钮置灰 + 顶部提示
  const isTerminal = ['completed', 'abandoned', 'superseded'].includes(plan.status ?? '');
  const canSubmit = !!outcome && !!channel && !submitted && !isTerminal;
  // 下次回访时间拆成 日期('yyyy-MM-dd')+ 时间('HH:mm')两部分,日历选日期、时/分下拉选时间,合成回 scheduledNextAt
  const schedDate = scheduledNextAt.split('T')[0] ?? '';
  const schedTime = scheduledNextAt.split('T')[1] ?? '';
  const [schedHour = '', schedMin = ''] = schedTime.split(':');
  // 时/分任一变更后合成回时间(缺省补 00),需已选日期
  const setTime = (h: string, m: string) => {
    if (schedDate) setScheduledNextAt(`${schedDate}T${(h || '00').padStart(2, '0')}:${(m || '00').padStart(2, '0')}`);
  };

  const handleSubmit = () => {
    if (!canSubmit) return;
    setSubmitted(true);
    onSubmit({ channel, outcome: outcome!, notes, scheduledNextAt, abandonReasons });
    setTimeout(() => {
      setSubmitted(false);
      setOutcome(null);
      setNotes('');
      setScheduledNextAt('');
      setAbandonReasons([]);
      setChannel(defaultChannel);
    }, 2400);
  };

  return (
    <div className="flex flex-col gap-2.5 h-full min-h-0">
      {isTerminal && (
        <div className="flex-none rounded bg-slate-100 border px-2.5 py-1.5 text-[11px] text-slate-500 leading-snug">
          本任务已{plan.status === 'abandoned' ? '放弃' : plan.status === 'superseded' ? '被新版本替代' : '结案'},不可再提交执行;如需重新跟进,等召回重新生成。
        </div>
      )}
      {CHANNEL_PICKER_VISIBLE && (
      <div className="flex-none">
        <div className="text-[10.5px] font-semibold text-slate-500 uppercase tracking-wider mb-1.5">
          触达方式 <span className="text-rose-500">*</span>
        </div>
        <div className="grid grid-cols-3 gap-1">
          {CHANNELS.map((c) => {
            const T = tone(c.tone);
            const selected = c.value === channel;
            return (
              <button
                key={c.value}
                onClick={() => setChannel(c.value)}
                className={cn(
                  'px-2 py-1.5 rounded text-[11.5px] border transition-colors inline-flex items-center justify-center gap-1.5',
                  selected
                    ? `${T.bg} ${T.text} border-current font-medium ring-1 ring-current/30`
                    : 'bg-white text-slate-700 hover:bg-slate-50',
                )}
              >
                <svg
                  viewBox="0 0 24 24"
                  className="w-3.5 h-3.5"
                  fill="none"
                  stroke="currentColor"
                  strokeWidth="1.8"
                  strokeLinecap="round"
                  strokeLinejoin="round"
                >
                  <path d={c.d} />
                </svg>
                {c.label}
              </button>
            );
          })}
        </div>
      </div>
      )}

      <div className="flex-none">
        <div className="text-[10.5px] font-semibold text-slate-500 uppercase tracking-wider mb-1.5">
          通话结果 <span className="text-rose-500">*</span>
        </div>
        <div className="space-y-2">
          {OUTCOME_GROUPS.map((grp) => (
            <div key={grp.key}>
              {/*
                分组标题(成功 / 不成功 / 保持)—— **隐藏**(2026-08-05 产品走查)。
                ⛔ 只藏标题,**分组本身必须留着**:它决定选项的排列顺序和 grid 分行,
                  也是 EXECUTION_OUTCOME_META 里 group 字段的展示投影 ——
                  把 OUTCOME_GROUPS 拍平会让选项顺序变成声明序,而那个顺序没有业务含义。
                置 true 即恢复。
              */}
              {OUTCOME_GROUP_TITLE_VISIBLE && (
                <div className="text-[9.5px] font-semibold text-slate-400 mb-1 pl-0.5">{grp.label}</div>
              )}
              <div className="grid grid-cols-2 gap-1">
                {grp.options.map((o) => {
                  const T = tone(o.tone);
                  const selected = o.value === outcome;
                  return (
                    <button
                      key={o.value}
                      onClick={() => setOutcome(o.value)}
                      className={cn(
                        'px-2 py-1 rounded text-left text-[11px] border transition-colors flex items-center gap-1.5',
                        selected
                          ? `${T.bg} ${T.text} border-current font-medium ring-1 ring-current/30`
                          : 'bg-white text-slate-700 hover:bg-slate-50',
                      )}
                    >
                      <span className={cn('flex-none w-1.5 h-1.5 rounded-full', selected ? T.dot : 'bg-slate-300')} />
                      <span className="truncate">{o.label}</span>
                    </button>
                  );
                })}
              </div>
            </div>
          ))}
        </div>
        {cur && (
          <div className={cn('mt-1.5 text-[10.5px] flex items-center gap-1.5', tone(cur.tone).text)}>
            <svg viewBox="0 0 24 24" className="w-3 h-3" fill="none" stroke="currentColor" strokeWidth="2">
              <path d="M5 12h14M13 5l7 7-7 7" strokeLinecap="round" />
            </svg>
            <span>{stateHint(cur.drives, cur.suppressDays, needsScheduledNext)}</span>
          </div>
        )}
      </div>

      {needsScheduledNext && (
        <div className="flex-none rounded bg-amber-50 border border-amber-200 px-2.5 py-2">
          <label className="block text-[10.5px] font-semibold text-amber-900 mb-1">下次回访时间</label>
          <div className="flex gap-1.5">
            <div className="flex-1 min-w-0">
              <DatePicker
                value={schedDate ? fromYMD(schedDate) : undefined}
                // 选日期时,时间缺省给 00:00(默认 0 时 0 分)
                onChange={(d) => setScheduledNextAt(d ? `${toYMD(d)}T${schedTime || '00:00'}` : '')}
                min={new Date(new Date().setHours(0, 0, 0, 0))}
                placeholder="选择回访日期"
              />
            </div>
            {/* 时 / 分 两个下拉,每分钟粒度,默认 00:00;下拉内可键入数字快速定位 */}
            <div className="flex flex-none items-center gap-0.5">
              <Select value={schedHour || undefined} disabled={!schedDate} onValueChange={(h) => setTime(h, schedMin)}>
                <SelectTrigger className="h-9 w-[58px] bg-white text-[12.5px]">
                  <SelectValue placeholder="时" />
                </SelectTrigger>
                <SelectContent className="max-h-64 min-w-0">
                  {HOURS.map((h) => (
                    <SelectItem key={h} value={h} className="text-[12.5px]">
                      {h}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
              <span className="text-[12.5px] text-amber-900">:</span>
              <Select value={schedMin || undefined} disabled={!schedDate} onValueChange={(m) => setTime(schedHour, m)}>
                <SelectTrigger className="h-9 w-[58px] bg-white text-[12.5px]">
                  <SelectValue placeholder="分" />
                </SelectTrigger>
                <SelectContent className="max-h-64 min-w-0">
                  {MINUTES.map((m) => (
                    <SelectItem key={m} value={m} className="text-[12.5px]">
                      {m}
                    </SelectItem>
                  ))}
                </SelectContent>
              </Select>
            </div>
          </div>
        </div>
      )}
      {needsAbandonReasons && (
        <div className="flex-none rounded bg-rose-50 border border-rose-200 px-2.5 py-2">
          <label className="block text-[10.5px] font-semibold text-rose-900 mb-1.5">放弃原因(可多选)</label>
          <div className="grid grid-cols-2 gap-1">
            {ABANDON_REASONS.map((r) => (
              <label
                key={r.key}
                className="flex items-center gap-1.5 px-1.5 py-1 rounded bg-white border border-rose-100 text-[10.5px] cursor-pointer"
              >
                <input
                  type="checkbox"
                  checked={abandonReasons.includes(r.key)}
                  onChange={() =>
                    setAbandonReasons(
                      abandonReasons.includes(r.key)
                        ? abandonReasons.filter((x) => x !== r.key)
                        : [...abandonReasons, r.key],
                    )
                  }
                  className="accent-rose-600 scale-90"
                />
                <span className="text-slate-700 truncate">{r.labelZh}</span>
              </label>
            ))}
          </div>
        </div>
      )}

      {/* 召回反馈已移出执行表单 → 详情页标题栏拇指(RecallFeedbackControl);此处不再收集 */}

      <div className="flex-1 min-h-0 flex flex-col">
        <label className="flex-none text-[10.5px] font-semibold text-slate-500 uppercase tracking-wider mb-1">
          通话纪要
        </label>
        <textarea
          value={notes}
          onChange={(e) => setNotes(e.target.value)}
          placeholder="患者反馈、关键点、需告知医生的信息……"
          className="flex-1 min-h-[60px] px-2.5 py-1.5 rounded border bg-white text-[12.5px] resize-none focus:outline-none focus:ring-2 focus:ring-brand-200 focus:border-brand-300"
        />
      </div>

      <div className="flex-none flex items-center justify-between gap-2 pt-1">
        <span className="text-[10px] text-slate-400 leading-tight">
          本次为第 <strong className="text-slate-600 tabular-nums">{plan.contactAttempts + 1}</strong> 次触达
        </span>
        <div className="flex items-center gap-1.5">
          {/* 「新建预约」按钮已上移到详情页顶栏右上角(与 潜在 / 回访 同处动作区) */}
          <button
            disabled={!canSubmit}
            onClick={handleSubmit}
            className={cn(
              'px-4 py-1.5 rounded text-[12.5px] font-semibold inline-flex items-center gap-1.5 transition-colors',
              submitted
                ? 'bg-emerald-600 text-white'
                : canSubmit
                  ? 'bg-brand-600 text-white hover:bg-brand-700'
                  : 'bg-slate-100 text-slate-400 cursor-not-allowed',
            )}
          >
            {submitted ? (
              <>
                <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" className="w-3.5 h-3.5">
                  <path d="M5 13l4 4L19 7" strokeLinecap="round" strokeLinejoin="round" />
                </svg>
                已归档
              </>
            ) : (
              <>
                <svg
                  viewBox="0 0 24 24"
                  fill="none"
                  stroke="currentColor"
                  strokeWidth="1.8"
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  className="w-3.5 h-3.5"
                >
                  <path d="M22 2L11 13M22 2l-7 20-4-9-9-4 20-7z" />
                </svg>
                提交
              </>
            )}
          </button>
        </div>
      </div>
    </div>
  );
}
