// メニュー名から色クラスを決定（カレンダー表示用）
// 優先順位：初回 > 回数券/月額 > 90分 > 60分 > その他
function getMenuColorClass(b) {
  // 初回フラグが立っていれば最優先で「初回」緑
  if (b.isFirstVisit === true) return 'menu-color-first';
  const m = String(b.menu || b.menu_name || '');
  if (m.includes('初回')) return 'menu-color-first';
  if (m.includes('回数券') || m.includes('月額') || m.includes('プラン')) return 'menu-color-ticket';
  if (m.includes('90')) return 'menu-color-90';
  if (m.includes('60')) return 'menu-color-60';
  return 'menu-color-other';
}

// ===== Calendar (week / day / month) =====
function CalendarPage({ bookings, settings, blocks, onSetBlocks, menus, customers, onOpenBooking, onAddBooking, onUpdateBooking }) {
  const [mode, setMode] = useState('booking'); // booking | block
  // スマホ（≤600px）は「日」表示が見やすいので初期値を変える
  const [view, setView] = useState(() =>
    (typeof window !== 'undefined' && window.innerWidth <= 600) ? 'day' : 'week'
  ); // week | day | month
  const [cursor, setCursor] = useState(() => { const d = new Date(); d.setHours(0,0,0,0); return d; });
  const [draggingId, setDraggingId] = useState(null);
  const [dropHint, setDropHint] = useState(null); // {date, time}

  const todayISO = D.isoDate(new Date());

  const navPrev = () => {
    const d = new Date(cursor);
    if (view === 'week') d.setDate(d.getDate()-7);
    else if (view === 'day') d.setDate(d.getDate()-1);
    else d.setMonth(d.getMonth()-1);
    setCursor(d);
  };
  const navNext = () => {
    const d = new Date(cursor);
    if (view === 'week') d.setDate(d.getDate()+7);
    else if (view === 'day') d.setDate(d.getDate()+1);
    else d.setMonth(d.getMonth()+1);
    setCursor(d);
  };
  const navToday = () => { const d = new Date(); d.setHours(0,0,0,0); setCursor(d); };

  const headerLabel = useMemo(() => {
    if (view === 'day') return D.fmtDateLong(cursor);
    if (view === 'month') return D.fmtMonth(cursor);
    const ws = D.startOfWeek(cursor);
    const we = new Date(ws); we.setDate(we.getDate()+6);
    return ws.getFullYear() + '年 ' + (ws.getMonth()+1) + '/' + ws.getDate() + ' 〜 ' + (we.getMonth()+1) + '/' + we.getDate();
  }, [view, cursor]);

  // 定休日：完全クローズ（クリック/ドラッグ不可）
  const isClosed = (date, t) => {
    const dow = date.getDay();
    const h = settings.hours[dow];
    if (!h.open) return true;
    // 祝日休み設定がONなら祝日も定休扱い
    if (settings.holidaysClosed !== false && D.isHoliday && D.isHoliday(D.isoDate(date))) return true;
    const m = D.timeToMin(t);
    // 開店前は閉店扱い、閉店後21:00までは営業時間外（クリック/ドラッグ可）
    if (m < D.timeToMin(h.from)) return true;
    return false;
  };
  // 営業時間外（閉店後〜21:00）：薄色表示・操作可
  const isOutOfHours = (date, t) => {
    const dow = date.getDay();
    const h = settings.hours[dow];
    if (!h.open) return false;
    const m = D.timeToMin(t);
    return m >= D.timeToMin(h.to);
  };

  const isBlocked = (date, t) => {
    const iso = D.isoDate(date);
    const m = D.timeToMin(t);
    return blocks.some(b => b.date === iso && (b.allDay || (D.timeToMin(b.from) <= m && m < D.timeToMin(b.to))));
  };

  const getBlockReason = (date) => {
    const iso = D.isoDate(date);
    const allDay = blocks.find(b => b.date === iso && b.allDay);
    return allDay ? (allDay.reason || '定休日') : '';
  };

  // Drag handlers
  const onEventDragStart = (e, id) => {
    setDraggingId(id);
    e.dataTransfer.effectAllowed = 'move';
    e.dataTransfer.setData('text/plain', id);
  };
  const onEventDragEnd = () => { setDraggingId(null); setDropHint(null); };
  const onSlotDragOver = (e, dateISO, time) => {
    e.preventDefault();
    setDropHint({ date: dateISO, time });
  };
  const onSlotDrop = (e, dateISO, time) => {
    e.preventDefault();
    if (draggingId) {
      // 移動先の時間枠で重複がないかクライアント側で先にチェック（即時UX）
      const dragged = bookings.find(b => b.id === draggingId);
      if (dragged) {
        const dur = dragged.duration || 60;
        const [h, m] = String(time).split(':').map(n => parseInt(n, 10));
        const startMin = (h || 0) * 60 + (m || 0);
        const endMin = startMin + dur;
        const conflict = bookings.find(b => {
          if (b.id === draggingId) return false;
          if (b.date !== dateISO) return false;
          if (b.status !== 'confirmed' && b.status !== 'done') return false;
          const [bh, bm] = String(b.time || '0:0').split(':').map(n => parseInt(n, 10));
          const bs = (bh || 0) * 60 + (bm || 0);
          const be = bs + (b.duration || 60);
          return startMin < be && endMin > bs;
        });
        if (conflict) {
          alert(`移動先の時間帯（${time}）には既に「${conflict.name || '別の予約'} 様」の予約があります。\n\n別の時間枠を選んでください。`);
          setDraggingId(null);
          setDropHint(null);
          return;
        }
      }
      onUpdateBooking(draggingId, { date: dateISO, time });
    }
    setDraggingId(null);
    setDropHint(null);
  };

  return (
    <div>
      <div className="cal-mode-tabs">
        <button className={'cal-mode-tab' + (mode==='booking'?' active':'')} onClick={() => setMode('booking')}>
          <Icon name="calendar" size={14}/> 予約カレンダー
        </button>
        <button className={'cal-mode-tab' + (mode==='block'?' active':'')} onClick={() => setMode('block')}>
          <Icon name="block" size={14}/> 定休日設定
        </button>
        <div className="cal-mode-hint">
          {mode==='booking' ? '予約の確認・追加・移動ができます' : 'クリックひとつで予約可否を切り替え'}
        </div>
      </div>

      {mode === 'block' ? (
        <BlocksPage blocks={blocks} onSetBlocks={onSetBlocks} settings={settings} bookings={bookings} onOpenBooking={onOpenBooking}/>
      ) : (
      <>
      <div className="cal-toolbar">
        <div className="cal-view-switch">
          <button className={view==='month'?'active':''} onClick={() => setView('month')}>月</button>
          <button className={view==='week'?'active':''} onClick={() => setView('week')}>週</button>
          <button className={view==='day'?'active':''} onClick={() => setView('day')}>日</button>
        </div>
        <div className="cal-nav-group">
          <button className="cal-nav-btn" onClick={navPrev}><Icon name="chev-left" size={16}/></button>
          <div className="cal-current">{headerLabel}</div>
          <button className="cal-nav-btn" onClick={navNext}><Icon name="chev-right" size={16}/></button>
        </div>
        <button className="btn cal-today-btn" onClick={navToday}>今日</button>
        <div style={{flex:1}}/>
        <button className="btn primary" onClick={() => onAddBooking(D.isoDate(cursor), '10:00')}>
          <Icon name="plus" size={14}/> 予約を追加
        </button>
      </div>

      {view === 'week' && (
        <WeekView
          cursor={cursor} bookings={bookings} customers={customers} settings={settings} todayISO={todayISO}
          isClosed={isClosed} isBlocked={isBlocked} isOutOfHours={isOutOfHours}
          draggingId={draggingId} dropHint={dropHint}
          onEventClick={onOpenBooking} onSlotClick={onAddBooking}
          onEventDragStart={onEventDragStart} onEventDragEnd={onEventDragEnd}
          onSlotDragOver={onSlotDragOver} onSlotDrop={onSlotDrop}
        />
      )}
      {view === 'day' && (
        <DayView
          cursor={cursor} bookings={bookings} customers={customers} settings={settings} todayISO={todayISO}
          isClosed={isClosed} isBlocked={isBlocked} isOutOfHours={isOutOfHours}
          draggingId={draggingId}
          onEventClick={onOpenBooking} onSlotClick={onAddBooking}
          onEventDragStart={onEventDragStart} onEventDragEnd={onEventDragEnd}
          onSlotDragOver={onSlotDragOver} onSlotDrop={onSlotDrop}
        />
      )}
      {view === 'month' && (
        <MonthView
          cursor={cursor} bookings={bookings} settings={settings} blocks={blocks} todayISO={todayISO}
          onCellClick={(iso) => { setCursor(D.parseISO(iso)); setView('day'); }}
          onEventClick={onOpenBooking}
        />
      )}
      </>
      )}
    </div>
  );
}

