// ===== Shared components =====
const { useState, useEffect, useRef, useMemo, useCallback } = React;
const Icon = window.AdminIcon;
const D = window.AdminData;

function Modal({ title, children, onClose, footer, size }) {
  useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, [onClose]);
  return (
    <div className="modal-backdrop" onClick={(e) => { if (e.target === e.currentTarget) onClose(); }}>
      <div className={'modal' + (size === 'lg' ? ' lg' : '')}>
        <div className="modal-header">
          <h3 className="modal-title">{title}</h3>
          <button className="modal-close" onClick={onClose}><Icon name="close" size={18}/></button>
        </div>
        <div className="modal-body">{children}</div>
        {footer && <div className="modal-footer">{footer}</div>}
      </div>
    </div>
  );
}

function Toggle({ value, onChange }) {
  return <div className={'toggle' + (value ? ' on' : '')} onClick={() => onChange(!value)}/>;
}

function StatusPill({ status, isUnconfirmed }) {
  // 過去日付 confirmed → 「未確認」表示（実体は status='confirmed' のまま）
  if (isUnconfirmed) {
    return <span className="pill" style={{background:'#fdf0d6', color:'#a87320', fontWeight:700}}>未確認</span>;
  }
  const labels = { confirmed: '確定', done: '来院済', cancelled: 'キャンセル', no_show: '来院なし' };
  const styleNoShow = status === 'no_show' ? { background:'#f3e2dc', color:'#8a3a1f', fontWeight:700 } : null;
  if (styleNoShow) return <span className="pill" style={styleNoShow}>{labels[status]}</span>;
  return <span className={'pill ' + status}>{labels[status] || status}</span>;
}

// 過去日付かつ confirmed の予約は「未確認」状態と判定
function isBookingUnconfirmed(booking) {
  if (!booking || booking.status !== 'confirmed') return false;
  if (!booking.date) return false;
  const today = new Date();
  today.setHours(0, 0, 0, 0);
  const [y, m, d] = booking.date.split('-').map(Number);
  const bDate = new Date(y, m - 1, d);
  return bDate < today;
}

function SourcePill({ source }) {
  if (source === 'ai') return <span className="pill" style={{background:'#e8f9ed', color:'#06a045', fontWeight:700}}>AI相談</span>;
  if (source === 'lp') return <span className="pill new">LP予約</span>;
  return <span className="text-muted" style={{fontSize:11}}>店舗入力</span>;
}

function EmptyState({ icon, title, sub }) {
  return (
    <div className="empty">
      {icon && <div className="empty-icon"><Icon name={icon} size={48}/></div>}
      <div className="empty-title">{title}</div>
      {sub && <div className="empty-sub">{sub}</div>}
    </div>
  );
}

