// ===== 休講ブロック（マトリクスUI：クリック1つで予約可否を切り替え） =====
function BlocksPage({ blocks, onSetBlocks, settings, bookings, onOpenBooking }) {
  const [weekStart, setWeekStart] = useState(() => D.startOfWeek(new Date()));
  const [conflictModal, setConflictModal] = useState(null); // { date, time, bookings }

  // 30分刻み: 9:00-19:00（最終受付19:00）
  const TIMES = useMemo(() => {
    const arr = [];
    for (let h = 9; h <= 19; h++) {
      arr.push(h + ':00');
      if (h < 19) arr.push(h + ':30');
    }
    return arr;
  }, []);

  const days = useMemo(() => {
    const arr = [];
    for (let i = 0; i < 7; i++) {
      const d = new Date(weekStart); d.setDate(d.getDate() + i);
      arr.push(d);
    }
    return arr;
  }, [weekStart]);

  const todayStart = useMemo(() => { const d = new Date(); d.setHours(0,0,0,0); return d; }, []);

  // ブロック判定: その日その時間がブロック対象か
  const slotMap = useMemo(() => {
    const m = new Map();
    blocks.forEach(b => {
      if (b.allDay) {
        m.set(b.date + '::ALL', b);
      } else {
        // 開始〜終了の30分刻みすべてに展開
        const start = D.timeToMin(b.from);
        const end = D.timeToMin(b.to);
        for (let t = start; t < end; t += 30) {
          m.set(b.date + '::' + D.minToTime(t), b);
        }
      }
    });
    return m;
  }, [blocks]);

  // 既存予約マップ: 日付::時刻 → 予約配列（その時刻に重なる予約）
  const bookingsByDateTime = useMemo(() => {
    const m = new Map();
    (bookings || []).forEach(b => {
      if (b.status === 'cancelled') return;
      const start = D.timeToMin(b.time);
      const dur = b.duration || 60;
      // この予約が占有する30分スロットすべてに登録
      for (let t = start; t < start + dur; t += 30) {
        const key = b.date + '::' + D.minToTime(t);
        if (!m.has(key)) m.set(key, []);
        m.get(key).push(b);
      }
    });
    return m;
  }, [bookings]);

  const getBookingsAt = (d, time) => bookingsByDateTime.get(D.isoDate(d) + '::' + time) || [];
  const getBookingsForDay = (d) => {
    const iso = D.isoDate(d);
    return (bookings || []).filter(b => b.date === iso && b.status !== 'cancelled');
  };

  const isClosedByHours = (d, time) => {
    const h = settings.hours[d.getDay()];
    if (!h.open) return true;
    // 祝日休み設定がONなら祝日も定休扱い
    if (settings.holidaysClosed !== false && D.isHoliday && D.isHoliday(D.isoDate(d))) return true;
    const m = D.timeToMin(time);
    return m < D.timeToMin(h.from) || m >= D.timeToMin(h.to);
  };

  const slotStatus = (d, time) => {
    if (d < todayStart) return 'past';
    const iso = D.isoDate(d);
    const hasBooking = getBookingsAt(d, time).length > 0;
    const isClosed = isClosedByHours(d, time);
    // 営業時間外でも予約が入っていれば「営業外予約」として表示（保護対象）
    if (isClosed) return hasBooking ? 'closed-booked' : 'closed';
    if (slotMap.get(iso + '::ALL')) return hasBooking ? 'blocked-booked' : 'blocked-day';
    if (slotMap.get(iso + '::' + time)) return hasBooking ? 'blocked-booked' : 'blocked';
    if (hasBooking) return 'booked';
    return 'open';
  };

  const toggleSlot = (d, time) => {
    const status = slotStatus(d, time);
    if (status === 'past' || status === 'closed') return;
    // 予約が入っているコマは休講にできない（営業時間外の予約も含む）
    if (status === 'booked' || status === 'closed-booked') {
      const conflicts = getBookingsAt(d, time);
      setConflictModal({ date: d, time, bookings: conflicts, mode: 'slot' });
      return;
    }
    if (status === 'blocked-booked') {
      // 予約があるコマのブロックを解除する操作（解除はOK）
      // → 通常の解除フローへフォールスルー
    }
    const iso = D.isoDate(d);
    if (status === 'blocked-day' || (status === 'blocked-booked' && slotMap.get(iso + '::ALL'))) {
      // 終日ブロックの解除
      onSetBlocks(blocks.filter(b => !(b.date === iso && b.allDay)));
      return;
    }
    if (status === 'blocked' || status === 'blocked-booked') {
      // この時間のピンポイントブロックを解除（時間範囲ブロックなら30分だけ抜く）
      const target = slotMap.get(iso + '::' + time);
      if (!target) return;
      if (target.from === time && D.timeToMin(target.to) === D.timeToMin(time) + 30) {
        onSetBlocks(blocks.filter(b => b.id !== target.id));
      } else {
        const segments = [];
        if (D.timeToMin(target.from) < D.timeToMin(time)) {
          segments.push({ ...target, id: 'bk-' + Date.now() + '-a', to: time });
        }
        const next = D.minToTime(D.timeToMin(time) + 30);
        if (D.timeToMin(next) < D.timeToMin(target.to)) {
          segments.push({ ...target, id: 'bk-' + Date.now() + '-b', from: next });
        }
        onSetBlocks([...blocks.filter(b => b.id !== target.id), ...segments]);
      }
      return;
    }
    // open → 30分ブロックを追加
    const next = D.minToTime(D.timeToMin(time) + 30);
    onSetBlocks([...blocks, {
      id: 'bk-' + Date.now() + Math.random().toString(36).slice(2,5),
      date: iso, allDay: false, from: time, to: next, reason: ''
    }]);
  };

  const toggleAllDay = (d) => {
    if (d < todayStart) return;
    const iso = D.isoDate(d);
    const hasAllDay = blocks.some(b => b.date === iso && b.allDay);
    if (hasAllDay) {
      // 解除：その日のブロックを全消去（部分ブロック含む）→ 予約可能に
      onSetBlocks(blocks.filter(b => b.date !== iso));
      return;
    }
    // 新規に終日休にしようとする場合、その日に予約があるかチェック
    const dayBookings = getBookingsForDay(d);
    if (dayBookings.length > 0) {
      setConflictModal({ date: d, time: null, bookings: dayBookings, mode: 'day' });
      return;
    }
    // その日の部分ブロックを全消去してから、終日休を1件だけ追加
    onSetBlocks([...blocks.filter(b => b.date !== iso), {
      id: 'bk-' + Date.now() + Math.random().toString(36).slice(2,5),
      date: iso, allDay: true, from: '9:00', to: '19:00', reason: ''
    }]);
  };

  const navPrev = () => { const d = new Date(weekStart); d.setDate(d.getDate()-7); if (d < todayStart) return; setWeekStart(d); };
  const navNext = () => { const d = new Date(weekStart); d.setDate(d.getDate()+7); setWeekStart(d); };
  const navToday = () => setWeekStart(D.startOfWeek(new Date()));

  const monthLabel = useMemo(() => {
    const last = days[6];
    const a = (weekStart.getMonth()+1) + '月';
    const b = (last.getMonth()+1) + '月';
    return weekStart.getFullYear() + '年 ' + (a === b ? a : a + '〜' + b);
  }, [weekStart, days]);

  // 統計
  const blockedSlotsCount = useMemo(() => {
    let count = 0;
    days.forEach(d => {
      TIMES.forEach(t => {
        if (slotStatus(d, t) === 'blocked' || slotStatus(d, t) === 'blocked-day') count++;
      });
    });
    return count;
  }, [days, TIMES, blocks, settings]);

  return (
    <div>
      <div className="block-howto">
        <div className="block-howto-icon"><Icon name="block" size={20}/></div>
        <div>
          <div className="block-howto-title">クリックひとつで予約可否を切り替え</div>
          <div className="block-howto-desc">空きセル（◎）をクリック→ <span style={{color:'#c33', fontWeight:700}}>×</span> に変わり予約不可に。もう一度クリックで解除されます。曜日見出しの「終日休」ボタンで1日まるごとブロックも可能です。</div>
        </div>
      </div>

      <div className="block-toolbar">
        <div className="cal-nav-group">
          <button className="cal-nav-btn" onClick={navPrev} disabled={(() => { const p = new Date(weekStart); p.setDate(p.getDate()-7); return p < todayStart; })()}><Icon name="chev-left" size={16}/></button>
          <div className="cal-current">{monthLabel}</div>
          <button className="cal-nav-btn" onClick={navNext}><Icon name="chev-right" size={16}/></button>
        </div>
        <button className="btn" onClick={navToday}>今週</button>
        <div style={{flex:1}}/>
        <div className="block-legend">
          <span className="b-leg ok">◎ 予約可</span>
          <span className="b-leg ng">× 予約不可</span>
          <span className="b-leg booked">🔒 予約済</span>
          <span className="b-leg cl">休 営業時間外</span>
        </div>
      </div>

      <div className="block-matrix-wrap">
        <table className="block-matrix">
          <thead>
            <tr className="bm-day-row">
              <th className="bm-corner"></th>
              {days.map((d, i) => {
                const isToday = D.isoDate(d) === D.isoDate(new Date());
                const isPast = d < todayStart;
                const isAllDay = !!blocks.find(b => b.date === D.isoDate(d) && b.allDay);
                const dayBookings = getBookingsForDay(d);
                const cls = d.getDay() === 0 ? 'sun' : d.getDay() === 6 ? 'sat' : '';
                const holidayName = D.getHolidayName ? D.getHolidayName(D.isoDate(d)) : null;
                return (
                  <th key={i} className={cls + (isToday ? ' today' : '') + (isPast ? ' past' : '')}>
                    <div className="bm-day-week">{D.WEEK_LABELS[d.getDay()]}</div>
                    <div className="bm-day-num">{d.getMonth()+1}/{d.getDate()}</div>
                    {holidayName && <div style={{fontSize:9, color:'var(--red)', fontWeight:700, marginTop:1}}>{holidayName}</div>}
                    {dayBookings.length > 0 && !isPast && (
                      <div className="bm-day-bookings">予約 {dayBookings.length}件</div>
                    )}
                    <button
                      className={'bm-allday' + (isAllDay ? ' active' : '') + (dayBookings.length > 0 && !isAllDay ? ' has-bookings' : '')}
                      onClick={() => toggleAllDay(d)}
                      disabled={isPast}
                      title={isAllDay ? '終日休を解除' : (dayBookings.length > 0 ? '予約あり：終日休にできません' : '終日休にする')}
                    >
                      {isAllDay ? '✓ 終日休' : '終日休'}
                    </button>
                  </th>
                );
              })}
            </tr>
          </thead>
          <tbody>
            {TIMES.map(t => {
              const isHour = t.endsWith(':00');
              return (
                <tr key={t}>
                  <td className={'bm-time' + (isHour ? ' hour' : '')}>{t}</td>
                  {days.map((d, i) => {
                    const status = slotStatus(d, t);
                    const isPast = status === 'past';
                    const isClosed = status === 'closed';
                    const isClosedBooked = status === 'closed-booked';
                    const isBlocked = status === 'blocked' || status === 'blocked-day';
                    const isBooked = status === 'booked';
                    const isBlockedBooked = status === 'blocked-booked';
                    const cellBookings = getBookingsAt(d, t);
                    return (
                      <td
                        key={i}
                        className={'bm-cell ' + status}
                        onClick={() => toggleSlot(d, t)}
                        title={(isBooked || isClosedBooked) ? cellBookings.map(b => b.time + ' ' + b.name + '様（' + b.menu + '）' + (isClosedBooked ? '【営業時間外】' : '')).join('\n') : ''}
                      >
                        <span className="bm-mark">
                          {isPast ? '・'
                            : isClosedBooked ? '⚠'
                            : isClosed ? '休'
                            : isBlockedBooked ? '⚠'
                            : isBlocked ? '×'
                            : isBooked ? '🔒'
                            : '◎'}
                        </span>
                        {(isBooked || isBlockedBooked || isClosedBooked) && cellBookings.length > 0 && (
                          <span className="bm-booking-badge">{cellBookings.length}</span>
                        )}
                      </td>
                    );
                  })}
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>

      <div className="block-stat-row">
        <div className="block-stat">この週のブロック中スロット <strong>{blockedSlotsCount}</strong> コマ</div>
        {blocks.length > 0 && (
          <button className="btn sm danger" onClick={() => { if (confirm('すべての定休日設定をクリアしますか？')) onSetBlocks([]); }}>
            <Icon name="trash" size={12}/> すべてクリア
          </button>
        )}
      </div>

      {conflictModal && (
        <Modal
          title="この時間には予約があります"
          onClose={() => setConflictModal(null)}
          footer={<button className="btn primary" onClick={() => setConflictModal(null)}>閉じる</button>}
        >
          <div className="conflict-warn">
            <div className="conflict-warn-icon">⚠</div>
            <div>
              <div className="conflict-warn-title">
                {conflictModal.mode === 'day'
                  ? D.fmtDateJa(D.isoDate(conflictModal.date)) + ' は予約が入っているため、終日休にできません'
                  : D.fmtDateJa(D.isoDate(conflictModal.date)) + ' ' + conflictModal.time + ' には予約があるため、定休日にできません'}
              </div>
              <div className="conflict-warn-desc">
                先に下記の予約をキャンセルまたは別日へ変更してから、再度ブロック設定してください。
              </div>
            </div>
          </div>
          <div className="conflict-list">
            {conflictModal.bookings.map(b => (
              <div key={b.id} className="conflict-item" onClick={() => { setConflictModal(null); onOpenBooking && onOpenBooking(b); }}>
                <div className="conflict-time">
                  <div className="t">{b.time}</div>
                  <div className="dur">{b.duration||60}分</div>
                </div>
                <div className="conflict-info">
                  <div className="name">{b.name} 様</div>
                  <div className="menu">{b.menu}・¥{(b.price||0).toLocaleString()}</div>
                  <div className="tel">{b.tel}</div>
                </div>
                <StatusPill status={b.status}/>
                <Icon name="chev-right" size={16}/>
              </div>
            ))}
          </div>
          <div className="conflict-hint">予約をクリックすると詳細・キャンセル画面が開きます</div>
        </Modal>
      )}
    </div>
  );
}
window.BlocksPage = BlocksPage;