// ============ Week View ============
function WeekView({ cursor, bookings, customers, settings, todayISO, isClosed, isBlocked, isOutOfHours, draggingId, dropHint, onEventClick, onSlotClick, onEventDragStart, onEventDragEnd, onSlotDragOver, onSlotDrop }) {
  const ws = D.startOfWeek(cursor);
  const days = Array.from({length:7}, (_, i) => { const d = new Date(ws); d.setDate(d.getDate()+i); return d; });
  // 9:00 - 21:00 in 15-min slots = 48 rows（営業終了後〜21:00 は特例予約用）
  const startMin = D.CONSTANTS.DAY_START_MIN, endMin = D.CONSTANTS.DAY_END_MIN, step = D.CONSTANTS.SLOT_MINUTES;
  const rows = (endMin - startMin) / step;
  const headRef = useRef(null);
  const [headH, setHeadH] = useState(60);
  // 依存配列に [headH] を指定。レンダー毎に走らないようにし、無限ループ警告を回避
  useEffect(() => {
    if (headRef.current) {
      const h = headRef.current.getBoundingClientRect().height;
      if (h && Math.abs(h - headH) > 0.5) setHeadH(h);
    }
  }, [headH]);

  // group bookings by day
  const byDay = {};
  bookings.forEach(b => {
    if (b.status === 'cancelled' || b.status === 'no_show') return;
    if (!byDay[b.date]) byDay[b.date] = [];
    byDay[b.date].push(b);
  });

  return (
    <div className="cal-week">
      <div className="cal-week-grid">
        <div className="cal-week-head">
          <div className="corner" ref={headRef}/>
          {days.map((d, i) => {
            const isToday = D.isoDate(d) === todayISO;
            const cls = d.getDay() === 0 ? 'sun' : d.getDay() === 6 ? 'sat' : '';
            const holidayName = D.getHolidayName ? D.getHolidayName(D.isoDate(d)) : null;
            return (
              <div key={i}>
                <div>{D.WEEK_LABELS[d.getDay()]}</div>
                {isToday
                  ? <span className="day-num"><span className="today">{d.getDate()}</span></span>
                  : <span className={'day-num ' + cls}>{d.getDate()}</span>}
                {holidayName && <div style={{fontSize:9, color:'var(--red)', fontWeight:700, marginTop:-2}}>{holidayName}</div>}
              </div>
            );
          })}
        </div>
        {Array.from({length: rows}, (_, r) => {
          const min = startMin + r*step;
          const t = D.minToTime(min);
          const isHour = (min % 60) === 0;
          const isHalf = (min % 30) === 0;
          return (
            <React.Fragment key={r}>
              <div className={'cal-time-col' + (isHour ? ' hour' : '')}>{isHour ? t : ''}</div>
              {days.map((d, di) => {
                const closed = isClosed(d, t);
                const blocked = !closed && isBlocked && isBlocked(d, t);
                const ooh = !closed && !blocked && isOutOfHours && isOutOfHours(d, t);
                const dateISO = D.isoDate(d);
                const isToday = dateISO === todayISO;
                const isHint = dropHint && dropHint.date === dateISO && dropHint.time === t;
                const unavailable = closed || blocked;
                return (
                  <div
                    key={di}
                    className={'cal-day-col' + (isHour ? ' hour-mark' : (isHalf ? ' half-mark' : '')) + (closed ? ' closed' : '') + (blocked ? ' blocked' : '') + (ooh ? ' out-of-hours' : '') + (isToday ? ' today-col' : '')}
                    onClick={() => { if (!unavailable) onSlotClick(dateISO, t); }}
                    onDragOver={(e) => { if (!unavailable) onSlotDragOver(e, dateISO, t); }}
                    onDrop={(e) => { if (!unavailable) onSlotDrop(e, dateISO, t); }}
                  >
                    {isHint && (
                      <div className="cal-drop-indicator" style={{ top: 0, height: '36px' }}/>
                    )}
                  </div>
                );
              })}
            </React.Fragment>
          );
        })}
      </div>
      {/* Events overlay: absolutely positioned over the grid */}
      <div className="cal-events-overlay" style={{ top: headH + 'px' }}>
        {days.map((d, di) => {
          const dateISO = D.isoDate(d);
          const events = (byDay[dateISO] || []).slice().sort((a,b) => a.time.localeCompare(b.time));
          const BUFFER = D.CONSTANTS.BOOKING_BUFFER_MIN;
          return events.flatMap(b => {
            const startTotal = D.timeToMin(b.time);
            const offset = startTotal - startMin;
            if (offset < 0 || offset >= (endMin - startMin)) return [];
            const top = (offset / step) * 36; // 36px per row
            const height = ((b.duration || 60) / step) * 36;
            const bufferTop = top + height;
            const bufferHeight = (BUFFER / step) * 36;
            const isNew = b.isFirstVisit === true;
            const isDragging = draggingId === b.id;
            const menuClass = getMenuColorClass(b);
            const showBuffer = b.status !== 'cancelled' && b.status !== 'no_show';
            // 顧客情報（領収書希望・支払い方法）
            const cust = (customers || []).find(c => c.id === b.customerId);
            const needsReceipt = cust && cust.needsReceipt;
            const hasMemo = cust && cust.pendingMemo;
            return [
              <div
                key={b.id}
                className={'cal-event ' + b.status + ' ' + menuClass + (isNew ? ' new-flag' : '') + (isDragging ? ' dragging' : '')}
                style={{
                  left: `calc(60px + (100% - 60px) / 7 * ${di} + 2px)`,
                  width: `calc((100% - 60px) / 7 - 4px)`,
                  top: top + 'px',
                  height: (height - 2) + 'px',
                }}
                draggable
                onClick={(e) => { e.stopPropagation(); onEventClick(b); }}
                onDragStart={(e) => onEventDragStart(e, b.id)}
                onDragEnd={onEventDragEnd}
                title={b.name + ' / ' + b.menu + (needsReceipt ? ' / 📄領収書' : '') + (hasMemo ? ' / 📝' + hasMemo : '')}
              >
                <div className="cal-event-time">
                  {needsReceipt && <span className="cal-event-icon-inline" style={{color:'#d6651e'}} title="領収書必要">📄</span>}
                  {hasMemo && <span className="cal-event-icon-inline" title={hasMemo}>📝</span>}
                  {b.time}
                </div>
                <div className="cal-event-name">{b.name} 様</div>
                <div className="cal-event-menu">{b.menu}</div>
              </div>,
              showBuffer && (
                <div
                  key={b.id + '-buf'}
                  className="cal-event-buffer"
                  style={{
                    position: 'absolute',
                    left: `calc(60px + (100% - 60px) / 7 * ${di} + 2px)`,
                    width: `calc((100% - 60px) / 7 - 4px)`,
                    top: bufferTop + 'px',
                    height: (bufferHeight - 2) + 'px',
                    pointerEvents: 'none',
                  }}
                  title={'片付け・お見送り 15分'}
                />
              )
            ].filter(Boolean);
          });
        })}
      </div>
      <div className="cal-legend-row" style={{flexWrap:'wrap', gap:6}}>
        <span style={{background:'#d8ecd6', borderLeft:'4px solid #4f8c4d', padding:'2px 8px', borderRadius:4, fontSize:11}}>初回</span>
        <span style={{background:'#d4e2f5', borderLeft:'4px solid #3568b0', padding:'2px 8px', borderRadius:4, fontSize:11}}>60分</span>
        <span style={{background:'#fadcc4', borderLeft:'4px solid #d6651e', padding:'2px 8px', borderRadius:4, fontSize:11}}>90分</span>
        <span style={{background:'#e2d5ee', borderLeft:'4px solid #7e3fae', padding:'2px 8px', borderRadius:4, fontSize:11}}>回数券/月額</span>
        <span style={{background:'#fff', border:'1px solid var(--line)', padding:'2px 8px', borderRadius:4, fontSize:11, color:'var(--ink-mute)'}}><span style={{background:'#2966a3', color:'#fff', fontSize:9, padding:'1px 4px', borderRadius:3, marginRight:3}}>✓済</span>来院済</span>
        <span style={{background:'rgba(214,101,30,0.04)', border:'1px solid rgba(214,101,30,0.3)', padding:'2px 8px', borderRadius:4, fontSize:11, color:'var(--ink-soft)'}}>営業時間外</span>
        <span className="blocked" style={{padding:'2px 8px', borderRadius:4, fontSize:11}}>定休日</span>
        <span style={{background:'repeating-linear-gradient(45deg, rgba(180,180,180,0.25) 0 4px, rgba(160,160,160,0.12) 4px 8px)', padding:'2px 8px', borderRadius:4, fontSize:11, color:'var(--ink-soft)'}}>片付け15分</span>
        <span style={{background:'#fff', border:'1px solid var(--line)', padding:'2px 8px', borderRadius:4, fontSize:11}}>📄 領収書必要</span>
        <span style={{background:'#fff', border:'1px solid var(--line)', padding:'2px 8px', borderRadius:4, fontSize:11}}>📝 次回宿題</span>
        <span style={{marginLeft:'auto', color:'var(--ink-mute)', fontSize:11}}>※ ドラッグで予約移動可</span>
      </div>
    </div>
  );
}