// ===== 検索可能な顧客選択コンボボックス =====
// 入力中はリアルタイム絞り込み（漢字・かな・電話下4桁・カルテNo）
function CustomerCombobox({ customers, value, onChange, currentName }) {
  const [query, setQuery] = useState('');
  const [open, setOpen] = useState(false);
  const [activeIdx, setActiveIdx] = useState(0);
  const inputRef = useRef(null);
  const listRef = useRef(null);

  const selected = (customers || []).find(c => c.id === value);
  const displayValue = selected
    ? `${selected.name}（${String(selected.tel || selected.phone || '').replace(/[^0-9]/g, '').slice(-4) || '----'}・${selected.visits || 0}回）`
    : '';

  // 検索ロジック（顧客リストと同じ）
  const matches = useMemo(() => {
    const hiraToKata = (s) => String(s || '').replace(/[ぁ-ゖ]/g, ch => String.fromCharCode(ch.charCodeAt(0) + 0x60));
    // 全角/半角スペース・記号を除去して比較できるように
    const stripSep = (s) => String(s || '').replace(/[\s　・\-_,.]+/g, '');
    const digits = (s) => String(s || '').replace(/[^0-9]/g, '');
    const kRaw = (query || '').toLowerCase().trim();
    const k = stripSep(kRaw);
    const kKata = stripSep(hiraToKata(kRaw));
    const kDigits = digits(query || '');
    const arr = (customers || []).filter(c => {
      if (!kRaw) return true;
      if (stripSep((c.name || '').toLowerCase()).includes(k)) return true;
      if (stripSep(hiraToKata((c.kana || '').toLowerCase())).includes(kKata)) return true;
      if (String(c.chartNo || '').toLowerCase().includes(kRaw)) return true;
      if (kDigits.length >= 2) {
        const p1 = digits(c.tel || c.phone);
        const p2 = digits(c.phone2);
        if (p1.includes(kDigits) || p2.includes(kDigits)) return true;
      }
      return false;
    });
    // カナ順
    arr.sort((a, b) => String(a.kana || a.name || '').localeCompare(String(b.kana || b.name || '')));
    return arr.slice(0, 50); // パフォーマンス：最大50件
  }, [customers, query]);

  useEffect(() => { setActiveIdx(0); }, [query]);

  const onKeyDown = (e) => {
    if (!open) return;
    if (e.key === 'ArrowDown') { e.preventDefault(); setActiveIdx(i => Math.min(i + 1, matches.length - 1)); }
    else if (e.key === 'ArrowUp') { e.preventDefault(); setActiveIdx(i => Math.max(i - 1, 0)); }
    else if (e.key === 'Enter') {
      e.preventDefault();
      const c = matches[activeIdx];
      if (c) { onChange(c.id); setQuery(''); setOpen(false); inputRef.current?.blur(); }
    }
    else if (e.key === 'Escape') { setOpen(false); inputRef.current?.blur(); }
  };

  const select = (c) => {
    onChange(c.id);
    setQuery('');
    setOpen(false);
    inputRef.current?.blur();
  };

  const clear = () => {
    onChange('');
    setQuery('');
    inputRef.current?.focus();
    setOpen(true);
  };

  return (
    <div style={{position:'relative'}}>
      {/* 選択済み顧客の表示 */}
      {value && !open ? (
        <div style={{
          display:'flex', alignItems:'center', gap:8,
          padding:'8px 12px', border:'1px solid var(--line)', borderRadius:8,
          background:'var(--green-50)', cursor:'pointer'
        }} onClick={() => { setOpen(true); setQuery(''); setTimeout(() => inputRef.current?.focus(), 0); }}>
          <span style={{flex:1, fontSize:13.5}}>{displayValue}</span>
          <button type="button" onClick={(e) => { e.stopPropagation(); clear(); }}
            style={{background:'transparent', border:0, cursor:'pointer', color:'var(--ink-mute)', fontSize:14}}
            title="選択解除">×</button>
          <button type="button" onClick={() => { setOpen(true); setQuery(''); setTimeout(() => inputRef.current?.focus(), 0); }}
            style={{background:'transparent', border:0, cursor:'pointer', color:'var(--ink-mute)', fontSize:12}}
            title="変更">変更</button>
        </div>
      ) : (
        <input
          ref={inputRef}
          type="text"
          className="input"
          placeholder={currentName ? `「${currentName}」を入力中…` : '🔍 ここに名前を入力（例：中村、なかむら、ミドリ、電話下4桁、カルテNo）'}
          value={query}
          onChange={e => { setQuery(e.target.value); setOpen(true); }}
          onFocus={() => setOpen(true)}
          onBlur={() => setTimeout(() => setOpen(false), 200)}
          onKeyDown={onKeyDown}
          autoComplete="off"
          style={{fontWeight: 500}}
        />
      )}

      {/* 候補リスト */}
      {open && (
        <div ref={listRef} style={{
          position:'absolute', top:'100%', left:0, right:0, zIndex:100,
          marginTop:2, maxHeight:480, overflowY:'auto',
          background:'#fff', border:'1px solid var(--line)', borderRadius:8,
          boxShadow:'0 4px 16px rgba(0,0,0,0.08)',
        }}>
          {matches.length === 0 ? (
            <div style={{padding:'10px 14px', fontSize:12.5, color:'var(--ink-mute)'}}>
              該当する顧客がいません。下欄に新規顧客を入力してください。
            </div>
          ) : (
            <>
              {!query ? (
                <div style={{padding:'8px 12px', fontSize:11, color:'#a87320', background:'#fdf0d6', borderBottom:'1px solid var(--line)', fontWeight:600, position:'sticky', top:0}}>
                  ⚠️ 全{(customers || []).length}名のうち最初の50名のみ表示中。<span style={{color:'#a40'}}>名前を入力してください</span>（中村さん等は検索しないと出ません）
                </div>
              ) : (
                <div style={{padding:'6px 12px', fontSize:11, color:'var(--ink-mute)', background:'var(--green-50)', borderBottom:'1px solid var(--line)', position:'sticky', top:0}}>
                  「{query}」で <strong style={{color:'var(--green-900)'}}>{matches.length}名</strong> ヒット（カナ順）{matches.length > 8 ? ' ↓ スクロールできます' : ''}
                </div>
              )}
              {matches.map((c, i) => {
                const last4 = String(c.tel || c.phone || '').replace(/[^0-9]/g, '').slice(-4) || '----';
                const v = c.visits || 0;
                const visitLabel = v === 0 ? '初回' : `${v}回`;
                return (
                  <div key={c.id}
                    onMouseDown={(e) => { e.preventDefault(); select(c); }}
                    onMouseEnter={() => setActiveIdx(i)}
                    style={{
                      padding:'8px 12px', fontSize:13, cursor:'pointer',
                      background: i === activeIdx ? 'var(--green-50)' : 'transparent',
                      borderLeft: i === activeIdx ? '3px solid var(--green-700)' : '3px solid transparent',
                    }}>
                    <div style={{fontWeight:600}}>
                      <span style={{color:'var(--ink-mute)', fontSize:11, marginRight:6}}>#{c.chartNo || '?'}</span>
                      {c.name}
                    </div>
                    <div style={{fontSize:11, color:'var(--ink-mute)', marginTop:1}}>
                      {[c.kana, last4, visitLabel].filter(Boolean).join(' ・ ')}
                    </div>
                  </div>
                );
              })}
            </>
          )}
        </div>
      )}
    </div>
  );
}

