/* global React, Icon, UI, LessonHighData */
// ============================================================
// I7 · 공지 발송 — Announcements page + compose modal
//   학원장이 직접 작성하는 단체 공지 (시스템 트리거 알림과 별개)
//   - 채널: SMS / 친구톡 (알림톡은 트리거 전용)
//   - 대상: 전체 / 그룹 (상태·강사·반) / 개별
//   - 즉시 발송 / 예약 발송
// ============================================================
(function () {
const { useState, useMemo } = React;
const D = LessonHighData;

const CHANNEL_META = {
  sms:    { label: 'SMS',     hint: '일반 문자 · 모든 회원 수신 가능',     locked: false },
  friend: { label: '친구톡',  hint: '카카오 채널 친구 추가한 회원만',       locked: false },
  kakao:  { label: '알림톡',  hint: '트리거 전용 (등하원·결제 등) · 공지엔 사용 불가', locked: true },
};

const STATUS_META = {
  draft:     { label: '초안',     tone: 'neutral'  },
  scheduled: { label: '예약',     tone: 'warning'  },
  sending:   { label: '발송중',   tone: 'accent'   },
  sent:      { label: '발송 완료', tone: 'success'  },
  cancelled: { label: '취소',     tone: 'neutral'  },
};

// ────────────────────────────────────────────────────────────
// Main page
// ────────────────────────────────────────────────────────────
function AnnouncementsPage({ onOpenMember }) {
  const [items, setItems]       = useState(() => [...D.announcements]);
  const [filter, setFilter]     = useState('all');
  const [composing, setComposing] = useState(false);
  const [editingId, setEditingId] = useState(null);
  const editing = editingId ? items.find(i => i.id === editingId) : null;

  const stats = useMemo(() => {
    const sent      = items.filter(a => a.status === 'sent').length;
    const scheduled = items.filter(a => a.status === 'scheduled').length;
    const drafts    = items.filter(a => a.status === 'draft').length;
    const cost      = items.filter(a => a.stats).reduce((s, a) => s + (a.stats?.costKRW || 0), 0);
    const totalSent = items.filter(a => a.stats).reduce((s, a) => s + (a.stats?.sent || 0), 0);
    const totalRead = items.filter(a => a.stats).reduce((s, a) => s + (a.stats?.read || 0), 0);
    const readRate  = totalSent ? Math.round(totalRead / totalSent * 100) : null;
    return { sent, scheduled, drafts, cost, readRate, totalSent };
  }, [items]);

  const filterTabs = [
    { id: 'all',       label: '전체',       count: items.length },
    { id: 'sent',      label: '발송 완료',  count: stats.sent },
    { id: 'scheduled', label: '예약',       count: stats.scheduled },
    { id: 'draft',     label: '초안',       count: stats.drafts },
  ];
  const visible = filter === 'all' ? items : items.filter(a => a.status === filter);
  // Sort: scheduled first, then by createdAt desc
  const sorted = [...visible].sort((a, b) => {
    const order = { scheduled: 0, draft: 1, sending: 2, sent: 3, cancelled: 4 };
    if (order[a.status] !== order[b.status]) return order[a.status] - order[b.status];
    const aDate = a.scheduledAt || a.sentAt || a.createdAt;
    const bDate = b.scheduledAt || b.sentAt || b.createdAt;
    return bDate.localeCompare(aDate);
  });

  const handleSave = (data) => {
    if (editingId) {
      setItems(prev => prev.map(a => a.id === editingId ? { ...a, ...data } : a));
    } else {
      const id = 'an' + Date.now();
      setItems(prev => [{ ...data, id, authorId: 't1', createdAt: nowStr() }, ...prev]);
    }
    setComposing(false);
    setEditingId(null);
  };

  return (
    <React.Fragment>
      <header className="page-header">
        <div>
          <h1 className="page-title">공지</h1>
          <div className="page-sub">
            이번 달 {stats.sent}건 발송 · 예약 {stats.scheduled}건 · 초안 {stats.drafts}건
            <span className="ann-cap"> · 월 한도 {D.announcementPolicy.perMonthCap}건 중 {stats.sent}건 사용</span>
          </div>
        </div>
        <div className="page-actions">
          <UI.Button icon={Icon.Receipt} variant="ghost">발송 로그 내보내기</UI.Button>
          <UI.Button icon={Icon.Plus} variant="primary" onClick={() => { setEditingId(null); setComposing(true); }}>새 공지</UI.Button>
        </div>
      </header>

      {/* KPI cards */}
      <div className="grid-4" style={{ marginBottom: 16 }}>
        <AnnKpi
          label="이번 달 발송"
          value={stats.sent}
          sub={stats.readRate != null ? `평균 읽음 ${stats.readRate}%` : '발송 전'}
          tone="accent"
        />
        <AnnKpi
          label="예약 대기"
          value={stats.scheduled}
          sub={
            stats.scheduled > 0
              ? `다음 ${shortWhen(sorted.find(s => s.status === 'scheduled')?.scheduledAt)}`
              : '예약된 공지 없음'
          }
          tone="warning"
        />
        <AnnKpi
          label="이번 달 비용"
          value={`${stats.cost.toLocaleString()}원`}
          sub={`발송 ${stats.totalSent}건 × 평균 ${stats.totalSent ? Math.round(stats.cost / stats.totalSent) : 0}원`}
          tone="neutral"
        />
        <AnnKpi
          label="월 한도"
          value={`${stats.sent} / ${D.announcementPolicy.perMonthCap}`}
          sub={stats.sent / D.announcementPolicy.perMonthCap > 0.7 ? '한도 임박' : '여유 있음'}
          tone={stats.sent / D.announcementPolicy.perMonthCap > 0.7 ? 'warning' : 'success'}
        />
      </div>

      {/* Channel policy banner */}
      <div className="ann-banner">
        <span className="ann-banner__dot"/>
        <span>
          공지 채널 디폴트는 <strong>SMS</strong> · 카카오 채널 친구 추가한 회원에겐 <strong>친구톡</strong>이 더 저렴 (40원 → 15원)
        </span>
        <span style={{ marginLeft: 'auto' }} className="t-caption">
          알림톡은 사전 승인된 템플릿만 사용 가능 — 공지엔 부적합. <a style={{ color: 'var(--accent-default)' }}>설정 / 알림 템플릿</a>
        </span>
      </div>

      {/* List */}
      <UI.Card>
        <UI.CardHeader
          title="공지 리스트"
          actions={
            <div className="row" style={{ background: 'var(--bg-subtle)', padding: 2, borderRadius: 'var(--radius-md)', gap: 0 }}>
              {filterTabs.map(t => (
                <button
                  key={t.id}
                  className={`btn is-sm ${filter === t.id ? 'is-primary' : 'is-ghost'}`}
                  style={{ borderColor: 'transparent', height: 28 }}
                  onClick={() => setFilter(t.id)}
                >{t.label} {t.count}</button>
              ))}
            </div>
          }
        />
        <UI.CardBody flush>
          {sorted.length === 0 ? (
            <UI.Empty icon={Icon.Bell} title="공지가 없습니다" hint="새 공지를 작성해서 회원·학부모에게 안내해보세요"/>
          ) : (
            <table className="tbl ann-tbl">
              <thead><tr>
                <th>제목 / 카테고리</th>
                <th>대상</th>
                <th>채널</th>
                <th>발송 / 예약</th>
                <th>읽음 / 상태</th>
                <th style={{ width: 100 }}></th>
              </tr></thead>
              <tbody>
                {sorted.map(a => (
                  <AnnRow
                    key={a.id}
                    a={a}
                    onEdit={() => { setEditingId(a.id); setComposing(true); }}
                    onSendNow={() => setItems(prev => prev.map(x => x.id === a.id ? { ...x, status: 'sent', sentAt: nowStr(), stats: simulateStats(x) } : x))}
                    onCancel={() => setItems(prev => prev.map(x => x.id === a.id ? { ...x, status: 'cancelled' } : x))}
                  />
                ))}
              </tbody>
            </table>
          )}
        </UI.CardBody>
      </UI.Card>

      {/* Compose / edit modal */}
      <ComposeAnnouncementModal
        open={composing}
        editing={editing}
        onClose={() => { setComposing(false); setEditingId(null); }}
        onSave={handleSave}
      />
    </React.Fragment>
  );
}

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

// ────────────────────────────────────────────────────────────
// Row
// ────────────────────────────────────────────────────────────
function AnnRow({ a, onEdit, onSendNow, onCancel }) {
  const cat = D.getAnnouncementCategory(a.category);
  const status = STATUS_META[a.status];
  const audienceText = audienceLabel(a.audience);
  const recipientText =
    a.audience.recipients === 'both'    ? '회원+학부모'
    : a.audience.recipients === 'guardian' ? '학부모만'
    : '회원만';
  const ch = CHANNEL_META[a.channel] || CHANNEL_META.sms;

  return (
    <tr>
      <td>
        <div className="col" style={{ gap: 4 }}>
          <span className="t-body-strong" style={{ fontWeight: 600 }}>{a.title}</span>
          <div className="row" style={{ gap: 6, alignItems: 'center' }}>
            <span className={`ann-cat-chip ann-cat-${a.category}`}>{cat?.label}</span>
            <span className="t-caption" style={{ fontSize: 11 }}>
              {a.body.length > 50 ? a.body.slice(0, 50).replace(/\n/g, ' ') + '…' : a.body.replace(/\n/g, ' ')}
            </span>
          </div>
        </div>
      </td>
      <td>
        <div className="col" style={{ gap: 0 }}>
          <span className="t-body-strong t-mono num" style={{ fontSize: 13 }}>{(a.audience.actualCount ?? a.audience.estimatedCount).toLocaleString()}명</span>
          <span className="t-caption" style={{ fontSize: 11 }}>{audienceText} · {recipientText}</span>
        </div>
      </td>
      <td>
        <span className={`ann-channel ann-channel-${a.channel}`}>{ch.label}</span>
      </td>
      <td>
        {a.status === 'sent' && (
          <div className="col" style={{ gap: 0 }}>
            <span className="t-mono num" style={{ fontSize: 12 }}>{D.fmt.timestamp(a.sentAt)}</span>
            <span className="t-caption" style={{ fontSize: 11 }}>발송 완료</span>
          </div>
        )}
        {a.status === 'scheduled' && (
          <div className="col" style={{ gap: 0 }}>
            <span className="t-mono num" style={{ fontSize: 12, color: 'var(--status-warning)' }}>{D.fmt.timestamp(a.scheduledAt)}</span>
            <span className="t-caption" style={{ fontSize: 11 }}>예약됨 · 자동 발송</span>
          </div>
        )}
        {a.status === 'draft' && (
          <span className="t-caption" style={{ fontSize: 11 }}>{shortWhen(a.createdAt)} 작성</span>
        )}
        {a.status === 'cancelled' && (
          <span className="t-caption" style={{ fontSize: 11 }}>취소됨</span>
        )}
      </td>
      <td>
        {a.stats ? (
          <div className="col" style={{ gap: 2 }}>
            <div className="row" style={{ gap: 4, alignItems: 'baseline' }}>
              <span className="t-mono num" style={{ fontSize: 13, fontWeight: 600 }}>
                {a.stats.read}/{a.stats.sent}
              </span>
              <span className="t-caption" style={{ fontSize: 11 }}>읽음</span>
            </div>
            <div className="ann-read-bar">
              <div className="ann-read-bar__fill" style={{ width: `${Math.round(a.stats.read / a.stats.sent * 100)}%` }}/>
            </div>
            {a.stats.failed > 0 && (
              <span className="t-caption" style={{ fontSize: 10.5, color: 'var(--status-danger)' }}>실패 {a.stats.failed}</span>
            )}
          </div>
        ) : (
          <UI.Badge tone={status.tone}>{status.label}</UI.Badge>
        )}
      </td>
      <td className="cell-actions" style={{ textAlign: 'right' }}>
        <div className="row" style={{ justifyContent: 'flex-end', gap: 4 }}>
          {a.status === 'draft' && (
            <React.Fragment>
              <UI.Button size="sm" variant="ghost" onClick={onEdit}>편집</UI.Button>
              <UI.Button size="sm" variant="primary" icon={Icon.Send} onClick={onSendNow}>발송</UI.Button>
            </React.Fragment>
          )}
          {a.status === 'scheduled' && (
            <React.Fragment>
              <UI.Button size="sm" variant="ghost" onClick={onCancel}>취소</UI.Button>
              <UI.Button size="sm" variant="ghost" onClick={onEdit}>편집</UI.Button>
            </React.Fragment>
          )}
          {a.status === 'sent' && (
            <UI.Button size="sm" variant="ghost">상세</UI.Button>
          )}
        </div>
      </td>
    </tr>
  );
}

// ────────────────────────────────────────────────────────────
// Compose modal
// ────────────────────────────────────────────────────────────
function ComposeAnnouncementModal({ open, editing, onClose, onSave }) {
  const isEdit = !!editing;
  const [title, setTitle]       = useState('');
  const [body, setBody]         = useState('');
  const [category, setCategory] = useState('etc');
  const [audKind, setAudKind]   = useState('all');
  const [audStatus, setAudStatus]     = useState(['ACTIVE']);
  const [audTeachers, setAudTeachers] = useState([]);
  const [audPassType, setAudPassType] = useState([]);
  const [audMemberIds, setAudMemberIds] = useState([]);
  const [recipients, setRecipients] = useState('both');
  const [channel, setChannel]   = useState('sms');
  const [schedKind, setSchedKind] = useState('now'); // 'now' | 'later'
  const [schedAt, setSchedAt]   = useState(suggestNextSendTime());

  // Initialize on open with editing or defaults
  React.useEffect(() => {
    if (!open) return;
    if (editing) {
      setTitle(editing.title);
      setBody(editing.body);
      setCategory(editing.category);
      setAudKind(editing.audience.kind);
      setAudStatus(editing.audience.filters?.status || ['ACTIVE']);
      setAudTeachers(editing.audience.filters?.teacherIds || []);
      setAudPassType(editing.audience.filters?.passType || []);
      setAudMemberIds(editing.audience.memberIds || []);
      setRecipients(editing.audience.recipients);
      setChannel(editing.channel);
      setSchedKind(editing.scheduledAt ? 'later' : 'now');
      setSchedAt(editing.scheduledAt || suggestNextSendTime());
    } else {
      setTitle(''); setBody(''); setCategory('etc');
      setAudKind('all'); setAudStatus(['ACTIVE']);
      setAudTeachers([]); setAudPassType([]); setAudMemberIds([]);
      setRecipients(D.announcementPolicy.audienceDefaults.recipients);
      setChannel(D.announcementPolicy.defaultChannel);
      setSchedKind('now'); setSchedAt(suggestNextSendTime());
    }
  }, [open, editing]);

  // Audience estimate (live)
  const audienceFilter = useMemo(() => {
    if (audKind === 'all') return { status: ['ACTIVE'] };
    if (audKind === 'individual') return { /* memberIds handled below */ };
    return {
      status: audStatus.length ? audStatus : ['ACTIVE'],
      teacherIds: audTeachers,
      passType: audPassType,
    };
  }, [audKind, audStatus, audTeachers, audPassType]);

  const estimatedCount = useMemo(() => {
    if (audKind === 'individual') {
      return D.estimateAudience({ memberIds: audMemberIds }, recipients);
    }
    return D.estimateAudience(audienceFilter, recipients);
  }, [audKind, audMemberIds, audienceFilter, recipients]);

  const estimatedCost = useMemo(() => {
    return D.estimateCost(estimatedCount, channel, body.length);
  }, [estimatedCount, channel, body]);

  const canSave = title.trim().length > 0 && body.trim().length > 0 && estimatedCount > 0;

  function buildPayload(status) {
    return {
      title: title.trim(),
      body:  body.trim(),
      category,
      scheduledAt: schedKind === 'later' ? schedAt : null,
      sentAt:      status === 'sent' ? nowStr() : null,
      status,
      audience: {
        kind: audKind,
        filters: audKind === 'individual'
          ? {}
          : (audKind === 'all'
              ? { status: ['ACTIVE'] }
              : { status: audStatus, teacherIds: audTeachers, passType: audPassType }),
        memberIds: audKind === 'individual' ? audMemberIds : undefined,
        recipients,
        estimatedCount,
      },
      channel,
      stats: status === 'sent' ? simulateStats({ audience: { estimatedCount } }) : null,
    };
  }

  return (
    <UI.Modal
      open={open}
      onClose={onClose}
      title={isEdit ? '공지 편집' : '새 공지 작성'}
      width={920}
      footer={
        <React.Fragment>
          <UI.Button variant="ghost" onClick={onClose}>취소</UI.Button>
          <div style={{ flex: 1 }}/>
          <UI.Button variant="ghost" icon={Icon.Note} onClick={() => onSave(buildPayload('draft'))}>초안 저장</UI.Button>
          {schedKind === 'later' ? (
            <UI.Button
              variant="primary"
              icon={Icon.Clock}
              onClick={() => canSave && onSave(buildPayload('scheduled'))}
            >
              예약 발송 · {shortWhen(schedAt)}
            </UI.Button>
          ) : (
            <UI.Button
              variant="primary"
              icon={Icon.Send}
              onClick={() => canSave && onSave(buildPayload('sent'))}
            >
              지금 발송 ({estimatedCount}명)
            </UI.Button>
          )}
        </React.Fragment>
      }
    >
      <div className="ann-compose">
        {/* Left — form */}
        <div className="ann-compose__form">
          {/* Category chips */}
          <FormBlock label="카테고리">
            <div className="row" style={{ gap: 6, flexWrap: 'wrap' }}>
              {D.announcementCategories.map(c => (
                <button
                  key={c.id}
                  className={`ann-cat-chip ann-cat-${c.id} ${category === c.id ? 'is-on' : ''}`}
                  onClick={() => setCategory(c.id)}
                  style={{ cursor: 'pointer', border: 'none', font: 'inherit' }}
                  title={c.hint}
                >{c.label}</button>
              ))}
            </div>
          </FormBlock>

          {/* Title + body */}
          <FormBlock label="제목" required>
            <input
              className="input"
              value={title}
              onChange={e => setTitle(e.target.value)}
              placeholder="예: 11월 24일(월) 학원 임시 휴원 안내"
              maxLength={60}
              style={{ fontSize: 14, fontWeight: 600 }}
            />
            <div className="t-caption" style={{ fontSize: 11, textAlign: 'right', marginTop: 2 }}>{title.length} / 60</div>
          </FormBlock>

          <FormBlock label="본문" required hint="줄바꿈 가능. 90자를 넘기면 LMS로 자동 분류 (40원→80원)">
            <textarea
              className="input"
              value={body}
              onChange={e => setBody(e.target.value)}
              placeholder="공지 내용을 입력하세요…"
              rows={6}
              style={{ fontFamily: 'var(--font-sans)', fontSize: 13.5, lineHeight: 1.6, resize: 'vertical', minHeight: 120 }}
              maxLength={1000}
            />
            <div className="row" style={{ justifyContent: 'space-between', marginTop: 2 }}>
              <span className="t-caption" style={{ fontSize: 11 }}>
                {body.length > 90 ? (
                  <span style={{ color: 'var(--status-warning)' }}>● LMS · 장문</span>
                ) : (
                  <span>● SMS · 단문</span>
                )}
              </span>
              <span className="t-caption" style={{ fontSize: 11 }}>{body.length} / 1000</span>
            </div>
          </FormBlock>

          {/* Audience */}
          <FormBlock label="대상" required>
            <div className="ann-audkind">
              {[
                { id: 'all',         label: '전체 회원' },
                { id: 'group',       label: '그룹 (필터)' },
                { id: 'individual',  label: '개별 선택' },
              ].map(k => (
                <button
                  key={k.id}
                  className={`ann-audkind__btn ${audKind === k.id ? 'is-on' : ''}`}
                  onClick={() => setAudKind(k.id)}
                >{k.label}</button>
              ))}
            </div>
            {audKind === 'group' && (
              <div className="col" style={{ gap: 10, marginTop: 12 }}>
                <FilterChipGroup
                  label="회원 상태"
                  options={[
                    { id: 'ACTIVE',    label: '재원중' },
                    { id: 'HOLD',      label: '휴원중' },
                    { id: 'WITHDRAWN', label: '퇴원' },
                  ]}
                  selected={audStatus}
                  onChange={setAudStatus}
                />
                <FilterChipGroup
                  label="담당 강사"
                  options={D.teachers.map(t => ({ id: t.id, label: t.name }))}
                  selected={audTeachers}
                  onChange={setAudTeachers}
                />
                <FilterChipGroup
                  label="반 / 패스 유형"
                  options={D.passTypes.map(p => ({ id: p.id, label: p.label }))}
                  selected={audPassType}
                  onChange={setAudPassType}
                />
              </div>
            )}
            {audKind === 'individual' && (
              <div className="ann-indiv" style={{ marginTop: 10 }}>
                <div className="t-caption" style={{ fontSize: 11, marginBottom: 6 }}>회원 다중 선택 — 클릭하여 추가/제거</div>
                <div className="row" style={{ flexWrap: 'wrap', gap: 4 }}>
                  {D.members.filter(m => m.status === 'ACTIVE').slice(0, 16).map(m => (
                    <button
                      key={m.id}
                      className={`ann-indiv__chip ${audMemberIds.includes(m.id) ? 'is-on' : ''}`}
                      onClick={() => setAudMemberIds(prev =>
                        prev.includes(m.id) ? prev.filter(x => x !== m.id) : [...prev, m.id]
                      )}
                    >
                      {m.name}
                      {audMemberIds.includes(m.id) && <Icon.X size={10}/>}
                    </button>
                  ))}
                  {D.members.filter(m => m.status === 'ACTIVE').length > 16 && (
                    <span className="t-caption" style={{ fontSize: 11, padding: '4px 8px' }}>
                      ... 외 {D.members.filter(m => m.status === 'ACTIVE').length - 16}명
                    </span>
                  )}
                </div>
              </div>
            )}
          </FormBlock>

          {/* Recipients */}
          <FormBlock label="수신자" hint="회원 본인 + 학부모 양쪽에 모두 보내려면 '회원+학부모'">
            <div className="ann-audkind">
              {[
                { id: 'member',   label: '회원만' },
                { id: 'guardian', label: '학부모만' },
                { id: 'both',     label: '회원+학부모' },
              ].map(r => (
                <button
                  key={r.id}
                  className={`ann-audkind__btn ${recipients === r.id ? 'is-on' : ''}`}
                  onClick={() => setRecipients(r.id)}
                >{r.label}</button>
              ))}
            </div>
          </FormBlock>

          {/* Channel */}
          <FormBlock label="발송 채널" hint="알림톡은 트리거 전용 — 공지엔 SMS / 친구톡">
            <div className="ann-ch-grid">
              {['sms', 'friend'].map(c => (
                <button
                  key={c}
                  className={`ann-ch-card ${channel === c ? 'is-on' : ''}`}
                  onClick={() => setChannel(c)}
                >
                  <div className="ann-ch-card__head">
                    <span className="t-body-strong">{CHANNEL_META[c].label}</span>
                    <span className="t-mono num" style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>
                      {D.announcementPolicy.pricing[c]}원/건
                    </span>
                  </div>
                  <div className="t-caption" style={{ fontSize: 11 }}>{CHANNEL_META[c].hint}</div>
                </button>
              ))}
              <div className="ann-ch-card is-locked" title="알림톡은 사전 승인된 템플릿만 — 트리거 전용">
                <div className="ann-ch-card__head">
                  <span className="t-body-strong" style={{ color: 'var(--text-tertiary)' }}>알림톡</span>
                  <span className="t-mono num" style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>13원/건</span>
                </div>
                <div className="t-caption" style={{ fontSize: 11 }}>트리거 전용 (등하원·결제) · 공지엔 사용 불가</div>
              </div>
            </div>
          </FormBlock>

          {/* Schedule */}
          <FormBlock label="발송 시각">
            <div className="ann-audkind">
              <button
                className={`ann-audkind__btn ${schedKind === 'now' ? 'is-on' : ''}`}
                onClick={() => setSchedKind('now')}
              >지금 발송</button>
              <button
                className={`ann-audkind__btn ${schedKind === 'later' ? 'is-on' : ''}`}
                onClick={() => setSchedKind('later')}
              >예약 발송</button>
            </div>
            {schedKind === 'later' && (
              <div style={{ marginTop: 10 }}>
                <input
                  className="input t-mono num"
                  value={schedAt}
                  onChange={e => setSchedAt(e.target.value)}
                  placeholder="2025-11-22 09:00"
                  style={{ maxWidth: 220 }}
                />
                <div className="t-caption" style={{ fontSize: 11, marginTop: 4 }}>
                  정숙시간 22:00–08:00에 예약되면 자동으로 09:00로 이동됩니다
                </div>
              </div>
            )}
          </FormBlock>
        </div>

        {/* Right — preview + cost */}
        <aside className="ann-compose__preview">
          <div className="ann-preview-stack">
            {/* Audience summary */}
            <div className="ann-preview-card ann-preview-card--audience">
              <div className="t-micro">예상 수신</div>
              <div className="t-mono num" style={{ fontSize: 32, fontWeight: 700, marginTop: 6, color: 'var(--text-primary)', letterSpacing: '-0.018em' }}>
                {estimatedCount.toLocaleString()}<span style={{ fontSize: 14, marginLeft: 2 }}>명</span>
              </div>
              <div className="t-caption" style={{ fontSize: 11, marginTop: 4 }}>
                {audienceLabel({ kind: audKind, filters: audienceFilter, memberIds: audMemberIds, recipients })}
              </div>
            </div>

            {/* Cost estimate */}
            <div className="ann-preview-card">
              <div className="t-micro">예상 비용</div>
              <div className="t-mono num" style={{ fontSize: 20, fontWeight: 700, marginTop: 4, color: 'var(--text-primary)' }}>
                {estimatedCost.toLocaleString()}원
              </div>
              <div className="t-caption" style={{ fontSize: 11, marginTop: 4 }}>
                {CHANNEL_META[channel].label} {D.announcementPolicy.pricing[channel]}원 × {estimatedCount}건
                {channel === 'sms' && body.length > 90 && <span style={{ color: 'var(--status-warning)' }}> · LMS</span>}
              </div>
            </div>

            {/* Phone preview */}
            <div className="ann-preview-card">
              <div className="t-micro" style={{ marginBottom: 8 }}>발송 미리보기</div>
              <div className="ann-phone-preview">
                <div className="ann-phone-preview__bar">
                  <span style={{ fontSize: 10, color: 'var(--text-tertiary)' }}>{channel === 'friend' ? '카카오톡' : '메시지'}</span>
                  <span style={{ fontSize: 10, color: 'var(--text-tertiary)' }}>지금</span>
                </div>
                <div className="ann-phone-preview__bubble">
                  <div className="ann-phone-preview__sender">
                    {channel === 'friend' ? '@송유이보컬' : '[송유이보컬] '}
                  </div>
                  <div className="ann-phone-preview__title">
                    {title || <span style={{ color: 'var(--text-tertiary)', fontWeight: 400 }}>(제목 없음)</span>}
                  </div>
                  <div className="ann-phone-preview__body">
                    {body || <span style={{ color: 'var(--text-tertiary)' }}>본문이 여기 표시됩니다…</span>}
                  </div>
                </div>
              </div>
            </div>

            {/* Quiet hours notice */}
            {schedKind === 'later' && isInQuietHours(schedAt) && (
              <div className="ann-preview-card" style={{ background: 'var(--status-warning-bg)', borderColor: 'var(--status-warning)' }}>
                <div className="t-micro" style={{ color: 'var(--status-warning)' }}>⚠ 정숙시간 회피</div>
                <div className="t-caption" style={{ fontSize: 11, marginTop: 4 }}>
                  지정한 시각이 22:00–08:00 사이라 자동으로 다음 날 09:00로 이동됩니다
                </div>
              </div>
            )}
          </div>
        </aside>
      </div>
    </UI.Modal>
  );
}

// ────────────────────────────────────────────────────────────
// Small subcomponents
// ────────────────────────────────────────────────────────────
function FormBlock({ label, hint, required, children }) {
  return (
    <div className="ann-fb">
      <div className="ann-fb__label">
        {label}
        {required && <span style={{ color: 'var(--status-danger)', marginLeft: 2 }}>*</span>}
        {hint && <span className="t-caption" style={{ fontSize: 11, marginLeft: 8, fontWeight: 400 }}>{hint}</span>}
      </div>
      <div className="ann-fb__body">{children}</div>
    </div>
  );
}

function FilterChipGroup({ label, options, selected, onChange }) {
  const toggle = (id) => {
    if (selected.includes(id)) onChange(selected.filter(x => x !== id));
    else onChange([...selected, id]);
  };
  return (
    <div className="row" style={{ gap: 8, alignItems: 'center', flexWrap: 'wrap' }}>
      <span className="t-caption" style={{ fontSize: 11, width: 90, color: 'var(--text-tertiary)' }}>{label}</span>
      <div className="row" style={{ gap: 4, flexWrap: 'wrap' }}>
        {options.map(o => (
          <button
            key={o.id}
            className={`ann-filter-chip ${selected.includes(o.id) ? 'is-on' : ''}`}
            onClick={() => toggle(o.id)}
          >{o.label}</button>
        ))}
      </div>
    </div>
  );
}

// ────────────────────────────────────────────────────────────
// Helpers
// ────────────────────────────────────────────────────────────
function audienceLabel(audience) {
  if (audience.kind === 'all') return '전체 재원 회원';
  if (audience.kind === 'individual') {
    const ids = audience.memberIds || [];
    if (ids.length === 0) return '회원 선택 안 됨';
    if (ids.length <= 2) {
      return ids.map(id => D.getMember(id)?.name || id).join(' · ');
    }
    return `${D.getMember(ids[0])?.name || ids[0]} 외 ${ids.length - 1}명`;
  }
  // group
  const parts = [];
  const f = audience.filters || {};
  if (f.status && f.status.length && !(f.status.length === 1 && f.status[0] === 'ACTIVE')) {
    const lbl = { ACTIVE: '재원', HOLD: '휴원', WITHDRAWN: '퇴원' };
    parts.push(f.status.map(s => lbl[s] || s).join('·'));
  }
  if (f.teacherIds && f.teacherIds.length) {
    parts.push(f.teacherIds.map(id => D.getTeacher(id)?.name).filter(Boolean).join('·') + ' 담당');
  }
  if (f.passType && f.passType.length) {
    parts.push(f.passType.map(id => D.passTypes.find(p => p.id === id)?.label).filter(Boolean).join('·'));
  }
  return parts.length ? parts.join(' · ') : '필터 없음';
}

function shortWhen(s) {
  if (!s) return '—';
  // ISO 8601 또는 'YYYY-MM-DD HH:mm' → 'M/D HH:mm'
  const d = new Date(s);
  if (!isNaN(d.getTime())) {
    const hh = String(d.getHours()).padStart(2, '0');
    const mm = String(d.getMinutes()).padStart(2, '0');
    return `${d.getMonth() + 1}/${d.getDate()} ${hh}:${mm}`;
  }
  return s;
}

function nowStr() {
  return D.nowIso();
}

function suggestNextSendTime() {
  // Next 09:00 (today if before 09, else tomorrow)
  const d = new Date();
  if (d.getHours() >= 9) d.setDate(d.getDate() + 1);
  d.setHours(9, 0, 0, 0);
  return D.Schema.toIsoKst(d);
}

function isInQuietHours(s) {
  const m = s.match(/(\d{2}):(\d{2})$/);
  if (!m) return false;
  const h = parseInt(m[1], 10);
  return h >= 22 || h < 8;
}

function simulateStats(a) {
  const sent = a.audience?.estimatedCount || 0;
  const failed = Math.floor(sent * 0.02);
  const delivered = sent - failed;
  const read = Math.floor(delivered * (0.6 + Math.random() * 0.3));
  const channel = a.channel || 'sms';
  const pricing = D.announcementPolicy.pricing;
  const cost = sent * (pricing[channel] || 40);
  return { sent, delivered, read, failed, costKRW: cost };
}

window.Announcements = { AnnouncementsPage };
})();