// ============ Day View ============
function DayView({ cursor, bookings, customers, settings, todayISO, isClosed, isBlocked, isOutOfHours, draggingId, onEventClick, onSlotClick, onEventDragStart, onEventDragEnd, onSlotDragOver, onSlotDrop }) {
  const dateISO = D.isoDate(cursor);
  const startMin = D.CONSTANTS.DAY_START_MIN, endMin = D.CONSTANTS.DAY_END_MIN, step = D.CONSTANTS.SLOT_MINUTES;
  const rows = (endMin - startMin) / step;
  const dayBookings = bookings
    .filter(b => b.date === dateISO && b.status !== 'cancelled' && b.status !== 'no_show')
    .sort((a,b) => a.time.localeCompare(b.time));

  return (
    <div className="cal-day-view">
      <div className="cal-day-times">
        {Array.from({length: rows}, (_, r) => {
          const min = startMin + r*step;
          const isHour = (min % 60) === 0;
          return <div key={r} className={'row' + (isHour ? ' hour' : '')}>{isHour ? D.minToTime(min) : ''}</div>;
        })}
      </div>
      <div className="cal-day-area">
        {Array.from({length: rows}, (_, r) => {
          const min = startMin + r*step;
          const t = D.minToTime(min);
          const closed = isClosed(cursor, t);
          const blocked = !closed && isBlocked && isBlocked(cursor, t);
          const ooh = !closed && !blocked && isOutOfHours && isOutOfHours(cursor, t);
          const unavailable = closed || blocked;
          return (
            <div
              key={r}
              className={'row' + (closed ? ' closed' : '') + (blocked ? ' blocked' : '') + (ooh ? ' out-of-hours' : '')}
              onClick={() => { if (!unavailable) onSlotClick(dateISO, t); }}
              onDragOver={(e) => { if (!unavailable) onSlotDragOver(e, dateISO, t); }}
              onDrop={(e) => { if (!unavailable) onSlotDrop(e, dateISO, t); }}
            />
          );
        })}
        {dayBookings.map(b => {
          const startTotal = D.timeToMin(b.time);
          const offset = startTotal - startMin;
          const top = (offset / step) * 60;
          const height = ((b.duration || 60) / step) * 60;
          const isDragging = draggingId === b.id;
          const menuClass = getMenuColorClass(b);
          return (
            <div
              key={b.id}
              className={'cal-day-event ' + b.status + ' ' + menuClass + (isDragging ? ' dragging' : '')}
              style={{ top: (top+4) + 'px', height: (height-8) + 'px' }}
              draggable
              onClick={(e) => { e.stopPropagation(); onEventClick(b); }}
              onDragStart={(e) => onEventDragStart(e, b.id)}
              onDragEnd={onEventDragEnd}
            >
              <div className="cal-day-event-time">{b.time}〜{D.addMinTime(b.time, b.duration||60)} ({(b.duration||60)}分)</div>
              <div className="cal-day-event-name">{b.name} 様 <StatusPill status={b.status}/></div>
              <div className="cal-day-event-menu">{b.menu}・¥{(b.price||0).toLocaleString()}・担当 {(settings.staff.find(s => s.id === b.staffId) || {}).name || '—'}</div>
            </div>
          );
        })}
      </div>
    </div>
  );
}

