// app/feedback.jsx — 문제제기(피드백): 사용자 제보 모달 + 관리자 접수함
// 백엔드: serve.ts 프록시(/api/feedback*) → mcps/feedback (REST /v1/feedback).
// 제보 1건 = 내용 + 첨부(이미지/압축/문서) + 세션 대화 전문 + 사용자명 (+문제가 된 응답, 업체, LLM 메타).
// window.RibbonFeedback 로 노출.

const { useState: useStateF, useEffect: useEffectF } = React;
const { Icon: IconF, I: IF } = window.RibbonChat;

// 서버(mcps/feedback domain.ts LIMITS/ALLOWED_EXTENSIONS)와 동일한 클라이언트 한도 — 큰 파일을
// 올리고 나서야 거부당하지 않게 브라우저에서 먼저 거른다.
const FB_MAX_FILES = 5;
const FB_MAX_FILE_MB = 10;
const FB_MAX_TOTAL_MB = 25;
const FB_ACCEPT = '.png,.jpg,.jpeg,.gif,.webp,.bmp,.svg,.zip,.7z,.tar,.gz,.pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.hwp,.hwpx,.txt,.csv,.md,.json,.log';

const FB_STATUS = {
  open: { label: '접수', color: 'var(--accent)', bg: 'rgba(204,120,92,0.13)' },
  in_progress: { label: '처리중', color: '#b8860b', bg: 'rgba(232,165,90,0.16)' },
  resolved: { label: '해결', color: '#3f8a52', bg: 'rgba(93,184,114,0.14)' },
  rejected: { label: '반려', color: 'var(--text-muted)', bg: 'var(--surface-soft)' },
};
const fbStatus = (s) => FB_STATUS[s] || FB_STATUS.open;
const fbTime = (iso) => { try { return new Date(iso).toLocaleString('ko-KR', { month: 'numeric', day: 'numeric', hour: '2-digit', minute: '2-digit' }); } catch (e) { return iso; } };
const fbSize = (n) => n >= 1024 * 1024 ? (n / 1024 / 1024).toFixed(1) + 'MB' : Math.max(1, Math.round(n / 1024)) + 'KB';

function fileToBase64(file) {
  return new Promise((resolve, reject) => {
    const r = new FileReader();
    r.onload = () => resolve(String(r.result).split(',')[1] || '');
    r.onerror = () => reject(r.error || new Error('파일 읽기 실패'));
    r.readAsDataURL(file);
  });
}

// ── 상태 배지 ───────────────────────────────────────────────
function FbBadge({ status }) {
  const s = fbStatus(status);
  return <span style={{ fontSize: 10.5, fontWeight: 700, letterSpacing: '0.3px', color: s.color, background: s.bg,
    padding: '3px 9px', borderRadius: 'var(--radius-pill)', flexShrink: 0 }}>{s.label}</span>;
}

