/* global React, Icon, UI, LessonHighData */
(function () {
const { useState } = React;
const D3 = LessonHighData;

// ============================================================
// Spaces — POS-style card view of rooms with weekly mini-schedule
// ============================================================
function Spaces({ spaceLabel, onOpenMember, onNav }) {
  const [selectedRoom, setSelectedRoom] = useState('r1');
  const [showAllEmpty, setShowAllEmpty] = useState(false);
  const todayDow = 3;
  const policy = D3.spacePolicy || {};

  // Demo: extend to 10 rooms so the layout demonstrates scalability.
  // Real rooms come from data; demoState fakes the "now" state for the rest.
  const NOW = 15 * 60 + 30;
  const demoRooms = [
    ...D3.rooms.map(r => ({ ...r, kind: 'practice' })),
    { id: 'r4',  name: '연습실 D',     capacity: 1, kind: 'practice', demo: { state: 'busy', memberId: 'm6',  teacherId: 't2', start: 15*60,    end: 16*60 } },
    { id: 'r5',  name: '연습실 E',     capacity: 1, kind: 'practice' },
    { id: 'r6',  name: '연습실 F',     capacity: 1, kind: 'practice', demo: { state: 'busy', memberId: 'm8',  teacherId: 't1', start: 14*60+30, end: 16*60 } },
    { id: 'r7',  name: '연습실 G',     capacity: 1, kind: 'practice', demoNext: { start: 16*60, memberId: 'm3' } },
    { id: 'r8',  name: '그룹 클래스룸', capacity: 6, kind: 'group',    demo: { state: 'busy', prospect: '재즈 보컬 그룹', teacherId: 't3', start: 15*60,    end: 16*60+30 } },
    { id: 'r9',  name: '보컬 부스 1',  capacity: 1, kind: 'booth',    demoNext: { start: 17*60, memberId: 'm5' } },
    { id: 'r10', name: '보컬 부스 2',  capacity: 1, kind: 'booth',    demo: { state: 'busy', memberId: 'm3',  teacherId: 't3', start: 15*60+15, end: 15*60+45 } },
  ];

  // Find "current usage" per room — real reservation first, then demo fallback
  function currentUsage(room) {
    const r = D3.reservations.find(r => r.dow === todayDow && r.roomId === room.id && r.start <= NOW && r.end > NOW);
    if (r) return { state: 'busy', res: r };
    if (room.demo) return { state: 'busy', res: room.demo };
    const next = D3.reservations
      .filter(r => r.dow === todayDow && r.roomId === room.id && r.start > NOW)
      .sort((a,b) => a.start - b.start)[0];
    return { state: 'free', next: next || room.demoNext };
  }

  const enriched = demoRooms.map(room => ({ room, usage: currentUsage(room) }));
  const busyRooms = enriched.filter(e => e.usage.state === 'busy');
  const freeRooms = enriched.filter(e => e.usage.state === 'free');
  const usagePct = enriched.length ? Math.round((busyRooms.length / enriched.length) * 100) : 0;
  const visibleFree = showAllEmpty ? freeRooms : freeRooms.slice(0, 6);

  return (
    <React.Fragment>
      <header className="page-header">
        <div>
          <h1 className="page-title">{spaceLabel}</h1>
          <div className="page-sub">현재 운영 중 · {enriched.length}개 공간 · 오늘 사용률 {usagePct}%</div>
        </div>
        <div className="page-actions">
          <UI.Button icon={Icon.Refresh} variant="ghost" onClick={() => window.toast && window.toast('공간 목록 새로고침', { tone: 'success' })}>새로고침</UI.Button>
          {policy.practiceEnabled && (
            <UI.Button icon={Icon.Plus} variant="ghost" onClick={() => window.toast && window.toast('자율 연습 예약 — 일정 페이지에서 시간을 선택하세요')}>자율 연습 예약</UI.Button>
          )}
          <UI.Button icon={Icon.Plus} variant="primary" onClick={() => window.toast && window.toast(`${spaceLabel} 대여 등록 모달 — 일정 페이지에서 수동 추가`)}>{spaceLabel} 대여 등록</UI.Button>
        </div>
      </header>

      {/* I6 — Practice booking policy banner */}
      <div className={`space-policy-banner ${policy.practiceEnabled ? '' : 'is-disabled'}`}>
        <span className="space-policy-banner__dot"/>
        {policy.practiceEnabled ? (
          <span>
            자율 연습 예약 <strong style={{ color: 'var(--text-primary)' }}>활성</strong>
            {' · '}
            {[policy.memberCanBook && '회원', policy.teacherCanBook && '강사'].filter(Boolean).join(' · ') || '권한 없음'} 예약 가능
            {' · '}
            {policy.minDurationMin}–{policy.maxDurationMin}분
            {' · '}
            {policy.costModel === 'free' ? '무료' :
             policy.costModel === 'pass' ? `패스 ${policy.passDeductRatio}회 차감` :
             `시간당 ${(policy.paidRatePerHour || 0).toLocaleString()}원`}
            {policy.requiresApproval && ' · 원장 승인 필요'}
          </span>
        ) : (
          <span>
            자율 연습 예약 <strong>비활성</strong> · 수업·상담·대여 예약만 허용됩니다
          </span>
        )}
        <button className="space-policy-banner__settings" onClick={() => onNav && onNav('settings')}>설정 열기 →</button>
      </div>

      {/* Summary strip — at-a-glance count */}
      <div className="room-summary">
        <div className="room-summary__seg">
          <div className="room-summary__bar">
            <div className="room-summary__fill" style={{ width: `${usagePct}%` }}/>
          </div>
        </div>
        <div className="row" style={{ gap: 20, flexShrink: 0 }}>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, whiteSpace: 'nowrap' }}>
            <span style={{ width: 8, height: 8, borderRadius: 99, background: 'var(--accent-default)' }}/>
            <span className="t-body-strong t-mono num">{busyRooms.length}</span>
            <span className="t-caption" style={{ whiteSpace: 'nowrap' }}>사용 중</span>
          </span>
          <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, whiteSpace: 'nowrap' }}>
            <span style={{ width: 8, height: 8, borderRadius: 99, background: 'var(--border-strong)' }}/>
            <span className="t-body-strong t-mono num">{freeRooms.length}</span>
            <span className="t-caption" style={{ whiteSpace: 'nowrap' }}>비어있음</span>
          </span>
        </div>
      </div>

      {/* Busy rooms — full detail */}
      {busyRooms.length > 0 && (
        <React.Fragment>
          <div className="room-section-label">
            <span className="t-micro">사용 중 · {busyRooms.length}개</span>
            <span className="t-caption">실시간 진행 상황</span>
          </div>
          <div className="room-grid room-grid--busy" style={{ marginBottom: 16 }}>
            {busyRooms.map(({ room, usage }) => (
              <RoomCardBusy key={room.id} room={room} usage={usage} isSelected={selectedRoom === room.id} onClick={() => setSelectedRoom(room.id)} />
            ))}
          </div>
        </React.Fragment>
      )}

      {/* Empty rooms — compact */}
      {freeRooms.length > 0 && (
        <React.Fragment>
          <div className="room-section-label">
            <span className="t-micro">비어있음 · {freeRooms.length}개</span>
            <span className="t-caption">바로 예약 가능</span>
          </div>
          <div className="room-grid room-grid--free" style={{ marginBottom: 24 }}>
            {visibleFree.map(({ room, usage }) => (
              <RoomCardFree key={room.id} room={room} usage={usage} isSelected={selectedRoom === room.id} onClick={() => setSelectedRoom(room.id)} />
            ))}
          </div>
          {freeRooms.length > 6 && (
            <button className="btn is-ghost is-sm" style={{ marginTop: -12, marginBottom: 24 }} onClick={() => setShowAllEmpty(v => !v)}>
              {showAllEmpty ? '접기' : `+${freeRooms.length - 6}개 더 보기`}
            </button>
          )}
        </React.Fragment>
      )}

      {/* Weekly timetable for selected room */}
      <UI.Card>
        <UI.CardHeader
          title={`${demoRooms.find(r => r.id === selectedRoom)?.name} · 이번 주`}
          actions={<UI.Button size="sm" variant="ghost" onClick={() => window.toast && window.toast(`전체 일정으로 이동 — ${demoRooms.find(r => r.id === selectedRoom)?.name} 필터 적용`)}>전체 일정 →</UI.Button>}
        />
        <UI.CardBody flush>
          <RoomWeek roomId={selectedRoom} onOpenMember={onOpenMember} />
        </UI.CardBody>
      </UI.Card>
    </React.Fragment>
  );
}