// ============ Month View ============
function MonthView({ cursor, bookings, settings, blocks, todayISO, onCellClick, onEventClick }) {
  const first = D.startOfMonth(cursor);
  const startGrid = D.startOfWeek(first);
  const cells = [];
  for (let i = 0; i < 42; i++) {
    const d = new Date(startGrid); d.setDate(d.getDate()+i);
    cells.push(d);
  }

  const isClosedDay = (d) => {
    const h = settings.hours[d.getDay()];
    return !h || !h.open;
  };
  const isBlockedDay = (d) => {
    const iso = D.isoDate(d);
    return blocks.some(b => b.date === iso && b.allDay);
  };
  const hasPartialBlock = (d) => {
    const iso = D.isoDate(d);
    return blocks.some(b => b.date === iso && !b.allDay);
  };

  return (
    <div className="cal-month-grid">
      <div className="cal-month-headrow">
        {D.WEEK_LABELS.map((w, i) => (
          <div key={i} className={i===0?'sun':i===6?'sat':''}>{w}</div>
        ))}
      </div>
      <div className="cal-month-body">
        {cells.map((d, i) => {
          const iso = D.isoDate(d);
          const inMonth = d.getMonth() === cursor.getMonth();
          const isToday = iso === todayISO;
          const dayBookings = bookings.filter(b => b.date === iso && b.status !== 'cancelled');
          const dow = d.getDay();
          const closed = isClosedDay(d);
          const blockedAllDay = isBlockedDay(d);
          const partialBlock = !blockedAllDay && hasPartialBlock(d);
          return (
            <div
              key={i}
              className={'cal-month-cell' + (inMonth ? '' : ' other-month') + (isToday ? ' today' : '') + (closed ? ' closed' : '') + (blockedAllDay ? ' blocked-day' : '')}
              onClick={() => onCellClick(iso)}
            >
              <span className={'cal-month-day' + (dow === 0 ? ' sun' : dow === 6 ? ' sat' : '')}>{d.getDate()}</span>
              {blockedAllDay && <span className="cal-month-block-badge">定休日</span>}
              {partialBlock && !blockedAllDay && <span className="cal-month-block-badge partial">一部休</span>}
              {dayBookings.slice(0, 3).map(b => (
                <div
                  key={b.id}
                  className={'cal-month-event ' + b.status}
                  onClick={(e) => { e.stopPropagation(); onEventClick(b); }}
                >
                  {b.time} {b.name}
                </div>
              ))}
              {dayBookings.length > 3 && <div className="cal-month-event more">+{dayBookings.length - 3} 件</div>}
            </div>
          );
        })}
      </div>
    </div>
  );
}

window.CalendarPage = CalendarPage;