// ── 사용자 제보 모달 (채팅 컬럼 오버레이) ────────────────────
// conv.reportMsg = 문제제기 대상 어시스턴트 메시지(없으면 세션 전체 제보).
function FeedbackModal({ conv, tenant, onClose }) {
  const [name, setName] = useStateF(() => { try { return localStorage.getItem('ribbon.reporter.name') || ''; } catch (e) { return ''; } });
  const [content, setContent] = useStateF('');
  const [files, setFiles] = useStateF([]); // File[]
  const [state, setState] = useStateF('edit'); // edit | sending | done
  const [error, setError] = useStateF(null);
  const [doneId, setDoneId] = useStateF(null);

  const msg = conv.reportMsg;
  const transcript = window.RibbonChat.historyFrom(conv.messages);

  function addFiles(list) {
    setError(null);
    const next = [...files];
    for (const f of Array.from(list || [])) {
      if (next.length >= FB_MAX_FILES) { setError(`첨부는 최대 ${FB_MAX_FILES}개까지예요.`); break; }
      if (f.size > FB_MAX_FILE_MB * 1024 * 1024) { setError(`'${f.name}' 이(가) 파일당 한도(${FB_MAX_FILE_MB}MB)를 넘어요.`); continue; }
      next.push(f);
    }
    if (next.reduce((a, f) => a + f.size, 0) > FB_MAX_TOTAL_MB * 1024 * 1024) {
      setError(`첨부 합계가 ${FB_MAX_TOTAL_MB}MB 를 넘어요.`);
      return;
    }
    setFiles(next);
  }

  async function submit() {
    if (!content.trim()) { setError('문제 내용을 적어 주세요.'); return; }
    if (!name.trim()) { setError('사용자명을 적어 주세요.'); return; }
    setState('sending'); setError(null);
    try {
      const attachments = [];
      for (const f of files) {
        attachments.push({ filename: f.name, mime: f.type || 'application/octet-stream', data_base64: await fileToBase64(f) });
      }
      const meta = (msg && msg.scenario && msg.scenario.meta) || undefined;
      const res = await fetch('/api/feedback', {
        method: 'POST', headers: { 'content-type': 'application/json' },
        body: JSON.stringify({
          user_name: name.trim(),
          content: content.trim(),
          tenant_id: (tenant && tenant.id) || undefined,
          session_id: conv.activeId,
          reported_message: msg ? (msg.text || '') : undefined,
          transcript,
          attachments,
          meta,
        }),
      });
      const j = await res.json().catch(() => ({}));
      if (!res.ok) throw new Error(j.error || ('HTTP ' + res.status));
      try { localStorage.setItem('ribbon.reporter.name', name.trim()); } catch (e) {}
      setDoneId(j.id); setState('done');
      setTimeout(onClose, 1600);
    } catch (e) {
      setState('edit');
      setError('접수 실패: ' + e.message);
    }
  }

  const inputStyle = { width: '100%', boxSizing: 'border-box', padding: '9px 12px', borderRadius: 'var(--radius-md)',
    border: '1px solid var(--border-hairline)', background: 'var(--surface-page)', fontFamily: 'var(--font-sans)',
    fontSize: 13.5, color: 'var(--text-heading)', outline: 'none' };

  return (
    <div style={{ position: 'absolute', inset: 0, zIndex: 70, display: 'flex', alignItems: 'center', justifyContent: 'center',
      background: 'rgba(20,18,15,0.45)', fontFamily: 'var(--font-sans)' }} onClick={onClose}>
      <div className="rb-rise" onClick={(e) => e.stopPropagation()}
        style={{ width: 'min(560px, calc(100% - 32px))', maxHeight: 'calc(100% - 48px)', overflowY: 'auto',
          background: 'var(--surface-page)', borderRadius: 'var(--radius-lg)', border: '1px solid var(--border-hairline)',
          boxShadow: '0 18px 50px rgba(0,0,0,0.22)', padding: '20px 22px' }}>
        {state === 'done' ? (
          <div style={{ textAlign: 'center', padding: '28px 6px' }}>
            <IconF d="M20 6L9 17l-5-5" size={34} color="var(--color-success)" />
            <div style={{ fontSize: 16, fontWeight: 700, color: 'var(--text-heading)', marginTop: 10 }}>문제제기가 접수됐어요</div>
            <div style={{ fontSize: 12.5, color: 'var(--text-muted)', marginTop: 5 }}>접수번호 {doneId} — 관리자와 개발팀이 확인 후 처리합니다.</div>
          </div>
        ) : (
          <React.Fragment>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
              <div>
                <div style={{ fontFamily: 'var(--font-display)', fontSize: 19, letterSpacing: '-0.3px', color: 'var(--text-heading)' }}>문제제기</div>
                <div style={{ fontSize: 12, color: 'var(--text-faint)', marginTop: 2 }}>챗봇 응답의 문제를 알려 주세요 — 대화 내용과 함께 접수됩니다</div>
              </div>
              <button onClick={onClose} style={{ marginLeft: 'auto', width: 32, height: 32, borderRadius: 'var(--radius-full)',
                border: '1px solid var(--border-hairline)', background: 'var(--surface-page)', cursor: 'pointer',
                display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                <IconF d={IF.x} size={15} color="var(--text-muted)" /></button>
            </div>

            {/* 문제가 된 응답 미리보기 */}
            {msg && (
              <div style={{ marginTop: 14, padding: '9px 12px', borderLeft: '3px solid var(--accent)', background: 'var(--surface-soft)',
                borderRadius: '0 var(--radius-md) var(--radius-md) 0', fontSize: 12.5, color: 'var(--text-muted)', lineHeight: 1.5,
                maxHeight: 74, overflow: 'hidden' }}>
                {(msg.text || '').slice(0, 180) || '(응답 본문 없음)'}{(msg.text || '').length > 180 ? '…' : ''}
              </div>
            )}

            <div style={{ marginTop: 16 }}>
              <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--text-strong)', marginBottom: 6 }}>사용자명 <span style={{ color: 'var(--accent)' }}>*</span></div>
              <input value={name} onChange={(e) => setName(e.target.value)} placeholder="이름 또는 직함 (예: 김대표)" style={inputStyle} />
            </div>

            <div style={{ marginTop: 14 }}>
              <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--text-strong)', marginBottom: 6 }}>문제 내용 <span style={{ color: 'var(--accent)' }}>*</span></div>
              <textarea value={content} onChange={(e) => setContent(e.target.value)} rows={4}
                placeholder="어떤 점이 잘못되었나요? (예: 가동률 숫자가 리본 통계 화면과 다릅니다 / 차종을 잘못 안내했습니다)"
                style={{ ...inputStyle, resize: 'vertical', minHeight: 92, lineHeight: 1.55 }} />
            </div>

            {/* 첨부 */}
            <div style={{ marginTop: 14 }}>
              <div style={{ fontSize: 12.5, fontWeight: 600, color: 'var(--text-strong)', marginBottom: 6 }}>
                첨부파일 <span style={{ fontWeight: 500, color: 'var(--text-faint)' }}>(이미지·압축·문서 · 최대 {FB_MAX_FILES}개/{FB_MAX_FILE_MB}MB)</span></div>
              <label style={{ display: 'inline-flex', alignItems: 'center', gap: 7, padding: '8px 13px', borderRadius: 'var(--radius-md)',
                border: '1px dashed var(--border-hairline)', cursor: 'pointer', fontSize: 12.5, fontWeight: 600, color: 'var(--text-muted)',
                background: 'var(--surface-soft)' }}>
                <IconF d={IF.clip} size={14} color="var(--text-muted)" />파일 선택
                <input type="file" multiple accept={FB_ACCEPT} style={{ display: 'none' }}
                  onChange={(e) => { addFiles(e.target.files); e.target.value = ''; }} />
              </label>
              {files.length > 0 && (
                <div style={{ display: 'flex', flexWrap: 'wrap', gap: 6, marginTop: 9 }}>
                  {files.map((f, i) => (
                    <span key={i} style={{ display: 'inline-flex', alignItems: 'center', gap: 6, padding: '5px 10px', borderRadius: 'var(--radius-pill)',
                      background: 'var(--surface-soft)', border: '1px solid var(--border-hairline)', fontSize: 12, color: 'var(--text-body)' }}>
                      <IconF d={IF.clip} size={12} color="var(--text-muted)" />{f.name}
                      <span style={{ color: 'var(--text-faint)' }}>{fbSize(f.size)}</span>
                      <button onClick={() => setFiles(files.filter((_, j) => j !== i))}
                        style={{ border: 'none', background: 'none', cursor: 'pointer', padding: 0, display: 'flex' }}>
                        <IconF d={IF.x} size={11} color="var(--text-faint)" /></button>
                    </span>
                  ))}
                </div>
              )}
            </div>

            {/* 자동 동봉 안내 */}
            <div style={{ marginTop: 14, padding: '9px 12px', borderRadius: 'var(--radius-md)', background: 'var(--surface-soft)',
              fontSize: 11.5, color: 'var(--text-muted)', lineHeight: 1.55 }}>
              함께 전송되는 정보: 이 세션의 대화 {transcript.length}턴{msg ? ' · 문제가 된 응답' : ''}{tenant ? ` · 업체(${tenant.name})` : ''} · 접수 시각
            </div>

            {error && <div style={{ marginTop: 10, fontSize: 12.5, fontWeight: 600, color: 'var(--color-error, #c0392b)' }}>{error}</div>}

            <div style={{ display: 'flex', justifyContent: 'flex-end', gap: 8, marginTop: 18 }}>
              <button onClick={onClose} style={{ height: 38, padding: '0 16px', borderRadius: 'var(--radius-md)', border: '1px solid var(--border-hairline)',
                background: 'var(--surface-page)', cursor: 'pointer', fontFamily: 'var(--font-sans)', fontSize: 13, fontWeight: 600, color: 'var(--text-body)' }}>취소</button>
              <button onClick={submit} disabled={state === 'sending'}
                style={{ height: 38, padding: '0 20px', borderRadius: 'var(--radius-md)', border: 'none', background: 'var(--accent)', color: '#fff',
                  fontFamily: 'var(--font-sans)', fontSize: 13, fontWeight: 600, cursor: state === 'sending' ? 'default' : 'pointer',
                  opacity: state === 'sending' ? 0.6 : 1 }}>
                {state === 'sending' ? '접수 중…' : '문제제기 접수'}</button>
            </div>
          </React.Fragment>
        )}
      </div>
    </div>
  );
}