// Busy room — full detail card (member + timer + progress)
function RoomCardBusy({ room, usage, isSelected, onClick }) {
  const NOW = 15 * 60 + 30;
  const member = usage.res.memberId ? D3.getMember(usage.res.memberId) : null;
  const teacher = usage.res.teacherId ? D3.getTeacher(usage.res.teacherId) : null;
  const remaining = Math.max(0, usage.res.end - NOW);
  const progress = Math.min(100, Math.max(0, ((NOW - usage.res.start) / (usage.res.end - usage.res.start)) * 100));
  return (
    <button onClick={onClick} className={`room-card is-busy ${isSelected ? 'is-selected' : ''}`}>
      <div className="room-card__head" style={{ marginBottom: 12 }}>
        <span className="t-h3 room-card__name">{room.name}</span>
        <UI.Badge tone="accent" dot>사용 중</UI.Badge>
        <span className="t-caption room-card__cap">최대 {room.capacity}인</span>
      </div>
      <div className="col" style={{ gap: 8 }}>
        <div className="row">
          {member && <UI.Avatar name={member.name} tone={member.tone} size="md"/>}
          <div className="col" style={{ gap: 0, flex: 1, minWidth: 0 }}>
            <span className="t-body-strong">{member ? member.name : usage.res.prospect || '대여 중'}</span>
            <span className="t-caption">{teacher ? `${teacher.name} 선생님` : '공간 대여'}</span>
          </div>
        </div>
        <div className="row t-mono num" style={{ fontSize: 12, color: 'var(--text-secondary)' }}>
          <span>{D3.formatTime(usage.res.start)} – {D3.formatTime(usage.res.end)}</span>
          <span style={{ marginLeft: 'auto', color: 'var(--accent-default)', fontWeight: 600 }}>
            {remaining}분 남음
          </span>
        </div>
        <div className="room-progress">
          <div className="room-progress__fill" style={{ width: `${progress}%` }}/>
        </div>
      </div>
    </button>
  );
}

