'use client';
import { Fragment, useEffect, useState } from 'react';
import { useQuery } from '@tanstack/react-query';
import { useParams, useRouter } from 'next/navigation';
import { api } from '@/lib/api';
import { StatusBadge, IconToggle, IconBan, IconEdit, IconTrash, IconClose, CopyButton } from '@/components/ui';
import { useAuth } from '@/lib/auth-context';
import { EmployeePicker, CategoryPicker, DepartmentPicker } from '@/components/pickers';
import { FloatInput, FloatSelect, FloatArea } from '@/components/float';
import { formatDate, formatDateTime, capitalizeFirst as cap } from '@/lib/format';
import { validate, v, Errors } from '@/lib/validation';
import { useDialog } from '@/components/dialog';
import { AttachmentComposer, AttachmentRow, useStagedAttachments, uploadStaged, linkRowErrors } from '@/components/attachments';
import { CommentThread, CommentBox } from '@/components/comments';
import { DatePicker } from '@/components/date-picker';
import { useCan } from '@/lib/permissions';

export default function ActionDetail() {
  const routeParams = useParams<{ id: string }>();
  const params = { id: String(routeParams?.id ?? '') };
  const { user } = useAuth();
  const { confirmDialog, notify } = useDialog();
  const router = useRouter();
  const canDeleteDeviation = useCan('deviation', 'delete');
  const canCreateCapa = useCan('capa', 'create');
  const canDeleteCapa = useCan('capa', 'delete');
  const [editingSub, setEditingSub] = useState<number | null>(null);
  const [showAddSub, setShowAddSub] = useState(false);
  const [showUpdate, setShowUpdate] = useState(false);
  const q = useQuery({ queryKey: ['action', params.id], queryFn: () => api.get(`/api/v1/actions/${params.id}`) });
  const [status, setStatus] = useState('In Progress');
  const [remarks, setRemarks] = useState('');
  const [assignee, setAssignee] = useState<number | null>(null);
  const [category, setCategory] = useState<number | null>(null);
  const [dept, setDept] = useState<number | null>(null);
  const [target, setTarget] = useState('');
  const [prio, setPrio] = useState('Low');
  const [descr, setDescr] = useState('');
  const [remind, setRemind] = useState('');
  const [remindErr, setRemindErr] = useState('');
  const [targetErr, setTargetErr] = useState('');
  const [seeded, setSeeded] = useState(false);
  const a = q.data;
  // Seed the editors once the deviation loads (per record).
  useEffect(() => {
    setSeeded(false);
  }, [params.id]);
  useEffect(() => {
    if (!seeded && a) {
      setAssignee(a.assigned_employee_id ?? null);
      setCategory(a.category_id ?? null);
      setDept(a.department_id ?? null);
      setTarget(a.target_date ? String(a.target_date).slice(0, 10) : '');
      setPrio(a.priority ?? 'Low');
      setDescr(a.description ?? '');
      setRemind(a.reminder_lead_days != null ? String(a.reminder_lead_days) : '');
      setSeeded(true);
    }
  }, [a, seeded]);
  if (q.isLoading) return <p>Loading…</p>;
  if (!a) return <p>Not found</p>;
  const canEditParent = user?.role !== 'employee';
  return (
    <div className="space-y-3">
      <div className="card">
        <div className="flex flex-wrap gap-2 items-start">
          <div className="mr-auto min-w-0">
            <p className="text-xs text-slate-500">{a.reference_number}</p>
            <h1 className="text-xl font-semibold">{a.title}</h1>
          </div>
          <span className="flex gap-1">
            <button type="button" className="btn-plain" aria-label={`Edit ${a.title}`} title="Edit deviation" onClick={() => setShowUpdate((v) => !v)}><IconEdit /></button>
            {canDeleteDeviation && (
              <button type="button" className="btn-plain" aria-label={`Delete ${a.title}`} title="Delete deviation" onClick={async () => {
                if (!await confirmDialog(`Delete deviation ${a.reference_number}? Its CAPAs go with it.`, { title: 'Delete deviation', confirmLabel: 'Delete' })) return;
                try { await api.del(`/api/v1/actions/${a.id}`); notify('Deviation deleted.', { tone: 'success' }); router.push('/actions'); }
                catch (e: any) { notify(e.message, { tone: 'error' }); }
              }}><IconTrash /></button>
            )}
          </span>
        </div>
        <div className="flex flex-wrap gap-2 mt-2">
          <StatusBadge status={a.status} /><span className="badge bg-slate-100">{a.priority}</span>
          {a.escalation_level > 0 && <span className="badge bg-red-100 text-red-800">Escalated L{a.escalation_level}</span>}
          {a.reminder_lead_days != null && <span className="badge bg-blue-50 text-blue-800">Reminder within {a.reminder_lead_days} day{a.reminder_lead_days === 1 ? '' : 's'}</span>}
          {a.recurrence_rule && a.recurrence_rule !== 'none' && <span className="badge bg-purple-100 text-purple-800">↻ {a.recurrence_rule}{a.recurrence_end_date ? ` until ${formatDate(a.recurrence_end_date)}` : ''}</span>}
        </div>
        {a.description && <p className="text-sm mt-2 whitespace-pre-wrap text-slate-600">{a.description}</p>}
        <dl className="grid sm:grid-cols-2 gap-x-6 gap-y-0 mt-3 border rounded-xl overflow-hidden" style={{ borderColor: 'var(--line)' }}>
          {[
            ['Category', a.category_name ?? '—'],
            ['Department', a.department_name ?? '—'],
            ['Target date', formatDate(a.target_date)],
            ['Assignee', a.assignee_name ?? '—'],
            ['Priority', a.priority],
            ['Reminder', a.reminder_lead_days != null ? `${a.reminder_lead_days}d before target` : '—'],
          ].map(([k, val]) => (
            <div key={k} className="flex items-baseline gap-3 px-4 py-2.5 odd:bg-slate-50/60">
              <dt className="text-[11px] font-bold uppercase tracking-wider w-24 shrink-0" style={{ color: 'var(--muted)' }}>{k}</dt>
              <dd className="text-sm font-medium truncate" title={String(val)}>{val}</dd>
            </div>
          ))}
        </dl>
        {a.escalated_at && <p className="text-xs text-red-700 mt-1">Escalated at {formatDateTime(a.escalated_at)}</p>}
        {canEditParent && a.recurrence_rule && a.recurrence_rule !== 'none' && (
          <button type="button" className="btn-plain mt-1" aria-label="Stop repeating this deviation" title="Stop repeating" onClick={async () => {
            if (!await confirmDialog('Stop repeating this deviation? Existing occurrences are kept.', { title: 'Stop repeating' })) return;
            await api.patch(`/api/v1/actions/${a.id}`, { recurrence_rule: 'none' });
            q.refetch();
          }}><IconBan /></button>
        )}
        {(a.occurrences ?? []).length > 0 && (
          <div className="mt-2 text-sm">
            <b className="text-xs text-slate-500">SERIES OCCURRENCES</b>
            {(a.occurrences ?? []).map((o: any) => (
              <p key={o.id} className="text-xs border-t py-1">
                <a className="no-underline hover:underline font-mono" href={`/actions/${o.id}`}>{o.reference_number}</a>
                {' '}<StatusBadge status={o.status} /> <span className="text-slate-500">due {formatDate(o.target_date)}</span>
              </p>
            ))}
          </div>
        )}
      </div>

      <div className="card">
        <div className="flex flex-wrap gap-2 items-center mb-2">
          <h2 className="font-medium mr-auto">CAPAs ({a.sub_actions?.length ?? 0})</h2>
          {canCreateCapa && (showAddSub
            ? <button className="btn-ghost btn-icon" aria-label="Close CAPA form" title="Close" onClick={() => setShowAddSub(false)}><IconClose /></button>
            : <button className="btn-primary btn-sm" onClick={() => setShowAddSub(true)}>+ Add CAPA</button>
          )}
        </div>
        {showAddSub && canCreateCapa && <AddSub actionId={a.id} parentTarget={a.target_date ? String(a.target_date).slice(0, 10) : ''} onDone={() => { setShowAddSub(false); q.refetch(); }} />}
        <div className="overflow-x-auto">
          <table className="table">
            <thead><tr><th scope="col">Reference</th><th scope="col">Title</th><th scope="col">Assignee</th><th scope="col">Status</th><th scope="col">Category</th><th scope="col">Due</th><th scope="col"><span className="sr-only">Actions</span></th></tr></thead>
            <tbody>
              {(a.sub_actions ?? []).length === 0 && (
                <tr><td colSpan={7} className="text-center text-sm px-3 py-4" style={{ color: 'var(--muted)' }}>No CAPAs yet.</td></tr>
              )}
              {(a.sub_actions ?? []).map((s: any) => {
                const nDocs = (a.documents ?? []).filter((d: any) => Number(d.sub_action_id) === Number(s.id)).length;
                return (
                <Fragment key={s.id}>
                <tr className="border-t">
                  <td className="py-2 pr-2 font-mono text-xs">{s.sub_reference_number}</td>
                  <td>{s.title}{nDocs > 0 && <a href="#attachments" className="badge bg-slate-100 ml-1" title={`${nDocs} attachment${nDocs === 1 ? '' : 's'}`}>📎 {nDocs}</a>}</td>
                  <td>{s.assignee_name ?? '—'}</td>
                  <td><StatusBadge status={s.status} />{s.escalation_level > 0 && <span className="badge bg-red-100 text-red-800 ml-1">L{s.escalation_level}</span>}</td>
                  <td>{s.category_name ?? a.category_name ?? '—'}</td>
                  <td className="whitespace-nowrap">{formatDate(s.target_date)}</td>
                  <td>
                    <span className="flex gap-1">
                      <CopyButton url={typeof window !== 'undefined' ? `${window.location.origin}/actions/${a.id}` : `/actions/${a.id}`} label={s.sub_reference_number} />
                      {(user?.role !== 'employee' || s.assigned_employee_id === user?.id) && (
                        <button type="button" className="btn-plain" aria-label={s.status === 'Completed' ? `Reopen ${s.title}` : `Mark ${s.title} done`} title={s.status === 'Completed' ? 'Reopen' : 'Mark done'} onClick={async () => {
                          const ns = s.status === 'Completed' ? 'In Progress' : 'Completed';
                          try {
                            await api.patch(`/api/v1/sub-actions/${s.id}`, { status: ns });
                            q.refetch();
                          } catch (e: any) { notify(e.message, { tone: 'error' }); }
                        }}><IconToggle /></button>
                      )}
                      {canEditParent && (
                        <button type="button" className="btn-plain" aria-label={`Edit ${s.title}`} title="Edit" onClick={() => setEditingSub(editingSub === s.id ? null : s.id)}><IconEdit /></button>
                      )}
                      {canEditParent && (
                        <button type="button" className="btn-plain" aria-label={`Delete ${s.title}`} title="Delete CAPA" onClick={async () => {
                          if (!await confirmDialog(`Delete CAPA ${s.sub_reference_number}?`, { title: 'Delete CAPA', confirmLabel: 'Delete' })) return;
                          try { await api.del(`/api/v1/sub-actions/${s.id}`); notify('CAPA deleted.', { tone: 'success' }); q.refetch(); }
                          catch (e: any) { notify(e.message, { tone: 'error' }); }
                        }}><IconTrash /></button>
                      )}
                    </span>
                  </td>
                </tr>
                {editingSub === s.id && (
                  <tr>
                    <td colSpan={7} className="!px-0 pb-2">
                      <EditSub sub={s} actionId={a.id} parentTarget={a.target_date ? String(a.target_date).slice(0, 10) : ''} onDone={() => { setEditingSub(null); q.refetch(); }} onCancel={() => setEditingSub(null)} />
                    </td>
                  </tr>
                )}
                </Fragment>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>

      {showUpdate && (
      <div className="card space-y-2">
        <h2 className="font-medium">Update deviation</h2>
        <div className="grid md:grid-cols-3 gap-2">
          <FloatSelect label="Status" id="ad-status" value={status} onChange={(e) => setStatus(e.target.value)}>
            {['Open', 'Assigned', 'In Progress', 'On Hold', 'Completed', 'Overdue', 'Cancelled'].map(s => <option key={s}>{s}</option>)}
          </FloatSelect>
          <EmployeePicker value={assignee} onChange={setAssignee} />
          <FloatSelect label="Priority" id="ad-priority" value={prio} onChange={(e) => setPrio(e.target.value)}>
            <option>Low</option><option>Medium</option><option>High</option><option>Critical</option>
          </FloatSelect>
          {canEditParent && <CategoryPicker type="action" value={category} onChange={setCategory} />}
          {canEditParent && <DepartmentPicker value={dept} onChange={setDept} />}
          {canEditParent && <DatePicker label="Target date" id="ad-target" required error={targetErr} value={target} onChange={(val) => { setTarget(val); setTargetErr(''); }} min={new Date().toISOString().slice(0, 10)} />}
          {canEditParent && <FloatInput label="Remind days before target" id="ad-remind" type="number" min={0} max={90} title="Remind employee N days before target" error={remindErr} value={remind} onChange={(e) => setRemind(e.target.value)} />}
        </div>
        {canEditParent && <FloatArea label="Description" id="ad-desc" rows={2} value={descr} onChange={(e) => setDescr(e.target.value)} />}
        <div className="grid md:grid-cols-3 gap-2">
          <div className="md:col-span-2"><FloatArea label="Remarks" id="ad-remarks" rows={2} value={remarks} onChange={(e) => setRemarks(e.target.value)} /></div>
          <button className="btn-primary btn-tall" onClick={async () => {
            if (!assignee && status !== 'Open' && status !== 'Cancelled') {
              notify('Select an assignee first — a deviation cannot leave Open without an owner.', { tone: 'error' });
              return;
            }
            if (canEditParent) {
              const tErr = v.todayOrFuture(target, 'Target date');
              setTargetErr(tErr ?? '');
              if (tErr) return;
            }
            if (canEditParent && remind !== '' && (!Number.isInteger(Number(remind)) || Number(remind) < 0 || Number(remind) > 90)) {
              setRemindErr('Remind days must be a whole number 0–90');
              return;
            }
            setRemindErr('');
            try {
              await api.post(`/api/v1/actions/${a.id}/status`, {
                status, remarks,
                ...(assignee !== (a.assigned_employee_id ?? null) ? { assigned_employee_id: assignee } : {}),
              });
              if (canEditParent) {
                const patch: Record<string, any> = {};
                if ((category ?? null) !== (a.category_id ?? null)) patch.category_id = category;
                if ((dept ?? null) !== (a.department_id ?? null)) patch.department_id = dept;
                if (target !== String(a.target_date ?? '').slice(0, 10)) patch.target_date = target;
                if (prio !== (a.priority ?? 'Low')) patch.priority = prio;
                if (descr !== (a.description ?? '')) patch.description = descr || null;
                const remindVal = remind === '' ? null : Number(remind);
                if (remindVal !== (a.reminder_lead_days ?? null)) patch.reminder_lead_days = remindVal;
                if (Object.keys(patch).length) await api.patch(`/api/v1/actions/${a.id}`, patch);
              }
              notify('Deviation updated.', { tone: 'success' });
              setShowUpdate(false);
              q.refetch();
            } catch (e: any) { notify(e.message, { tone: 'error' }); }
          }}>Save</button>
        </div>
      </div>
      )}

      <div className="card space-y-3">
        <h2 className="font-medium">Comments</h2>
        <CommentThread comments={a.comments ?? []} />
        <CommentBox onSubmit={async (body) => {
          try { await api.post(`/api/v1/actions/${a.id}/comments`, { body }); q.refetch(); }
          catch (e: any) { notify(e.message, { tone: 'error' }); }
        }} />
      </div>

      <div className="card space-y-2" id="attachments">
        <h2 className="font-medium">Attachments <span className="text-xs font-normal text-slate-400">files + Drive links, for the deviation or a CAPA</span></h2>
        {(a.documents ?? []).map((d: any) => (
          <AttachmentRow key={d.id} d={d} onChanged={() => q.refetch()} badge={
            d.sub_action_id
              ? <span className="badge bg-purple-100 text-purple-800">{(a.sub_actions ?? []).find((s: any) => Number(s.id) === Number(d.sub_action_id))?.sub_reference_number ?? 'CAPA'}</span>
              : <span className="badge bg-slate-100">Deviation</span>
          } />
        ))}
        {(a.documents ?? []).length === 0 && <p className="text-sm text-slate-500">No attachments yet.</p>}
        <UploadBox actionId={a.id} subs={a.sub_actions ?? []} onDone={() => q.refetch()} />
      </div>
    </div>
  );
}

function EditSub({ sub, actionId, parentTarget, onDone, onCancel }: { sub: any; actionId: number; parentTarget: string; onDone: () => void; onCancel: () => void }) {
  const { notify } = useDialog();
  const [t, setT] = useState(sub.title ?? '');
  const [desc, setDesc] = useState(sub.description ?? '');
  const [who, setWho] = useState<number | null>(sub.assigned_employee_id ?? null);
  const [due, setDue] = useState(sub.target_date ? String(sub.target_date).slice(0, 10) : '');
  const [prio, setPrio] = useState(sub.priority ?? 'Low');
  const [cat, setCat] = useState<number | null>(sub.category_id ?? null);
  const [errs, setErrs] = useState<Errors>({});
  const [busy, setBusy] = useState(false);
  return (
    <div className="bg-slate-50 rounded-lg p-3 space-y-2">
      <div className="grid md:grid-cols-3 gap-2">
        <div className="md:col-span-2"><FloatInput label="CAPA title" id={`esub-t-${sub.id}`} error={errs.title} value={t} onChange={(e) => setT(e.target.value)} /></div>
        <DatePicker label="Target date" id={`esub-d-${sub.id}`} required error={errs.target_date} value={due} onChange={setDue} min={new Date().toISOString().slice(0, 10)} />
        <div className="md:col-span-3"><FloatArea label="Description / requirement" id={`esub-desc-${sub.id}`} rows={2} value={desc} onChange={(e) => setDesc(e.target.value)} /></div>
        <EmployeePicker value={who} onChange={setWho} />
        <FloatSelect label="Priority" id={`esub-p-${sub.id}`} value={prio} onChange={(e) => setPrio(e.target.value)}>
          <option>Low</option><option>Medium</option><option>High</option><option>Critical</option>
        </FloatSelect>
        <CategoryPicker type="action" value={cat} onChange={setCat} />
      </div>
      {(errs as Errors).window && <p role="alert" className="field-error">{(errs as Errors).window}</p>}
      <div className="flex gap-2">
        <button className="btn-primary btn-sm" disabled={busy} onClick={async () => {
          const fe = validate({
            title: () => v.minLen(t, 2, 'CAPA title'),
            target_date: () => v.todayOrFuture(due, 'Target date'),
            window: () => (parentTarget && due > parentTarget ? 'CAPA target cannot be beyond the deviation due date' : null),
          });
          setErrs(fe);
          if (Object.keys(fe).length) return;
          if (!who && sub.status !== 'Open' && sub.status !== 'Cancelled') {
            notify('Select an assignee first — a CAPA cannot leave Open without an owner.', { tone: 'error' });
            return;
          }
          setBusy(true);
          try {
            await api.patch(`/api/v1/sub-actions/${sub.id}`, {
              title: t.trim(), description: desc.trim() || null,
              assigned_employee_id: who, target_date: due, priority: prio, category_id: cat,
            });
            notify('CAPA updated.', { tone: 'success' });
            onDone();
          } catch (e: any) { notify(e.message, { tone: 'error' }); }
          finally { setBusy(false); }
        }}>{busy ? 'Saving…' : 'Save CAPA'}</button>
        <button className="btn-ghost btn-sm" onClick={onCancel}>Cancel</button>
      </div>
    </div>
  );
}

function AddSub({ actionId, parentTarget, onDone }: { actionId: number; parentTarget: string; onDone: () => void }) {
  const { notify } = useDialog();
  const [t, setT] = useState('');
  const [desc, setDesc] = useState('');
  const [who, setWho] = useState<number | null>(null);
  const [due, setDue] = useState('');
  const [prio, setPrio] = useState('Low');
  const [cat, setCat] = useState<number | null>(null);
  const [errs, setErrs] = useState<Errors>({});
  const [busy, setBusy] = useState(false);
  const staged = useStagedAttachments();
  return (
    <div className="mt-4 space-y-2 border rounded-xl p-3">
      <div className="grid md:grid-cols-3 gap-2">
        <div className="md:col-span-2"><FloatInput label="New CAPA title" id={`sub-t-${actionId}`} error={errs.title} value={t} onChange={(e) => setT(e.target.value)} /></div>
        <DatePicker label="Target date" id={`sub-d-${actionId}`} required error={errs.target_date} value={due} onChange={setDue} min={new Date().toISOString().slice(0, 10)} />
        <div className="md:col-span-3"><FloatArea label="Description / requirement" id={`sub-desc-${actionId}`} rows={2} value={desc} onChange={(e) => setDesc(e.target.value)} /></div>
        <EmployeePicker value={who} onChange={setWho} />
        <FloatSelect label="Priority" id={`sub-p-${actionId}`} value={prio} onChange={(e) => setPrio(e.target.value)}>
          <option>Low</option><option>Medium</option><option>High</option><option>Critical</option>
        </FloatSelect>
        <CategoryPicker type="action" value={cat} onChange={setCat} />
      </div>
      {(errs.window || errs.assignee) && <p role="alert" className="field-error">{errs.window ?? errs.assignee}</p>}
      <div className="flex gap-2">
        <button className="btn-ghost text-sm btn-tall" disabled={busy} onClick={async () => {
          const fe = validate({
            title: () => v.minLen(t, 2, 'CAPA title'),
            target_date: () => v.todayOrFuture(due, 'Target date'),
            window: () => (parentTarget && due > parentTarget ? 'CAPA target cannot be beyond the deviation due date' : null),
            assignee: () => (!who ? 'Assignee is required' : null),
          });
          setErrs(fe);
          if (Object.keys(fe).length) return;
          const linkErrs = linkRowErrors(staged.links);
          if (linkErrs.length) { notify(linkErrs.join('\n'), { tone: 'error' }); return; }
          setBusy(true);
          try {
            const created: any = await api.post(`/api/v1/actions/${actionId}/sub-actions`, {
              title: t.trim(), description: desc.trim() || null, target_date: due, assigned_employee_id: who,
              priority: prio, category_id: cat,
            });
            const failed = await uploadStaged({ sub_action_id: created.id }, staged.files, staged.complete);
            setT(''); setDesc(''); setWho(null); setDue(''); setPrio('Low'); setCat(null); staged.clear(); onDone();
            if (failed.length) notify(`CAPA created, but some attachments failed:\n${failed.join('\n')}`, { title: 'Created with warnings', tone: 'error' });
            else notify(`CAPA created${staged.count ? ` with ${staged.count} attachment${staged.count === 1 ? '' : 's'}` : ''}.`, { tone: 'success' });
          } catch (e: any) { notify(e.message, { tone: 'error' }); }
          finally { setBusy(false); }
        }}>{busy ? 'Creating…' : 'Create CAPA'}</button>
      </div>
      {(errs as Errors).window && <p role="alert" className="field-error">{(errs as Errors).window}</p>}
      <AttachmentComposer idPrefix={`sub-${actionId}`} files={staged.files} setFiles={staged.setFiles} links={staged.links} setLinks={staged.setLinks} />
    </div>
  );
}

function UploadBox({ actionId, subs, onDone }: { actionId: number; subs: any[]; onDone: () => void }) {
  const { notify } = useDialog();
  const [capa, setCapa] = useState('');
  const [busy, setBusy] = useState(false);
  const staged = useStagedAttachments();
  const attach = async () => {
    if (!staged.count) { notify('Choose files or fill a link row first.', { tone: 'error' }); return; }
    const linkErrs = linkRowErrors(staged.links);
    if (linkErrs.length) { notify(linkErrs.join('\n'), { tone: 'error' }); return; }
    setBusy(true);
    try {
      const failed = await uploadStaged(
        capa ? { sub_action_id: Number(capa) } : { action_id: actionId },
        staged.files, staged.complete);
      staged.clear(); onDone();
      if (failed.length) notify(failed.join('\n'), { title: 'Some attachments failed', tone: 'error' });
      else notify('Attachments added.', { tone: 'success' });
    } finally { setBusy(false); }
  };
  return (
    <div className="border-t pt-2 space-y-2">
      <div className="max-w-72">
        <FloatSelect label="Attach to" id={`att-capa-${actionId}`} value={capa} onChange={(e) => setCapa(e.target.value)}>
          <option value="">Deviation (general)</option>
          {subs.map((s: any) => <option key={s.id} value={s.id}>{s.sub_reference_number} — {cap(s.title)}</option>)}
        </FloatSelect>
      </div>
      <AttachmentComposer idPrefix={`att-${actionId}`} files={staged.files} setFiles={staged.setFiles} links={staged.links} setLinks={staged.setLinks} />
      <button type="button" className="btn-primary text-sm btn-tall" disabled={busy || !staged.count} onClick={attach}>
        {busy ? 'Attaching…' : `Attach${staged.count ? ` (${staged.count})` : ''}`}
      </button>
    </div>
  );
}