// ===== Booking detail / edit modal =====
function BookingModal({ booking, onClose, onSave, onDelete, customers, menus, settings, isNew, defaultDate, defaultTime, prefilledCustomer, prefilledMenu, onCloneForNext, onOpenCustomer }) {
  const [form, setForm] = useState(() => {
    if (booking) return { ...booking };
    const base = {
      id: 'admin-' + Date.now() + '-' + Math.random().toString(36).slice(2,7),
      createdAt: new Date().toISOString(),
      source: 'admin',
      status: 'confirmed',
      menuId: menus[0]?.id || '',
      menu: menus[0]?.name || '',
      duration: menus[0]?.duration || 60,
      price: menus[0]?.price || 0,
      date: defaultDate || D.isoDate(new Date()),
      time: defaultTime || '10:00',
      customerId: '',
      name: '',
      tel: '',
      email: '',
      note: '',
      staffId: settings.staff[0]?.id || ''
    };
    let result = base;
    // 顧客プリフィル（顧客リストの「予約を取る」or 予約モーダルの「次回予約を取る」から）
    if (prefilledCustomer) {
      result = {
        ...result,
        customerId: prefilledCustomer.customerId || prefilledCustomer.id || '',
        name: prefilledCustomer.name || '',
        kana: prefilledCustomer.kana || '',
        tel: prefilledCustomer.tel || prefilledCustomer.phone || '',
        email: prefilledCustomer.email || '',
      };
    }
    // メニュープリフィル（前回と同じコース）— menuId / menu名どちらでもマッチさせる
    if (prefilledMenu && (prefilledMenu.menuId || prefilledMenu.menu)) {
      // 現在のmenus配列からマッチするメニューを引き当て（最新の duration/price を反映）
      const m = (menus || []).find(x =>
        (prefilledMenu.menuId && x.id === prefilledMenu.menuId) ||
        (prefilledMenu.menu && x.name === prefilledMenu.menu)
      );
      result = {
        ...result,
        menuId: m?.id || prefilledMenu.menuId || '',
        menu: m?.name || prefilledMenu.menu || '',
        duration: m?.duration || prefilledMenu.duration || 60,
        price: m?.price || prefilledMenu.price || 0,
      };
    }
    return result;
  });
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));

  const onMenuChange = (id) => {
    const m = menus.find(x => x.id === id);
    if (m) setForm(f => ({ ...f, menuId: m.id, menu: m.name, duration: m.duration, price: m.price }));
  };
  const onCustomerSelect = (id) => {
    const c = customers.find(x => x.id === id);
    if (!c) {
      setForm(f => ({ ...f, customerId: '', useTicketId: '' }));
      return;
    }
    // 🛡 防御1：有効な回数券・月額プランがあれば自動で選択（カウント忘れ防止）
    let autoTicketId = '';
    let autoPrice = null;
    try {
      const todayISO = (form && form.date) || D.isoDate(new Date());
      if (window.TicketHelpers) {
        const tickets = window.TicketHelpers.getActiveTickets(c, todayISO);
        if (tickets.length > 0) {
          // 最も古い券（残数が少ない方）から消費していく
          const oldest = tickets.slice().sort((a, b) => {
            const ad = a.purchasedAt || a.contractedAt || '';
            const bd = b.purchasedAt || b.contractedAt || '';
            return ad.localeCompare(bd);
          })[0];
          autoTicketId = oldest.id;
          autoPrice = oldest.pricePerVisit;
        }
      }
    } catch (e) {}
    setForm(f => ({
      ...f,
      customerId: c.id, name: c.name, kana: c.kana || '', tel: c.tel, email: c.email,
      useTicketId: autoTicketId,
      ...(autoPrice !== null ? { price: autoPrice } : {}),
    }));
  };

  // 選択中の顧客のアクティブなチケット
  const selectedCustomer = (customers || []).find(c => c.id === form.customerId);
  const activeTickets = selectedCustomer && window.TicketHelpers
    ? window.TicketHelpers.getActiveTickets(selectedCustomer, form.date || D.isoDate(new Date()))
    : [];

  // 📧 カウンセリングシート催促メール送信（予約から）
  const sendCounselingEmailFromBooking = async () => {
    const email = form.email || (selectedCustomer && selectedCustomer.email) || '';
    const name = form.name || (selectedCustomer && selectedCustomer.name) || '';
    if (!email) {
      alert(`${name || 'このお客様'}のメールアドレスが登録されていません。`);
      return;
    }
    const ok = confirm(`${name} 様（${email}）にカウンセリングシート記入のお願いメールを送信します。\n\n送信元：フィジオサロンキムラ\nよろしいですか？`);
    if (!ok) return;
    try {
      const res = await D.apiFetch(`${D.API_BASE}/api/send-counseling-reminder`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ name, email }),
      });
      if (!res.ok) {
        const errData = await res.json().catch(() => ({}));
        alert(`❌ メール送信失敗: ${errData.error || res.status}`);
        return;
      }
      alert(`✅ ${name} 様にカウンセリングシート記入のお願いメールを送信しました。`);
    } catch (e) {
      alert(`❌ 通信エラー: ${e.message}`);
    }
  };
  const hasEmailForReminder = !!(form.email || (selectedCustomer && selectedCustomer.email));

  const onUseTicketChange = (ticketId) => {
    if (!ticketId) {
      setForm(f => ({ ...f, useTicketId: '' }));
      return;
    }
    const t = activeTickets.find(x => x.id === ticketId);
    if (!t) return;
    setForm(f => ({ ...f, useTicketId: ticketId, price: t.pricePerVisit || f.price }));
  };

  const TIMES = useMemo(() => {
    const arr = [];
    for (let h = 9; h < 20; h++) for (let m = 0; m < 60; m += 15) arr.push(h + ':' + D.pad(m));
    return arr;
  }, []);

  // 保存中フラグ（二重クリック防止）
  const [isSaving, setIsSaving] = useState(false);
  const handleSave = async () => {
    if (isSaving) return; // 連打ガード
    if (!form.name.trim()) { alert('お名前を入力してください'); return; }
    // 🛡 防御3：チケット保有顧客で「使わない」を選んでいる場合、最終確認
    if (activeTickets.length > 0 && !form.useTicketId) {
      const t = activeTickets[0];
      const def = window.TicketHelpers?.TICKET_DEFS?.[t.type];
      const remaining = window.TicketHelpers?.ticketRemainingLabel?.(t, form.date) || '';
      const ok = confirm(
        `⚠️ ${form.name} 様は有効な${def?.label || t.type}（${remaining}）をお持ちです。\n\n` +
        `「回数券を使わない」で保存します。\n` +
        `本当によろしいですか？\n\n` +
        `通常はキャンセルして「回数券を使う」に変更することを推奨します。\n` +
        `（このまま保存すると、残数が減らず実質二重請求になる可能性があります）`
      );
      if (!ok) return; // キャンセルしたら戻る
    }
    setIsSaving(true);
    try { await onSave(form); }
    finally { setTimeout(() => setIsSaving(false), 800); } // 短時間ブロック後リセット
  };

  // 「未確認」状態（過去日付 + confirmed）の判定
  const unconfirmed = !isNew && isBookingUnconfirmed(form);
  const markDone = async () => {
    if (isSaving) return;
    setIsSaving(true);
    try { await onSave({ ...form, status: 'done' }); }
    finally { setTimeout(() => setIsSaving(false), 800); }
  };
  const markNoShow = async () => {
    if (isSaving) return;
    setIsSaving(true);
    try { await onSave({ ...form, status: 'no_show' }); }
    finally { setTimeout(() => setIsSaving(false), 800); }
  };

  // 「来院済」or「確定」の予約 → 同じお客様の次回予約をコピー作成
  // （複数予約を連続で取るケースに対応）
  const canCloneForNext = !isNew && (form.status === 'done' || form.status === 'confirmed') && onCloneForNext;
  const handleCloneForNext = () => {
    onCloneForNext({
      // 顧客情報
      customerId: form.customerId || '',
      name: form.name || '',
      kana: form.kana || '',
      tel: form.tel || '',
      email: form.email || '',
      // 前回と同じメニューをコピー
      menuId: form.menuId || '',
      menu: form.menu || '',
      duration: form.duration || 60,
      price: form.price || 0,
      // 前回と同じ時間帯をコピー（多くの方が同じ時間帯に予約するため）
      time: form.time || '',
    });
  };

  return (
    <Modal
      title={isNew ? '新しい予約を追加' : (unconfirmed ? '予約の確認（未確認）' : '予約の詳細')}
      onClose={onClose}
      size="lg"
      footer={
        <>
          {!isNew && <button className="btn danger left" onClick={() => onDelete(form.id)}><Icon name="trash" size={14}/> 予約を削除</button>}
          {!isNew && form.customerId && onOpenCustomer && (
            <button className="btn" onClick={() => onOpenCustomer(form.customerId)} title="この顧客のカルテを開いて編集（領収書・支払い方法・宿題メモなど）">
              <Icon name="user" size={14}/> 顧客カルテを開く
            </button>
          )}
          {!isNew && hasEmailForReminder && (
            <button className="btn" onClick={sendCounselingEmailFromBooking} title="このお客様にカウンセリングシート記入のお願いメールを送信">
              <Icon name="mail" size={14}/> 📧 メール催促
            </button>
          )}
          {canCloneForNext && (
            <button className="btn" onClick={handleCloneForNext}>
              <Icon name="plus" size={14}/> 次回予約を取る
            </button>
          )}
          <button className="btn" onClick={onClose} disabled={isSaving}>キャンセル</button>
          <button className="btn primary" onClick={handleSave} disabled={isSaving}>
            <Icon name="check" size={14}/> {isSaving ? '保存中…' : '保存'}
          </button>
        </>
      }
    >
      {unconfirmed && (
        <div style={{marginBottom:14, padding:'12px 14px', background:'#fdf0d6', borderLeft:'4px solid #d6651e', borderRadius:6}}>
          <div style={{fontWeight:700, color:'#a87320', marginBottom:6, fontSize:13.5}}>
            ⚠ 過去の予約です。来院されましたか？
          </div>
          <div style={{display:'flex', gap:8, flexWrap:'wrap'}}>
            <button className="btn primary" onClick={markDone} disabled={isSaving}>
              <Icon name="check" size={14}/> {isSaving ? '処理中…' : '来院済として確定'}
            </button>
            <button className="btn" onClick={markNoShow} disabled={isSaving} style={{background:'#f3e2dc', color:'#8a3a1f', borderColor:'#e0c0b3'}}>
              <Icon name="close" size={14}/> {isSaving ? '処理中…' : '来院なしとして記録'}
            </button>
            <span style={{fontSize:11.5, color:'var(--ink-mute)', alignSelf:'center', marginLeft:6}}>
              ※ 来院済にすると累計売上・来院回数に自動加算されます
            </span>
          </div>
        </div>
      )}

      <div className="booking-form-grid" style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:14}}>
        <div className="field" style={{margin:0}}>
          <label className="field-label">日付</label>
          <input type="date" className="input" value={form.date} onChange={e => set('date', e.target.value)}/>
        </div>
        <div className="field" style={{margin:0}}>
          <label className="field-label">時間</label>
          <select className="select" value={form.time} onChange={e => set('time', e.target.value)}>
            {TIMES.map(t => <option key={t} value={t}>{t}</option>)}
          </select>
        </div>
        <div className="field" style={{margin:0, gridColumn:'span 2'}}>
          <label className="field-label">メニュー</label>
          <select className="select" value={form.menuId} onChange={e => onMenuChange(e.target.value)}>
            {(menus || [])
              .filter(m => m.active !== false)
              .slice()
              .sort((a, b) => {
                const ao = a.sortOrder !== undefined ? a.sortOrder : (a.sort_order !== undefined ? a.sort_order : 999);
                const bo = b.sortOrder !== undefined ? b.sortOrder : (b.sort_order !== undefined ? b.sort_order : 999);
                return ao - bo;
              })
              .map(m => <option key={m.id} value={m.id}>{m.name}（{m.duration}分・¥{m.price.toLocaleString()}）</option>)}
            {/* 削除済みメニュー or 非アクティブの場合：現在の値を残す */}
            {form.menuId && !(menus || []).find(m => m.id === form.menuId && m.active !== false) && (
              <option value={form.menuId}>{form.menu || '（削除済メニュー）'}（{form.duration || 60}分・¥{(form.price || 0).toLocaleString()}）— 削除済</option>
            )}
          </select>
        </div>
        <div className="field" style={{margin:0, gridColumn:'span 2'}}>
          <label className="field-label">顧客</label>
          <CustomerCombobox
            customers={customers || []}
            value={form.customerId}
            onChange={onCustomerSelect}
            currentName={form.name}
          />
        </div>

        {/* 📝 次回宿題・特記事項（顧客に紐づくメモがあれば強調表示） */}
        {selectedCustomer && selectedCustomer.pendingMemo && (
          <div className="field" style={{margin:0, gridColumn:'span 2', padding:'12px 14px', background:'#fdf0d6', borderRadius:8, border:'2px solid #f0c890'}}>
            <div style={{fontSize:13, color:'#a87320', fontWeight:700, marginBottom:6}}>
              📝 次回宿題・特記事項（{selectedCustomer.name}様）
            </div>
            <div style={{fontSize:13, color:'#5a3d10', whiteSpace:'pre-wrap', lineHeight:1.7, fontWeight:500}}>
              {selectedCustomer.pendingMemo}
            </div>
            <div style={{fontSize:11, color:'#a87320', marginTop:6}}>
              💡 完了したら、顧客カルテの「次回宿題」欄から削除してください。
            </div>
          </div>
        )}

        {activeTickets.length > 0 && (
          <div className="field" style={{margin:0, gridColumn:'span 2'}}>
            <label className="field-label" style={{color: form.useTicketId ? 'var(--green-900)' : '#a40', fontWeight: form.useTicketId ? 600 : 700}}>
              {form.useTicketId ? '✓ 回数券を使う' : '⚠️ 回数券を使う'}
            </label>
            <select
              className="select"
              value={form.useTicketId || ''}
              onChange={e => onUseTicketChange(e.target.value)}
              style={{
                background: form.useTicketId ? '#e8f0e6' : '#fdf0d6',
                border: form.useTicketId ? '2px solid #4f8c4d' : '2px solid #d6651e',
                fontWeight: 600,
              }}
            >
              <option value="">⚠️ 使わない（通常料金で記録 — 残数は減りません）</option>
              {activeTickets.map(t => {
                const def = window.TicketHelpers.TICKET_DEFS[t.type];
                const remaining = window.TicketHelpers.ticketRemainingLabel(t, form.date);
                return (
                  <option key={t.id} value={t.id}>
                    ✓ {def?.label || t.type}（{remaining}） ・ 1回 ¥{(t.pricePerVisit || 0).toLocaleString()}
                  </option>
                );
              })}
            </select>
            {form.useTicketId ? (
              <div style={{fontSize:11.5, color:'var(--green-900)', marginTop:6, padding:'6px 10px', background:'#e8f0e6', borderRadius:4}}>
                ✓ 保存すると残数が1減ります。料金は按分額（1回 ¥{(form.price || 0).toLocaleString()}）で計上されます。
              </div>
            ) : (
              <div style={{fontSize:12, color:'#a40', marginTop:6, padding:'8px 10px', background:'#fdf0d6', borderRadius:4, border:'1px solid #f0c890', fontWeight:600}}>
                ⚠️ このお客様は<strong>有効な回数券</strong>をお持ちです。<br/>
                使用しないと<strong>残数が減らず、二重請求</strong>になる可能性があります。<br/>
                通常メニュー料金で記録する場合のみ「使わない」を選んでください。
              </div>
            )}
          </div>
        )}
        <div className="field" style={{margin:0}}>
          <label className="field-label">お名前</label>
          <input className="input" value={form.name} onChange={e => set('name', e.target.value)} placeholder="山田 太郎"/>
        </div>
        <div className="field" style={{margin:0}}>
          <label className="field-label">電話番号</label>
          <input className="input" value={form.tel} onChange={e => set('tel', e.target.value)} placeholder="090-1234-5678"/>
        </div>
        <div className="field" style={{margin:0}}>
          <label className="field-label">メール</label>
          <input className="input" value={form.email} onChange={e => set('email', e.target.value)}/>
        </div>
        <div className="field" style={{margin:0}}>
          <label className="field-label">担当スタッフ</label>
          <select className="select" value={form.staffId} onChange={e => set('staffId', e.target.value)}>
            {((settings && settings.staff) || []).filter(s => s.active !== false).map(s => <option key={s.id} value={s.id}>{s.name}</option>)}
          </select>
        </div>
        <div className="field" style={{margin:0}}>
          <label className="field-label">ステータス</label>
          <select className="select" value={form.status} onChange={e => set('status', e.target.value)}>
            <option value="confirmed">確定</option>
            <option value="done">来院済</option>
            <option value="no_show">来院なし</option>
            <option value="cancelled">キャンセル</option>
          </select>
        </div>
        <div className="field" style={{margin:0}}>
          <label className="field-label">流入元</label>
          <div style={{paddingTop:6}}><SourcePill source={form.source}/></div>
        </div>
        {form.aiConsultId && (
          <div className="field" style={{margin:0, gridColumn:'span 2'}}>
            <label className="field-label">AI相談ログ</label>
            <div style={{padding:'10px 12px', background:'#e8f9ed', border:'1px solid #c8edd4', borderRadius:8, display:'flex', alignItems:'center', gap:10}}>
              <span style={{fontSize:11, color:'#06a045', fontWeight:700, background:'#fff', padding:'3px 8px', borderRadius:4}}>AI相談から予約</span>
              <span style={{fontSize:12, color:'var(--ink-soft)', flex:1, overflow:'hidden', textOverflow:'ellipsis', whiteSpace:'nowrap'}}>
                {form.aiConsultSummary || '相談内容あり'}
              </span>
              <button type="button" className="btn" style={{padding:'5px 10px', fontSize:11.5}}
                onClick={() => {
                  // ページをaiconsultsに切り替え + 該当ログを開く
                  window.dispatchEvent(new CustomEvent('open-ai-consult', { detail: { id: form.aiConsultId } }));
                  onClose();
                }}>
                <Icon name="messages" size={12}/> 会話を見る
              </button>
            </div>
          </div>
        )}
        <div className="field" style={{margin:0, gridColumn:'span 2'}}>
          <label className="field-label">メモ・要望</label>
          <textarea className="textarea" value={form.note} onChange={e => set('note', e.target.value)} rows={2}/>
        </div>
        {/* 夫婦・グループ施術モード（バッファ無視） */}
        <div className="field" style={{margin:0, gridColumn:'span 2', padding:'10px 12px', background:'#f4f8f0', border:'1px solid #c8d8be', borderRadius:8}}>
          <label style={{display:'flex', alignItems:'flex-start', gap:8, cursor:'pointer', fontSize:13}}>
            <input
              type="checkbox"
              checked={!!form.allowConflict}
              onChange={e => set('allowConflict', e.target.checked)}
              style={{marginTop:2}}
            />
            <span>
              <strong>夫婦・グループ施術として登録</strong>
              <div className="sub" style={{fontSize:11.5, marginTop:2, color:'var(--ink-soft)'}}>
                通常は前後の予約と15分（片付け時間）空ける必要がありますが、ONにするとくっつけて登録できます。
              </div>
            </span>
          </label>
        </div>
      </div>
    </Modal>
  );
}

Object.assign(window, { Modal, Toggle, StatusPill, SourcePill, EmptyState, BookingModal, isBookingUnconfirmed });