// ── 관리자 접수함: 목록 + 상세/처리 ─────────────────────────
const FB_FILTERS = [
  { value: '', label: '전체' },
  { value: 'open', label: '접수' },
  { value: 'in_progress', label: '처리중' },
  { value: 'resolved', label: '해결' },
  { value: 'rejected', label: '반려' },
];

function FeedbackAdmin() {
  const [items, setItems] = useStateF([]);
  const [filter, setFilter] = useStateF('');
  const [selectedId, setSelectedId] = useStateF(null);
  const [detail, setDetail] = useStateF(null);
  const [showTranscript, setShowTranscript] = useStateF(false);
  const [error, setError] = useStateF(null);
  const [busy, setBusy] = useStateF(false);
  // 처리 폼(선택 건 기준)
  const [status, setStatus] = useStateF('open');
  const [note, setNote] = useStateF('');
  const [assignee, setAssignee] = useStateF('');
  const [savedAt, setSavedAt] = useStateF(0);

  async function load(f = filter) {
    setError(null);
    try {
      const res = await fetch('/api/feedback?limit=100' + (f ? '&status=' + f : ''));
      const j = await res.json().catch(() => ({}));
      if (!res.ok) throw new Error(j.error || ('HTTP ' + res.status));
      setItems(j.items || []);
    } catch (e) { setError(e.message); setItems([]); }
  }
  useEffectF(() => { load(); }, [filter]);

  async function open(id) {
    setSelectedId(id); setDetail(null); setShowTranscript(false); setSavedAt(0);
    try {
      const res = await fetch('/api/feedback/' + encodeURIComponent(id));
      const j = await res.json().catch(() => ({}));
      if (!res.ok) throw new Error(j.error || ('HTTP ' + res.status));
      setDetail(j); setStatus(j.status); setNote(j.resolution_note || ''); setAssignee(j.assignee || '');
    } catch (e) { setError(e.message); }
  }

  async function save() {
    if (!detail) return;
    setBusy(true);
    try {
      const res = await fetch('/api/feedback/' + encodeURIComponent(detail.id), {
        method: 'PATCH', headers: { 'content-type': 'application/json' },
        body: JSON.stringify({ status, resolution_note: note || null, assignee: assignee || null }),
      });
      const j = await res.json().catch(() => ({}));
      if (!res.ok) throw new Error(j.error || ('HTTP ' + res.status));
      setSavedAt(Date.now());
      setDetail((d) => ({ ...d, status: j.status, resolution_note: j.resolution_note, assignee: j.assignee }));
      load();
    } catch (e) { alert('저장 실패: ' + e.message); }
    finally { setBusy(false); }
  }

  const inputStyle = { height: 36, padding: '0 11px', borderRadius: 'var(--radius-md)', border: '1px solid var(--border-hairline)',
    background: 'var(--surface-page)', fontFamily: 'var(--font-sans)', fontSize: 13, color: 'var(--text-heading)', outline: 'none' };

  return (
    <div style={{ flex: 1, minHeight: 0, display: 'flex', background: 'var(--surface-soft)', fontFamily: 'var(--font-sans)' }}>
      {/* 좌: 목록 */}
      <div style={{ width: 380, flexShrink: 0, borderRight: '1px solid var(--border-hairline)', display: 'flex', flexDirection: 'column', height: '100%' }}>
        <div style={{ padding: '18px 18px 10px' }}>
          <div style={{ fontFamily: 'var(--font-display)', fontSize: 18, letterSpacing: '-0.3px', color: 'var(--text-heading)' }}>문제제기 접수함</div>
          <div style={{ fontSize: 12, color: 'var(--text-faint)', marginTop: 2 }}>사용자가 제보한 챗봇 응답 문제</div>
          <div style={{ display: 'flex', gap: 5, marginTop: 11, flexWrap: 'wrap' }}>
            {FB_FILTERS.map((f) => (
              <button key={f.value} onClick={() => setFilter(f.value)}
                style={{ padding: '5px 11px', borderRadius: 'var(--radius-pill)', cursor: 'pointer', fontSize: 11.5, fontWeight: 600,
                  border: '1px solid ' + (filter === f.value ? 'var(--accent)' : 'var(--border-hairline)'),
                  background: filter === f.value ? 'rgba(204,120,92,0.10)' : 'var(--surface-page)',
                  color: filter === f.value ? 'var(--accent)' : 'var(--text-muted)' }}>{f.label}</button>
            ))}
          </div>
        </div>
        <div style={{ flex: 1, overflowY: 'auto', padding: '4px 12px 16px' }}>
          {error && <div style={{ fontSize: 12.5, color: 'var(--color-error, #c0392b)', padding: '8px 6px' }}>불러오기 실패: {error}</div>}
          {!error && items.length === 0 && <div style={{ fontSize: 12.5, color: 'var(--text-faint)', padding: '10px 6px' }}>접수된 문제제기가 없어요.</div>}
          {items.map((it) => (
            <button key={it.id} onClick={() => open(it.id)}
              style={{ width: '100%', textAlign: 'left', padding: '11px 12px', borderRadius: 'var(--radius-md)', marginBottom: 4, cursor: 'pointer',
                background: it.id === selectedId ? 'var(--surface-page)' : 'transparent',
                border: it.id === selectedId ? '1px solid var(--border-hairline)' : '1px solid transparent' }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                <span style={{ fontSize: 13, fontWeight: 600, color: 'var(--text-heading)' }}>{it.user_name}</span>
                {it.tenant_id && <span style={{ fontSize: 10.5, color: 'var(--text-faint)' }}>{it.tenant_id}</span>}
                <span style={{ marginLeft: 'auto' }}><FbBadge status={it.status} /></span>
              </div>
              <div style={{ fontSize: 12.5, color: 'var(--text-body)', marginTop: 4, lineHeight: 1.45, display: '-webkit-box',
                WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden' }}>{it.content_preview}</div>
              <div style={{ display: 'flex', gap: 9, marginTop: 5, fontSize: 10.5, color: 'var(--text-faint)' }}>
                <span>{fbTime(it.created_at)}</span>
                <span>대화 {it.transcript_turns}턴</span>
                {it.attachment_count > 0 && <span>첨부 {it.attachment_count}</span>}
                {it.assignee && <span>담당 {it.assignee}</span>}
              </div>
            </button>
          ))}
        </div>
      </div>

      {/* 우: 상세 + 처리 */}
      <div style={{ flex: 1, minWidth: 0, overflowY: 'auto', background: 'var(--surface-page)' }}>
        {!detail ? (
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', height: '100%', color: 'var(--text-faint)', fontSize: 13 }}>
            왼쪽 목록에서 문제제기를 선택하세요</div>
        ) : (
          <div style={{ maxWidth: 720, margin: '0 auto', padding: '24px 28px 40px' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 10, paddingBottom: 14, borderBottom: '1px solid var(--border-hairline)' }}>
              <div>
                <div style={{ display: 'flex', alignItems: 'center', gap: 9 }}>
                  <span style={{ fontFamily: 'var(--font-display)', fontSize: 19, color: 'var(--text-heading)' }}>{detail.user_name} 님의 문제제기</span>
                  <FbBadge status={detail.status} />
                </div>
                <div style={{ fontSize: 11.5, color: 'var(--text-faint)', marginTop: 3, fontFamily: 'var(--font-mono)' }}>
                  {detail.id} · {fbTime(detail.created_at)}{detail.tenant_id ? ' · ' + detail.tenant_id : ''}{detail.session_id ? ' · 세션 ' + detail.session_id : ''}</div>
              </div>
            </div>

            <div style={{ marginTop: 16, fontSize: 14, color: 'var(--text-heading)', lineHeight: 1.65, whiteSpace: 'pre-wrap' }}>{detail.content}</div>

            {detail.reported_message && (
              <div style={{ marginTop: 14 }}>
                <div style={{ fontSize: 11.5, fontWeight: 700, color: 'var(--text-faint)', textTransform: 'uppercase', letterSpacing: '0.5px', marginBottom: 6 }}>문제가 된 응답</div>
                <div style={{ padding: '10px 13px', borderLeft: '3px solid var(--accent)', background: 'var(--surface-soft)',
                  borderRadius: '0 var(--radius-md) var(--radius-md) 0', fontSize: 12.5, color: 'var(--text-body)', lineHeight: 1.6, whiteSpace: 'pre-wrap' }}>
                  {detail.reported_message}</div>
              </div>
            )}

            {detail.attachments && detail.attachments.length > 0 && (
              <div style={{ marginTop: 16 }}>
                <div style={{ fontSize: 11.5, fontWeight: 700, color: 'var(--text-faint)', textTransform: 'uppercase', letterSpacing: '0.5px', marginBottom: 7 }}>첨부파일</div>
                <div style={{ display: 'flex', flexWrap: 'wrap', gap: 7 }}>
                  {detail.attachments.map((a) => (
                    <a key={a.id} href={'/api/feedback/' + encodeURIComponent(detail.id) + '/attachments/' + encodeURIComponent(a.id)}
                      style={{ display: 'inline-flex', alignItems: 'center', gap: 7, padding: '7px 12px', borderRadius: 'var(--radius-md)',
                        border: '1px solid var(--border-hairline)', background: 'var(--surface-soft)', fontSize: 12.5, fontWeight: 600,
                        color: 'var(--text-body)', textDecoration: 'none' }}>
                      <IconF d={IF.download} size={13} color="var(--accent)" />{a.filename}
                      <span style={{ fontWeight: 500, color: 'var(--text-faint)' }}>{fbSize(a.size)}</span>
                    </a>
                  ))}
                </div>
              </div>
            )}

            {/* 세션 대화 */}
            <div style={{ marginTop: 16 }}>
              <button onClick={() => setShowTranscript((v) => !v)}
                style={{ display: 'inline-flex', alignItems: 'center', gap: 7, border: 'none', background: 'none', cursor: 'pointer',
                  fontSize: 12.5, fontWeight: 700, color: 'var(--accent)', padding: 0, fontFamily: 'var(--font-sans)' }}>
                <IconF d={showTranscript ? 'M6 15l6-6 6 6' : 'M6 9l6 6 6-6'} size={14} color="var(--accent)" />
                세션 대화 {detail.transcript ? detail.transcript.length : 0}턴 {showTranscript ? '접기' : '보기'}</button>
              {showTranscript && (
                <div style={{ marginTop: 10, display: 'flex', flexDirection: 'column', gap: 8, padding: '13px 14px',
                  border: '1px solid var(--border-hairline)', borderRadius: 'var(--radius-md)', background: 'var(--surface-soft)' }}>
                  {(detail.transcript || []).map((t, i) => (
                    <div key={i} style={{ display: 'flex', justifyContent: t.role === 'user' ? 'flex-end' : 'flex-start' }}>
                      <div style={{ maxWidth: '84%', padding: '7px 11px', borderRadius: 10, fontSize: 12.5, lineHeight: 1.55, whiteSpace: 'pre-wrap',
                        background: t.role === 'user' ? 'var(--surface-card-strong)' : 'var(--surface-page)',
                        border: '1px solid var(--border-hairline)', color: 'var(--text-body)' }}>{t.content}</div>
                    </div>
                  ))}
                </div>
              )}
            </div>

            {/* 처리(트리아지) */}
            <div style={{ marginTop: 22, padding: '16px 18px', border: '1px solid var(--border-hairline)', borderRadius: 'var(--radius-lg)', background: 'var(--surface-soft)' }}>
              <div style={{ fontSize: 12.5, fontWeight: 700, color: 'var(--text-strong)', marginBottom: 11 }}>처리</div>
              <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
                <select value={status} onChange={(e) => setStatus(e.target.value)} style={{ ...inputStyle, cursor: 'pointer' }}>
                  {Object.entries(FB_STATUS).map(([v, s]) => <option key={v} value={v}>{s.label}</option>)}
                </select>
                <input value={assignee} onChange={(e) => setAssignee(e.target.value)} placeholder="담당자" style={{ ...inputStyle, width: 130 }} />
                <input value={note} onChange={(e) => setNote(e.target.value)} placeholder="처리 메모 (원인/조치)" style={{ ...inputStyle, flex: 1, minWidth: 200 }} />
                <button onClick={save} disabled={busy}
                  style={{ height: 36, padding: '0 17px', borderRadius: 'var(--radius-md)', border: 'none', background: 'var(--accent)', color: '#fff',
                    fontFamily: 'var(--font-sans)', fontSize: 12.5, fontWeight: 600, cursor: busy ? 'default' : 'pointer', opacity: busy ? 0.6 : 1 }}>
                  {busy ? '저장 중…' : '저장'}</button>
              </div>
              {savedAt > 0 && <div style={{ marginTop: 9, fontSize: 12, fontWeight: 600, color: 'var(--color-success)' }}>처리 내용이 저장됐어요</div>}
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

window.RibbonFeedback = { FeedbackModal, FeedbackAdmin };