// Empty room — compact single-line card
function RoomCardFree({ room, usage, isSelected, onClick }) {
  const nextMember = usage.next?.memberId ? D3.getMember(usage.next.memberId) : null;
  const nextLabel = usage.next ? `${D3.formatTime(usage.next.start)}` : null;
  return (
    <button onClick={onClick} className={`room-card-compact ${isSelected ? 'is-selected' : ''}`}>
      <div className="row" style={{ gap: 8, alignItems: 'center' }}>
        <span style={{ width: 6, height: 6, borderRadius: 99, background: 'var(--border-strong)', flexShrink: 0 }}/>
        <span className="t-body-strong" style={{ minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{room.name}</span>
        <span className="t-caption" style={{ marginLeft: 'auto', fontSize: 11, flexShrink: 0 }}>최대 {room.capacity}</span>
      </div>
      <div className="t-caption" style={{ fontSize: 12, marginTop: 4, display: 'flex', gap: 4, alignItems: 'baseline' }}>
        {nextLabel ? (
          <React.Fragment>
            <span>다음</span>
            <span className="t-mono num" style={{ color: 'var(--text-secondary)', fontWeight: 600 }}>{nextLabel}</span>
            {nextMember && <span style={{ color: 'var(--text-secondary)', minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>· {nextMember.name}</span>}
          </React.Fragment>
        ) : <span className="tertiary">오늘 예약 없음</span>}
      </div>
    </button>
  );
}

function RoomWeek({ roomId, onOpenMember }) {
  const startHour = 13;
  const endHour = 22;
  const days = [
    { dow: 1, dn: '월', date: '17' },
    { dow: 2, dn: '화', date: '18' },
    { dow: 3, dn: '수', date: '19' },
    { dow: 4, dn: '목', date: '20', isToday: true },
    { dow: 5, dn: '금', date: '21' },
    { dow: 6, dn: '토', date: '22' },
    { dow: 7, dn: '일', date: '23' },
  ];
  // override — today is wed in our data
  days.forEach(d => d.isToday = (d.dow === 3));

  const slotPx = 28;

  return (
    <div style={{ overflowX: 'auto' }}>
      <div style={{ display: 'grid', gridTemplateColumns: '60px repeat(7, 1fr)', minWidth: 720 }}>
        {/* Header */}
        <div style={{ borderBottom: '1px solid var(--border-default)' }}></div>
        {days.map(d => (
          <div key={d.dow} style={{
            padding: '10px 8px',
            borderBottom: '1px solid var(--border-default)',
            background: d.isToday ? 'var(--accent-subtle-bg)' : 'transparent',
            textAlign: 'center',
          }}>
            <div className="t-micro">{d.dn}</div>
            <div className="t-h3" style={{ color: d.isToday ? 'var(--accent-default)' : 'var(--text-primary)' }}>{d.date}</div>
          </div>
        ))}

        {/* Hours */}
        {Array.from({ length: endHour - startHour }).map((_, hi) => {
          const hour = startHour + hi;
          return (
            <React.Fragment key={hour}>
              <div style={{ padding: '4px 8px', borderRight: '1px solid var(--border-default)', borderBottom: '1px solid var(--border-default)', height: slotPx * 2 + 1, position: 'relative' }}>
                <span className="t-mono num" style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>
                  {String(hour).padStart(2,'0')}:00
                </span>
              </div>
              {days.map(d => {
                const matches = D3.reservations.filter(r => r.roomId === roomId && r.dow === d.dow && Math.floor(r.start / 60) === hour);
                return (
                  <div key={d.dow + '-' + hour} style={{
                    borderRight: '1px solid var(--border-default)',
                    borderBottom: '1px solid var(--border-default)',
                    height: slotPx * 2 + 1,
                    background: d.isToday ? 'rgba(232,239,233,0.35)' : 'transparent',
                    position: 'relative',
                    padding: 2,
                  }}>
                    {matches.map(r => {
                      const m = r.memberId ? D3.getMember(r.memberId) : null;
                      const offsetMin = r.start % 60;
                      const height = ((r.end - r.start) / 60) * (slotPx * 2) - 4;
                      return (
                        <div
                          key={r.id}
                          onClick={() => m && onOpenMember && onOpenMember(m.id)}
                          className={`calblock type-${r.type}`}
                          style={{
                            position: 'absolute',
                            left: 2, right: 2,
                            top: (offsetMin / 60) * (slotPx * 2) + 2,
                            height,
                            padding: '3px 6px',
                          }}
                        >
                          <div className="name" style={{ fontSize: 11 }}>{m ? m.name : r.prospect || '대여'}</div>
                          <div className="meta" style={{ fontSize: 10 }}>{D3.formatTime(r.start)}</div>
                        </div>
                      );
                    })}
                  </div>
                );
              })}
            </React.Fragment>
          );
        })}
      </div>
    </div>
  );
}

// ============================================================
// Payments
// ============================================================
function Payments({ onOpenMember }) {
  const overdue = D3.payments.filter(p => p.status === 'OVERDUE');
  const dueSoon = D3.payments.filter(p => p.status === 'DUE_SOON');
  const paid    = D3.payments.filter(p => p.status === 'PAID');

  const Section = ({ title, items, tone, hint, empty }) => (
    <UI.Card>
      <UI.CardHeader
        title={title}
        actions={
          <React.Fragment>
            <span className="t-caption">{hint}</span>
            <UI.Badge tone={tone}>{items.length}건</UI.Badge>
          </React.Fragment>
        }
      />
      <UI.CardBody flush>
        {items.length === 0 ? (
          <UI.Empty icon={Icon.Card} title={empty}/>
        ) : (
          <table className="tbl">
            <thead><tr>
              <th>회원</th><th>금액</th><th>예정일</th><th>마지막 알림</th><th>방법</th><th style={{ width: 140 }}></th>
            </tr></thead>
            <tbody>
              {items.map(p => {
                const m = D3.getMember(p.memberId);
                return (
                  <tr key={p.id}>
                    <td className="cell-identity">
                      <div className="row" style={{ gap: 10 }}>
                        <UI.Avatar name={m.name} tone={m.tone} size="md"/>
                        <button onClick={() => onOpenMember(m.id)} className="t-body-strong" style={{ background: 'none', border: 'none', cursor: 'pointer', textAlign: 'left' }}>{m.name}</button>
                      </div>
                    </td>
                    <td data-label="금액" className="t-mono num">{D3.formatMoney(p.amount)}</td>
                    <td data-label="예정일">
                      <div className="col" style={{ gap: 0 }}>
                        <span className="t-mono num">{D3.formatDate(p.dueDate, { short: true })}</span>
                        {p.status === 'OVERDUE'  && <UI.Badge tone="danger" dot>D+{p.daysOverdue}</UI.Badge>}
                        {p.status === 'DUE_SOON' && <UI.Badge tone="warning">D-{p.daysUntil}</UI.Badge>}
                      </div>
                    </td>
                    <td data-label="마지막 알림" className="t-caption">{p.lastReminder ? D3.fmt.relative(p.lastReminder) : '발송 전'}</td>
                    <td data-label="방법">{p.method}</td>
                    <td className="cell-actions" style={{ textAlign: 'right' }}>
                      {p.status === 'PAID' ? (
                        <UI.Badge tone="success" dot>완료 · {D3.fmt.timestamp(p.paidAt)}</UI.Badge>
                      ) : (
                        <div className="row" style={{ justifyContent: 'flex-end', gap: 4 }}>
                          <UI.Button size="sm" variant="ghost" icon={Icon.Mail} onClick={(e) => { e.stopPropagation(); window.toast && window.toast(`${m.name}님께 대여 알림톡 발송`, { tone: 'success' }); }}>알림</UI.Button>
                          <UI.Button size="sm" onClick={(e) => { e.stopPropagation(); window.toast && window.toast(`${m.name}님 대여 결제 완료`, { tone: 'success' }); }}>결제 완료</UI.Button>
                        </div>
                      )}
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        )}
      </UI.CardBody>
    </UI.Card>
  );

  return (
    <React.Fragment>
      <header className="page-header">
        <div>
          <h1 className="page-title">결제</h1>
          <div className="page-sub">D-7 / D-3 / D-0 자동 알림톡 발송 중 · 이번 주 예정 결제 {dueSoon.length + overdue.length}건</div>
        </div>
        <div className="page-actions">
          <UI.Button icon={Icon.Receipt} onClick={() => window.toast && window.toast('11월 정산서 PDF 생성 완료 — 회원별 단가·환불·수수료 함함', { tone: 'success' })}>이번 달 정산</UI.Button>
          <UI.Button icon={Icon.Plus} variant="primary" onClick={() => window.toast && window.toast('결제 수동 등록 모달 — 현금/이체 입금 확인 이력으로 추가')}>결제 수동 등록</UI.Button>
        </div>
      </header>

      {/* Summary */}
      <div className="grid-3" style={{ marginBottom: 16 }}>
        <SumCard label="누락 (입금 미확인)" value={`${overdue.reduce((s,p)=>s+p.amount,0).toLocaleString()}원`} sub={`${overdue.length}건 · 오늘 09:02 알림 발송됨`} tone="danger"/>
        <SumCard label="이번 주 임박" value={`${dueSoon.reduce((s,p)=>s+p.amount,0).toLocaleString()}원`} sub={`${dueSoon.length}건 · D-7 알림 대기`} tone="warning"/>
        <SumCard label="이번 주 입금" value={`${paid.reduce((s,p)=>s+p.amount,0).toLocaleString()}원`} sub={`${paid.length}건 완료`} tone="success"/>
      </div>

      <div className="col" style={{ gap: 16 }}>
        <Section title="결제 누락" items={overdue} tone="danger"
          hint="D+1 이상 미입금" empty="누락된 결제가 없습니다"/>
        <Section title="결제 임박" items={dueSoon} tone="warning"
          hint="D-7 이내 결제 예정" empty="이번 주 임박 결제가 없습니다"/>
        <Section title="최근 입금" items={paid} tone="success" hint="최근 7일" empty="아직 입금이 없습니다"/>
      </div>
    </React.Fragment>
  );
}

function SumCard({ label, value, sub, tone }) {
  const c = tone === 'danger' ? 'var(--status-danger)' : tone === 'warning' ? 'var(--status-warning)' : 'var(--status-success)';
  const bg = tone === 'danger' ? 'var(--status-danger-bg)' : tone === 'warning' ? 'var(--status-warning-bg)' : 'var(--status-success-bg)';
  return (
    <div style={{ background: 'var(--bg-surface)', borderRadius: 'var(--radius-lg)', border: '1px solid var(--border-default)', padding: 20, position: 'relative', overflow: 'hidden' }}>
      <div style={{ position: 'absolute', top: 0, left: 0, width: 4, height: '100%', background: c }}/>
      <div className="t-micro" style={{ marginLeft: 8 }}>{label}</div>
      <div className="num t-mono" style={{ fontSize: 26, fontWeight: 700, marginLeft: 8, marginTop: 4, color: 'var(--text-primary)' }}>{value}</div>
      <div className="t-caption" style={{ marginLeft: 8, fontSize: 12 }}>{sub}</div>
    </div>
  );
}

// ============================================================
// Consultations
// ============================================================
function Consults({ onOpenMember, onOpenRegister }) {
  return (
    <React.Fragment>
      <header className="page-header">
        <div>
          <h1 className="page-title">상담</h1>
          <div className="page-sub">미가입 2명 · 가입 전환율 50% (지난 4건)</div>
        </div>
        <div className="page-actions">
          <UI.Button icon={Icon.Plus} variant="primary" onClick={onOpenRegister}>상담일지 작성</UI.Button>
        </div>
      </header>

      <UI.Card>
        <UI.CardBody flush>
          <table className="tbl">
            <thead><tr>
              <th>상담자</th><th>목표</th><th>음역대</th><th>유입</th><th>일시</th><th>진행</th><th style={{ width: 160 }}></th>
            </tr></thead>
            <tbody>
              {D3.consultations.map(c => (
                <tr key={c.id}>
                  <td className="cell-identity">
                    <div className="row" style={{ gap: 10 }}>
                      <UI.Avatar name={c.prospect} tone={c.joined ? 'tone-a' : 'tone-b'} size="md"/>
                      <div className="col" style={{ gap: 0 }}>
                        <span className="t-body-strong">{c.prospect}</span>
                        <span className="t-caption t-mono num" style={{ fontSize: 12 }}>{c.phone}</span>
                      </div>
                    </div>
                  </td>
                  <td data-label="목표" style={{ maxWidth: 320 }}>{c.goal}</td>
                  <td data-label="음역대" className="t-mono num">{c.range}</td>
                  <td data-label="유입" className="t-caption">{c.source}</td>
                  <td data-label="일시" className="t-mono num t-caption">{D3.fmt.timestamp(c.conductedAt)}</td>
                  <td data-label="진행">{D3.getTeacher(c.teacherId).name} 선생님</td>
                  <td className="cell-actions" style={{ textAlign: 'right' }}>
                    {c.joined
                      ? <UI.Button size="sm" variant="ghost" onClick={() => onOpenMember(c.memberId)}>회원 보기 →</UI.Button>
                      : <div className="row" style={{ justifyContent: 'flex-end', gap: 4 }}>
                          <UI.Button size="sm" icon={Icon.Send} onClick={() => window.toast && window.toast(`${c.prospect}님께 가입 링크 알림톡 발송`, { tone: 'success' })}>가입 링크</UI.Button>
                          <UI.Button size="sm" variant="primary" onClick={onOpenRegister}>회원 등록</UI.Button>
                        </div>}
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </UI.CardBody>
      </UI.Card>
    </React.Fragment>
  );
}

// ============================================================
// Notes (수업일지)
// ============================================================
function Notes({ onOpenMember, onOpenNote }) {
  return (
    <React.Fragment>
      <header className="page-header">
        <div>
          <h1 className="page-title">수업일지</h1>
          <div className="page-sub">최근 7일 작성 8건 · 미작성 3건</div>
        </div>
        <div className="page-actions">
          <UI.Button icon={Icon.Settings} onClick={() => window.toast && window.toast('설정 → 수업일지 양식으로 이동')}>일지 양식 편집</UI.Button>
          <UI.Button icon={Icon.Plus} variant="primary" onClick={() => onOpenNote && onOpenNote('m1')}>새 일지 작성</UI.Button>
        </div>
      </header>

      <div className="grid-2" style={{ gap: 16 }}>
        <UI.Card>
          <UI.CardHeader title="최근 일지" subtitle="회원이 부분 공개로 열람" />
          <UI.CardBody flush>
            <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
              {D3.lessonNotes.map(n => {
                const m = D3.getMember(n.memberId);
                return (
                  <li key={n.id} className="row" style={{ padding: '12px 20px', borderBottom: '1px solid var(--border-default)' }}>
                    <UI.Avatar name={m.name} tone={m.tone} size="md"/>
                    <div className="col" style={{ gap: 0, flex: 1 }}>
                      <span className="t-body-strong">{m.name} · {D3.formatDate(n.date, { short: true })}</span>
                      <span className="t-caption" style={{ whiteSpace: 'nowrap', textOverflow: 'ellipsis', overflow: 'hidden', maxWidth: 320 }}>{n.internalNote || n.memberMessage || '—'}</span>
                    </div>
                    <UI.Button size="sm" variant="ghost" onClick={() => onOpenMember(m.id)}>열기 →</UI.Button>
                  </li>
                );
              })}
              <li className="row" style={{ padding: '12px 20px', borderBottom: '1px solid var(--border-default)' }}>
                <UI.Avatar name="박재현" tone="tone-d" size="md"/>
                <div className="col" style={{ gap: 0, flex: 1 }}>
                  <span className="t-body-strong">박재현 · 11월 18일 (화)</span>
                  <span className="t-caption">호흡 위치 점검, 발성 기초 — 매일 5분 자가 훈련 권장</span>
                </div>
                <UI.Button size="sm" variant="ghost" onClick={() => window.toast && window.toast('이 일지는 회고용 샘플 — 실제 일지는 회원을 선택하세요')}>열기 →</UI.Button>
              </li>
              <li className="row" style={{ padding: '12px 20px' }}>
                <UI.Avatar name="이서윤" tone="tone-b" size="md"/>
                <div className="col" style={{ gap: 0, flex: 1 }}>
                  <span className="t-body-strong">이서윤 · 11월 19일 (수)</span>
                  <span className="t-caption">B&apos;day 새 키 적용 — 다음 시간 후렴 점검</span>
                </div>
                <UI.Button size="sm" variant="ghost" onClick={() => window.toast && window.toast('이 일지는 회고용 샘플 — 실제 일지는 회원을 선택하세요')}>열기 →</UI.Button>
              </li>
            </ul>
          </UI.CardBody>
        </UI.Card>

        <UI.Card>
          <UI.CardHeader title="작성 대기" actions={<UI.Badge tone="warning">3건</UI.Badge>}/>
          <UI.CardBody flush>
            <ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
              {[
                { id: 'm4', name: '최도현', tone: 'tone-e', when: '오늘 13:00 수업' },
                { id: 'm5', name: '김민지', tone: 'tone-a', when: '어제 19:00 수업' },
                { id: 'm5b', name: '정유나', tone: 'tone-c', when: '11월 17일 18:00' },
              ].map((x) => (
                <li key={x.id} className="row" style={{ padding: '12px 20px', borderBottom: '1px solid var(--border-default)' }}>
                  <UI.Avatar name={x.name} tone={x.tone} size="md"/>
                  <div className="col" style={{ gap: 0, flex: 1 }}>
                    <span className="t-body-strong">{x.name}</span>
                    <span className="t-caption">{x.when}</span>
                  </div>
                  <UI.Button size="sm" icon={Icon.Edit} onClick={() => onOpenNote && onOpenNote(x.id.replace('m5b','m5'))}>작성</UI.Button>
                </li>
              ))}
            </ul>
          </UI.CardBody>
        </UI.Card>
      </div>
    </React.Fragment>
  );
}

// ============================================================
// Settings — 일지 양식 편집 (form builder peek)
// ============================================================

// I6 — Space booking policy editor (used inside Settings)
function PracticePolicyEditor({ policy, setP, spaceLabel }) {
  const disabled = !policy.practiceEnabled;
  const [roomHours, setRoomHours] = useState(() => {
    const out = {};
    D3.rooms.forEach(r => { out[r.id] = r.hours ? { ...r.hours } : null; });
    return out;
  });
  function setRoomHour(id, key, value) {
    setRoomHours(rh => ({ ...rh, [id]: { ...(rh[id] || { ...policy.hours }), [key]: value } }));
  }
  function toggleRoomOverride(id, on) {
    setRoomHours(rh => ({ ...rh, [id]: on ? { ...policy.hours } : null }));
  }
  const overrideCount = Object.values(roomHours).filter(h => h).length;
  return (
    <div className="col" style={{ gap: 16 }}>
      {/* Master switch */}
      <div style={{
        padding: 14, borderRadius: 'var(--radius-md)',
        background: policy.practiceEnabled ? 'var(--accent-subtle-bg)' : 'var(--bg-canvas)',
        border: '1px solid var(--border-default)',
      }}>
        <UI.Toggle
          checked={policy.practiceEnabled}
          onChange={(v) => setP('practiceEnabled', v)}
          label="자율 연습 예약 허용"
          hint={`회원·강사가 ${spaceLabel}을(를) 수업 외 용도로 예약할 수 있습니다. 끄면 예약 폼·메뉴에서 자율 연습 옵션이 사라집니다.`}
        />
      </div>

      {/* Who can book */}
      <div className="col" style={{ gap: 8, opacity: disabled ? 0.5 : 1, pointerEvents: disabled ? 'none' : 'auto' }}>
        <span className="t-micro">예약 권한</span>
        <div className="row" style={{ gap: 8, flexWrap: 'wrap' }}>
          <PolicyChipToggle
            checked={policy.memberCanBook}
            onChange={(v) => setP('memberCanBook', v)}
            label="회원이 직접 예약"
            hint="회원 모바일 화면에서 자율 연습 슬롯을 잡을 수 있습니다"
          />
          <PolicyChipToggle
            checked={policy.teacherCanBook}
            onChange={(v) => setP('teacherCanBook', v)}
            label="강사가 직접 예약"
            hint="강사 본인의 연습·발표 준비를 위한 예약"
          />
          <PolicyChipToggle
            checked={policy.requiresApproval}
            onChange={(v) => setP('requiresApproval', v)}
            label="원장 승인 필요"
            hint="예약은 요청 상태로 들어오고, 원장이 승인해야 확정"
          />
        </div>
      </div>

      {/* Time bounds */}
      <div className="col" style={{ gap: 8, opacity: disabled ? 0.5 : 1, pointerEvents: disabled ? 'none' : 'auto' }}>
        <span className="t-micro">시간 한도</span>
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 12 }}>
          <PolicyNumField
            label="사전 예약 가능"
            unit="일 전부터"
            value={policy.advanceDays}
            onChange={(v) => setP('advanceDays', v)}
            min={1} max={60}
          />
          <PolicyNumField
            label="1회 최소"
            unit="분"
            value={policy.minDurationMin}
            onChange={(v) => setP('minDurationMin', v)}
            step={15} min={15} max={240}
          />
          <PolicyNumField
            label="1회 최대"
            unit="분"
            value={policy.maxDurationMin}
            onChange={(v) => setP('maxDurationMin', v)}
            step={15} min={15} max={240}
          />
          <PolicyNumField
            label="회원당 주간"
            unit="회"
            value={policy.perMemberWeeklyMax}
            onChange={(v) => setP('perMemberWeeklyMax', v)}
            min={1} max={14}
          />
        </div>
        <div className="row" style={{ gap: 12, marginTop: 4 }}>
          <div className="field" style={{ flex: 1 }}>
            <label className="field__label">취소 마감</label>
            <div className="row" style={{ gap: 6, alignItems: 'baseline' }}>
              <input className="input t-mono num" style={{ width: 80 }} type="number" min={0} max={48} value={policy.cancelCutoffHrs} onChange={(e) => setP('cancelCutoffHrs', parseInt(e.target.value || '0', 10))}/>
              <span className="t-caption">시간 전까지 무료 취소</span>
            </div>
          </div>
        </div>
      </div>

      {/* Operating hours — default + per-room overrides */}
      <div className="col" style={{ gap: 10, opacity: disabled ? 0.5 : 1, pointerEvents: disabled ? 'none' : 'auto' }}>
        <div className="row" style={{ alignItems: 'baseline', gap: 8 }}>
          <span className="t-micro">운영 시간</span>
          <span className="t-caption" style={{ fontSize: 11 }}>
            기본 운영 시간을 따르고, 필요한 {spaceLabel}만 개별 설정합니다
          </span>
        </div>

        <div className="row" style={{ gap: 12 }}>
          <div className="field" style={{ flex: 1 }}>
            <label className="field__label">기본 시작</label>
            <input className="input t-mono num" value={policy.hours.start} onChange={(e) => setP('hours', { ...policy.hours, start: e.target.value })}/>
          </div>
          <div className="field" style={{ flex: 1 }}>
            <label className="field__label">기본 종료</label>
            <input className="input t-mono num" value={policy.hours.end} onChange={(e) => setP('hours', { ...policy.hours, end: e.target.value })}/>
          </div>
          <div className="field" style={{ flex: 2 }}>
            <label className="field__label">상태</label>
            <div className="t-caption" style={{ fontSize: 12, paddingTop: 6 }}>
              {overrideCount === 0
                ? `모든 ${spaceLabel}이 기본 시간을 따릅니다`
                : <span><strong className="t-mono num" style={{ color: 'var(--text-primary)' }}>{overrideCount}개</strong> {spaceLabel}이 기본 시간과 다르게 설정됨</span>}
            </div>
          </div>
        </div>

        <div className="room-hours">
          <div className="room-hours__head">
            <span style={{ flex: 1 }}>{spaceLabel}</span>
            <span style={{ width: 110 }}>운영 시간</span>
            <span style={{ width: 90, textAlign: 'right' }}>개별 설정</span>
          </div>
          {D3.rooms.map(room => {
            const override = roomHours[room.id];
            const eff = override || policy.hours;
            return (
              <div key={room.id} className="room-hours__row">
                <div style={{ flex: 1, display: 'flex', alignItems: 'baseline', gap: 8 }}>
                  <span style={{ fontWeight: 600 }}>{room.name}</span>
                  <span className="t-caption" style={{ fontSize: 11 }}>최대 {room.capacity}인</span>
                </div>
                {override ? (
                  <div className="row" style={{ gap: 4, width: 'auto' }}>
                    <input
                      className="input t-mono num"
                      style={{ width: 70, height: 28, padding: '0 6px', fontSize: 12 }}
                      value={eff.start}
                      onChange={(e) => setRoomHour(room.id, 'start', e.target.value)}
                    />
                    <span className="t-caption" style={{ fontSize: 12 }}>–</span>
                    <input
                      className="input t-mono num"
                      style={{ width: 70, height: 28, padding: '0 6px', fontSize: 12 }}
                      value={eff.end}
                      onChange={(e) => setRoomHour(room.id, 'end', e.target.value)}
                    />
                  </div>
                ) : (
                  <span className="t-mono num room-hours__inherit" style={{ width: 110 }}>
                    {eff.start} – {eff.end}
                    <span className="room-hours__inherit-tag">기본</span>
                  </span>
                )}
                <div style={{ width: 90, display: 'flex', justifyContent: 'flex-end' }}>
                  <UI.Toggle
                    checked={!!override}
                    onChange={(v) => toggleRoomOverride(room.id, v)}
                  />
                </div>
              </div>
            );
          })}
        </div>
        <div className="t-caption" style={{ fontSize: 11, paddingLeft: 4 }}>
          예시: 일부 {spaceLabel}만 늦은 시간까지 개방하거나, 그룹 클래스룸만 오후 시간대로 제한할 때 활용
        </div>
      </div>

      {/* Cost model */}
      <div className="col" style={{ gap: 8, opacity: disabled ? 0.5 : 1, pointerEvents: disabled ? 'none' : 'auto' }}>
        <span className="t-micro">비용 정책</span>
        <div className="row" style={{ gap: 6 }}>
          {[
            { v: 'free', label: '무료', sub: '회원 혜택' },
            { v: 'pass', label: '패스 차감', sub: '수업권에서 부분 차감' },
            { v: 'paid', label: '유료', sub: '시간당 별도 결제' },
          ].map(o => (
            <button
              key={o.v}
              onClick={() => setP('costModel', o.v)}
              className={`policy-cost ${policy.costModel === o.v ? 'is-on' : ''}`}
            >
              <span className="policy-cost__label">{o.label}</span>
              <span className="policy-cost__sub">{o.sub}</span>
            </button>
          ))}
        </div>
        {policy.costModel === 'pass' && (
          <div className="row" style={{ gap: 12, alignItems: 'baseline', paddingLeft: 4 }}>
            <span className="t-caption">자율 연습 1회 =</span>
            <select
              className="select"
              style={{ width: 110, height: 30 }}
              value={policy.passDeductRatio}
              onChange={(e) => setP('passDeductRatio', parseFloat(e.target.value))}
            >
              <option value={0.25}>0.25회</option>
              <option value={0.5}>0.5회</option>
              <option value={0.75}>0.75회</option>
              <option value={1}>1회 (전액)</option>
            </select>
            <span className="t-caption">패스 차감</span>
          </div>
        )}
        {policy.costModel === 'paid' && (
          <div className="row" style={{ gap: 12, alignItems: 'baseline', paddingLeft: 4 }}>
            <span className="t-caption">1시간당</span>
            <input
              type="number"
              className="input t-mono num"
              style={{ width: 120, height: 30 }}
              value={policy.paidRatePerHour}
              step={1000}
              onChange={(e) => setP('paidRatePerHour', parseInt(e.target.value || '0', 10))}
            />
            <span className="t-caption">원</span>
          </div>
        )}
      </div>

      {/* Summary preview */}
      <div className="policy-summary">
        <span className="t-micro">현재 정책 요약</span>
        <p className="t-caption" style={{ lineHeight: 1.7, margin: 0 }}>
          {!policy.practiceEnabled ? (
            <span>자율 연습 예약 <strong style={{ color: 'var(--status-danger)' }}>비활성</strong> — {spaceLabel}은 수업·상담·대여 용도로만 예약됩니다.</span>
          ) : (
            <span>
              {[policy.memberCanBook && '회원', policy.teacherCanBook && '강사'].filter(Boolean).join('과 ') || '아무도'}이(가)
              {' '}<strong className="t-mono num">{policy.advanceDays}일</strong> 전부터
              {' '}<strong className="t-mono num">{policy.minDurationMin}–{policy.maxDurationMin}분</strong> 단위로
              {' '}예약 가능. 회원당 주 <strong className="t-mono num">{policy.perMemberWeeklyMax}회</strong>.
              {' '}비용은 {policy.costModel === 'free' ? '무료' : policy.costModel === 'pass' ? `패스에서 ${policy.passDeductRatio}회 차감` : `시간당 ${policy.paidRatePerHour.toLocaleString()}원`}.
              {policy.requiresApproval && ' 원장 승인 필요.'}
            </span>
          )}
        </p>
      </div>
    </div>
  );
}

function PolicyChipToggle({ checked, onChange, label, hint }) {
  return (
    <button
      type="button"
      onClick={() => onChange(!checked)}
      className={`policy-chip ${checked ? 'is-on' : ''}`}
    >
      <span className={`policy-chip__box ${checked ? 'is-on' : ''}`}>
        {checked && <Icon.Check size={12}/>}
      </span>
      <span className="col" style={{ gap: 1, alignItems: 'flex-start' }}>
        <span style={{ fontWeight: 600, fontSize: 13 }}>{label}</span>
        <span className="t-caption" style={{ fontSize: 11 }}>{hint}</span>
      </span>
    </button>
  );
}

function PolicyNumField({ label, unit, value, onChange, min = 0, max = 999, step = 1 }) {
  return (
    <div className="field">
      <label className="field__label">{label}</label>
      <div className="row" style={{ gap: 6, alignItems: 'baseline' }}>
        <input
          type="number"
          className="input t-mono num"
          value={value}
          min={min} max={max} step={step}
          onChange={(e) => onChange(parseFloat(e.target.value || '0'))}
          style={{ width: 80, height: 32, textAlign: 'right' }}
        />
        <span className="t-caption" style={{ fontSize: 12 }}>{unit}</span>
      </div>
    </div>
  );
}

// ============================================================
// CampaignCodeManager — UTM 캠페인 코드 CRUD + 광고 링크 빌더
// ============================================================
function CampaignCodeManager({ spaceLabel }) {
  const [codes, setCodes] = useState(() => {
    // seed from D.utmDict.cmp + leads/enrolled aggregates
    const leadsByCmp = {};
    const enrolledByCmp = {};
    D3.consultations.forEach(c => {
      const k = c.utm?.cmp; if (!k) return;
      leadsByCmp[k] = (leadsByCmp[k] || 0) + 1;
      if (c.stage === 'enrolled') enrolledByCmp[k] = (enrolledByCmp[k] || 0) + 1;
    });
    return Object.entries(D3.utmDict?.cmp || {}).map(([code, info]) => ({
      code, ...info,
      leads:    leadsByCmp[code]    || 0,
      enrolled: enrolledByCmp[code] || 0,
      status: info.spend === 0 && info.period.includes('예정') ? 'planned' :
              (info.period.includes('상시') ? 'active' : 'active'),
    }));
  });
  const [draft, setDraft] = useState(null);   // { code, label, desc, periodStart, periodEnd, spend, status }
  const [linkOpenFor, setLinkOpenFor] = useState(null);

  function addNew() {
    setDraft({
      code: '', label: '', desc: '',
      periodStart: '2026-01-01', periodEnd: '',
      spend: 0, status: 'active',
    });
  }
  function edit(c) {
    setDraft({
      code: c.code, label: c.label, desc: c.desc,
      periodStart: '', periodEnd: '', spend: c.spend,
      status: c.status, isEdit: true,
    });
  }
  function saveDraft() {
    if (!draft.code || !draft.label) return;
    const period = draft.periodEnd ? `${draft.periodStart} ~ ${draft.periodEnd}` :
                   (draft.status === 'planned' ? `${draft.periodStart} ~ (예정)` : '상시');
    if (draft.isEdit) {
      setCodes(cs => cs.map(c => c.code === draft.code ? { ...c, label: draft.label, desc: draft.desc, period, spend: draft.spend, status: draft.status } : c));
    } else {
      setCodes(cs => [...cs, { code: draft.code, label: draft.label, desc: draft.desc, period, spend: draft.spend, status: draft.status, leads: 0, enrolled: 0 }]);
    }
    setDraft(null);
  }
  function archive(code) {
    setCodes(cs => cs.map(c => c.code === code ? { ...c, status: 'ended' } : c));
  }

  return (
    <div className="col" style={{ gap: 14 }}>
      <div className="row" style={{ alignItems: 'baseline' }}>
        <span className="t-caption" style={{ fontSize: 12 }}>
          현재 <strong className="t-mono num" style={{ color: 'var(--text-primary)' }}>{codes.filter(c => c.status !== 'ended').length}개</strong> 활성 캠페인
        </span>
        <UI.Button size="sm" variant="primary" icon={Icon.Plus} style={{ marginLeft: 'auto' }} onClick={addNew}>새 코드</UI.Button>
      </div>

      <div className="cmp-table">
        <div className="cmp-table__head">
          <span style={{ flex: '0 0 140px' }}>코드</span>
          <span style={{ flex: 1 }}>이름 / 기간</span>
          <span style={{ flex: '0 0 100px', textAlign: 'right' }}>실적</span>
          <span style={{ flex: '0 0 90px', textAlign: 'right' }}>예산</span>
          <span style={{ flex: '0 0 160px', textAlign: 'right' }}></span>
        </div>
        {codes.map(c => (
          <div key={c.code} className={`cmp-table__row ${c.status === 'ended' ? 'is-ended' : ''}`}>
            <div style={{ flex: '0 0 140px', display: 'flex', alignItems: 'center', gap: 8 }}>
              <span className="t-mono utm-cmp-row__code" style={{ fontSize: 12 }}>{c.code}</span>
              {c.status === 'planned' && <UI.Badge tone="neutral">예정</UI.Badge>}
              {c.status === 'ended'   && <UI.Badge tone="neutral">종료</UI.Badge>}
            </div>
            <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 2, minWidth: 0 }}>
              <span style={{ fontWeight: 600, fontSize: 13 }}>{c.label}</span>
              <span className="t-caption" style={{ fontSize: 11.5 }}>{c.desc}</span>
              <span className="t-mono num" style={{ fontSize: 10.5, color: 'var(--text-tertiary)' }}>{c.period}</span>
            </div>
            <div style={{ flex: '0 0 100px', textAlign: 'right' }}>
              <div className="t-mono num" style={{ fontSize: 13, fontWeight: 600 }}>
                {c.enrolled} <span style={{ color: 'var(--text-tertiary)', fontWeight: 400 }}>/ {c.leads}</span>
              </div>
              <div className="t-caption" style={{ fontSize: 10.5 }}>등록 / 상담</div>
            </div>
            <div style={{ flex: '0 0 90px', textAlign: 'right' }}>
              <div className="t-mono num" style={{ fontSize: 13 }}>
                {c.spend > 0 ? `${(c.spend / 10000).toFixed(0)}만` : '—'}
              </div>
              {c.spend > 0 && c.enrolled > 0 && (
                <div className="t-caption" style={{ fontSize: 10.5 }}>
                  CAC {Math.round(c.spend / c.enrolled / 1000)}천
                </div>
              )}
            </div>
            <div style={{ flex: '0 0 160px', display: 'flex', justifyContent: 'flex-end', gap: 4 }}>
              <UI.Button size="sm" variant="ghost" icon={Icon.Layers} onClick={() => setLinkOpenFor(c)}>링크</UI.Button>
              <UI.Button size="sm" variant="ghost" icon={Icon.Edit} onClick={() => edit(c)}>편집</UI.Button>
              {c.status !== 'ended' && (
                <UI.Button size="sm" variant="ghost" onClick={() => archive(c.code)}>종료</UI.Button>
              )}
            </div>
          </div>
        ))}
      </div>

      <div className="t-caption" style={{ fontSize: 11.5, lineHeight: 1.6 }}>
        ※ <strong>코드</strong>는 영문 소문자 + 하이픈만 사용 (예: <span className="t-mono">autumn-25</span>, <span className="t-mono">vocal-kw</span>).
        구글·인스타·네이버 광고 매니저에서 <span className="t-mono">utm_campaign</span> 값으로 똑같이 입력하면 자동으로 매칭됩니다.
      </div>

      {/* Draft modal */}
      <UI.Modal
        open={!!draft}
        onClose={() => setDraft(null)}
        title={draft?.isEdit ? '캠페인 코드 편집' : '새 캠페인 코드'}
        width={520}
        footer={
          <React.Fragment>
            <UI.Button variant="ghost" onClick={() => setDraft(null)}>취소</UI.Button>
            <UI.Button variant="primary" icon={Icon.Check} onClick={saveDraft}>저장</UI.Button>
          </React.Fragment>
        }
      >
        {draft && (
          <div className="col" style={{ gap: 14 }}>
            <div className="field">
              <label className="field__label">코드 *</label>
              <input
                className="input t-mono"
                value={draft.code}
                disabled={draft.isEdit}
                onChange={(e) => setDraft(d => ({ ...d, code: e.target.value.toLowerCase().replace(/[^a-z0-9-]/g, '') }))}
                placeholder="autumn-25"
              />
              <span className="t-caption" style={{ fontSize: 11, marginTop: 4 }}>영문 소문자 + 숫자 + 하이픈. 저장 후에는 변경 불가.</span>
            </div>
            <div className="field">
              <label className="field__label">이름 *</label>
              <input
                className="input"
                value={draft.label}
                onChange={(e) => setDraft(d => ({ ...d, label: e.target.value }))}
                placeholder="2025 가을 모집"
              />
            </div>
            <div className="field">
              <label className="field__label">설명</label>
              <input
                className="input"
                value={draft.desc}
                onChange={(e) => setDraft(d => ({ ...d, desc: e.target.value }))}
                placeholder="9-11월 신규 회원 모집 캠페인"
              />
            </div>
            <div className="row" style={{ gap: 12 }}>
              <div className="field" style={{ flex: 1 }}>
                <label className="field__label">시작</label>
                <input className="input t-mono num" value={draft.periodStart} onChange={(e) => setDraft(d => ({ ...d, periodStart: e.target.value }))} placeholder="2025-09-01"/>
              </div>
              <div className="field" style={{ flex: 1 }}>
                <label className="field__label">종료 (비우면 상시)</label>
                <input className="input t-mono num" value={draft.periodEnd} onChange={(e) => setDraft(d => ({ ...d, periodEnd: e.target.value }))} placeholder="2025-11-30"/>
              </div>
              <div className="field" style={{ flex: 1 }}>
                <label className="field__label">예산 (원)</label>
                <input type="number" className="input t-mono num" value={draft.spend} onChange={(e) => setDraft(d => ({ ...d, spend: parseInt(e.target.value || '0', 10) }))}/>
              </div>
            </div>
          </div>
        )}
      </UI.Modal>

      {/* Link builder modal */}
      <UI.Modal
        open={!!linkOpenFor}
        onClose={() => setLinkOpenFor(null)}
        title={`광고 링크 만들기 — ${linkOpenFor?.label || ''}`}
        width={560}
        footer={<UI.Button onClick={() => setLinkOpenFor(null)}>닫기</UI.Button>}
      >
        {linkOpenFor && <LinkBuilder cmp={linkOpenFor}/>}
      </UI.Modal>
    </div>
  );
}

function LinkBuilder({ cmp }) {
  const [src, setSrc] = useState('instagram');
  const [med, setMed] = useState('feed');
  const baseUrl = 'https://lessonhigh.com/c/송유이';
  const url = `${baseUrl}?utm_source=${src}&utm_medium=${med}&utm_campaign=${cmp.code}`;
  const [copied, setCopied] = useState(false);

  const sources = Object.keys(D3.utmDict?.src || { instagram: '', naver: '', referral: '', offline: '' });
  const mediums = Object.keys(D3.utmDict?.med || { feed: '', reels: '', story: '', search: '', flyer: '', word: '' });

  function copy() {
    navigator.clipboard?.writeText(url);
    setCopied(true);
    setTimeout(() => setCopied(false), 1600);
  }

  return (
    <div className="col" style={{ gap: 14 }}>
      <div className="row" style={{ gap: 12 }}>
        <div className="field" style={{ flex: 1 }}>
          <label className="field__label">유입 채널 (utm_source)</label>
          <select className="select" value={src} onChange={(e) => setSrc(e.target.value)}>
            {sources.map(s => <option key={s} value={s}>{D3.utmDict?.src?.[s] || s} ({s})</option>)}
          </select>
        </div>
        <div className="field" style={{ flex: 1 }}>
          <label className="field__label">광고 형식 (utm_medium)</label>
          <select className="select" value={med} onChange={(e) => setMed(e.target.value)}>
            {mediums.map(m => <option key={m} value={m}>{D3.utmDict?.med?.[m] || m} ({m})</option>)}
          </select>
        </div>
      </div>

      <div className="field">
        <label className="field__label">완성된 광고 링크</label>
        <div style={{
          background: 'var(--bg-canvas)', border: '1px solid var(--border-default)',
          borderRadius: 'var(--radius-md)', padding: '10px 12px',
          fontFamily: 'var(--font-mono)', fontSize: 12,
          wordBreak: 'break-all', lineHeight: 1.6,
        }}>{url}</div>
        <div className="row" style={{ marginTop: 8 }}>
          <UI.Button size="sm" variant="primary" icon={Icon.Layers} onClick={copy}>{copied ? '복사됨!' : '링크 복사'}</UI.Button>
          <UI.Button size="sm" variant="ghost" onClick={() => window.toast && window.toast('QR 코드 다운로드 — PNG 파일이 저장 폴더에 생성됨', { tone: 'success' })}>QR 코드</UI.Button>
        </div>
      </div>

      <div className="policy-summary">
        <span className="t-micro">사용 방법</span>
        <p className="t-caption" style={{ lineHeight: 1.7, margin: 0 }}>
          이 링크를 인스타·네이버 광고 매니저의 <strong>"이동 URL"</strong>에 붙이세요.
          누군가 클릭해 상담 폼을 작성하면 <span className="t-mono">{cmp.code}</span> 캠페인의 상담으로 자동 집계됩니다.
        </p>
      </div>
    </div>
  );
}


function Settings({ spaceLabel }) {
  const [fields, setFields] = useState(() => D3.settings.lessonFormFields.map(f => ({ ...f })));
  // NOTE: 이 Settings 는 pages-settings.jsx 에 의해 overwrite 됨 (dead code).
  const [shareDefault, setShareDefault] = useState(false);
  // I6 — space booking policy (mutable demo)
  const [policy, setPolicy] = useState(() => ({ ...D3.spacePolicy }));
  const setP = (k, v) => setPolicy(p => ({ ...p, [k]: v }));

  return (
    <React.Fragment>
      <header className="page-header">
        <div>
          <h1 className="page-title">학원 설정</h1>
          <div className="page-sub">일지 양식 · 공간 라벨 · 결제 정책</div>
        </div>
        <div className="page-actions">
          <UI.Button onClick={() => window.toast && window.toast('변경 사항 취소됨', { tone: 'warning' })}>변경 사항 취소</UI.Button>
          <UI.Button variant="primary" icon={Icon.Check} onClick={() => window.toast && window.toast('설정이 저장되었습니다', { tone: 'success' })}>저장</UI.Button>
        </div>
      </header>

      <div className="settings-layout" style={{ display: 'grid', gridTemplateColumns: '220px 1fr', gap: 24 }}>
        {/* Side nav */}
        <nav className="col" style={{ gap: 2 }}>
          {[
            { id: 's1', label: '기본 정보', active: false },
            { id: 's2', label: '공간 라벨', active: false },
            { id: 's3', label: '결제 정책', active: false },
            { id: 's4', label: '수업일지 양식', active: false },
            { id: 's4b', label: '자유 메모 공유', active: false },
            { id: 's4c', label: `${spaceLabel} 예약 정책`, active: false },
            { id: 's4d', label: '캠페인 코드', active: true },
            { id: 's5', label: '상담일지 양식', active: false },
            { id: 's6', label: '알림톡 템플릿', active: false },
            { id: 's7', label: '선생님 관리', active: false },
            { id: 's8', label: '접근 링크 정책', active: false },
          ].map(s => (
            <button key={s.id}
              className={`side-item ${s.active ? 'is-active' : ''}`}
              style={{ paddingLeft: 12 }}
            >{s.label}</button>
          ))}
        </nav>

        <div className="col" style={{ gap: 16 }}>
          <UI.Card>
            <UI.CardHeader title="수업일지 정형 필드" subtitle="모든 선생님의 일지 작성 화면에 적용"/>
            <UI.CardBody>
              <div className="col" style={{ gap: 8 }}>
                {fields.map((f, i) => (
                  <div key={f.id} className="row" style={{
                    background: 'var(--bg-canvas)', border: '1px solid var(--border-default)',
                    borderRadius: 'var(--radius-md)', padding: '8px 12px', gap: 12,
                  }}>
                    <span style={{ color: 'var(--text-tertiary)', cursor: 'grab', fontSize: 16 }}>⋮⋮</span>
                    <input className="input" style={{ flex: 1, height: 30 }} defaultValue={f.label}/>
                    <select className="select" style={{ width: 130, height: 30 }} defaultValue={f.type}>
                      <option value="text">짧은 텍스트</option>
                      <option value="longtext">긴 텍스트</option>
                      <option value="select">단일 선택</option>
                      <option value="multi">다중 선택</option>
                      <option value="number">숫자</option>
                    </select>
                    <label className="row" style={{ gap: 6, fontSize: 13, color: 'var(--text-secondary)' }}>
                      <input type="checkbox" defaultChecked={f.required}/>필수
                    </label>
                    <UI.Button size="sm" isIcon icon={Icon.X} variant="ghost"/>
                  </div>
                ))}
                <UI.Button icon={Icon.Plus} variant="ghost" onClick={() => window.toast && window.toast('새 필드 추가 — 이름·타입 설정 모달')}>필드 추가</UI.Button>
              </div>
            </UI.CardBody>
          </UI.Card>

          <UI.Card>
            <UI.CardHeader title="자유 메모 공유 초기값" subtitle="신규 수업일지 생성 시 토글 초기값"/>
            <UI.CardBody>
              <div style={{ padding: 14, background: shareDefault ? 'var(--accent-subtle-bg)' : 'var(--bg-canvas)', border: '1px solid var(--border-default)', borderRadius: 'var(--radius-md)' }}>
                <UI.Toggle
                  checked={shareDefault}
                  onChange={setShareDefault}
                  label="자유 메모를 회원 화면에 노출"
                  hint="강사가 매 일지마다 토글하는 부담을 줄입니다. 강사가 일지 작성 화면에서 개별로 OFF 가능."
                />
              </div>
              <div className="t-caption" style={{ marginTop: 8, fontSize: 12 }}>
                ※ "자유 메모"는 강사의 내부 메모 영역입니다. ON 시 회원 모바일 화면(회원 본인·추가 알림 수신자 모두)에 노출됩니다.
              </div>
            </UI.CardBody>
          </UI.Card>

          <UI.Card>
            <UI.CardHeader title="공간 라벨"/>
            <UI.CardBody>
              <div className="row" style={{ gap: 12 }}>
                <div className="field" style={{ flex: 1 }}>
                  <label className="field__label">단수형</label>
                  <input className="input" defaultValue="연습실"/>
                </div>
                <div className="field" style={{ flex: 1 }}>
                  <label className="field__label">복수형</label>
                  <input className="input" defaultValue="연습실"/>
                </div>
                <div className="field" style={{ flex: 1 }}>
                  <label className="field__label">대여 가능</label>
                  <select className="select" defaultValue="yes">
                    <option value="yes">예 · {spaceLabel} 대여 메뉴 표시</option>
                    <option value="no">아니오</option>
                  </select>
                </div>
              </div>
              <div className="t-caption" style={{ marginTop: 8 }}>모든 메뉴·헤더·버튼·알림 텍스트에 즉시 반영됩니다.</div>
            </UI.CardBody>
          </UI.Card>

          <UI.Card>
            <UI.CardHeader title={`${spaceLabel} 예약 정책`} subtitle="수업·대여 외에 자율 연습·강사 사용 등 어떤 예약을 허용할지 설정"/>
            <UI.CardBody>
              <PracticePolicyEditor policy={policy} setP={setP} spaceLabel={spaceLabel}/>
            </UI.CardBody>
          </UI.Card>

          <UI.Card>
            <UI.CardHeader
              title="캠페인 코드"
              subtitle="광고 링크에 붙는 UTM 식별자를 학원장이 직접 관리"
            />
            <UI.CardBody>
              <CampaignCodeManager spaceLabel={spaceLabel}/>
            </UI.CardBody>
          </UI.Card>

          <UI.Card>
            <UI.CardHeader title="프리뷰" subtitle="실제 작성 화면"/>
            <UI.CardBody>
              <div className="col" style={{ gap: 14, maxWidth: 520 }}>
                {fields.map(f => (
                  <div key={f.id} className="field">
                    <label className="field__label">
                      {f.label}
                      {f.required && <span style={{ color: 'var(--status-danger)', marginLeft: 4 }}>*</span>}
                    </label>
                    {f.type === 'longtext' ? (
                      <textarea className="textarea" rows={3} placeholder={f.placeholder || ''}/>
                    ) : f.type === 'select' ? (
                      <select className="select" defaultValue="">
                        <option value="">선택…</option>
                        {(f.options || []).map(o => <option key={o} value={o}>{o}</option>)}
                      </select>
                    ) : (
                      <input className="input" placeholder={f.placeholder || ''}/>
                    )}
                    {f.hint && <div className="t-caption" style={{ fontSize: 11, marginTop: 4 }}>{f.hint}</div>}
                  </div>
                ))}
              </div>
            </UI.CardBody>
          </UI.Card>
        </div>
      </div>
    </React.Fragment>
  );
}

window.AdminPages2 = { Spaces, Payments, Consults, Notes, Settings, PracticePolicyEditor, CampaignCodeManager, PolicyChipToggle, PolicyNumField };
})();
