/* ============================================================
   create-flow.jsx — 세로 누적형 가이드 생성 플로우 (URL · 이미지 공용)
   공통 4단계:
     ① 입력      : URL 입력 / 이미지 업로드
     ② 분석      : 수집·업로드 배너 수 → 디자인 유사도로 N개 그룹 분류
     ③ 그룹 확인 : 비슷한 배너끼리 묶음. 잘못 묶였으면 드래그로 이동/제외
     ④ 가이드 후보: 확정된 묶음을 기준으로 후보 가이드 도출 → 선택 생성
   ============================================================ */

/* 배너 썸네일 (URL=BannerMock / 이미지=src) */
function bannerThumb(b, scale) {
  if (b.src) return <img src={b.src} alt="" draggable={false} style={{ width: '100%', height: '100%', objectFit: 'cover', display: 'block' }} />;
  const ar = b.w / b.h;
  return <BannerMock d={b} scale={scale || 0.3} showProduct={ar < 4} />;
}

/* accordion step shell */
function FlowStep({ n, title, sub, state, summary, onEdit, action, children }) {
  return (
    <div className={`flow-step ${state}`} style={{ borderStyle: "none" }}>
      <div className="flow-step-head">
        <span className="flow-step-num">{state === 'done' ? <S2Icon name="check" size={14} strokeWidth={3} /> : n}</span>
        <div className="flow-step-titles">
          <div className="flow-step-title">{title}</div>
          {state === 'done' && summary ?
          <div className="flow-step-summary">{summary}</div> :
          sub ? <div className="flow-step-sub">{sub}</div> : null}
        </div>
        {state === 'done' && onEdit &&
        <button type="button" className="flow-step-edit" onClick={onEdit}>수정</button>}
        {state === 'active' && action ? <div className="flow-step-action">{action}</div> : null}
      </div>
      {state === 'active' && <div className="flow-step-body">{children}</div>}
    </div>);
}

const FLOW_TOKENS = [
{ k: '주조색', v: '#3B1D6E' }, { k: 'CTA', v: '#4E4CDB' }, { k: '포인트', v: '#FFD84D' },
{ k: '배경', v: '#FFFFFF' }, { k: '텍스트', v: '#1A1530' }];

/* ③ 그룹 확인 — 드래그로 배너 이동 / × 제외 */
function GroupEditor({ groups, setGroups, onDirty }) {
  const [drag, setDrag] = React.useState(null); // {gid, bid}
  const [over, setOver] = React.useState(null);

  const move = (fromG, bid, toG) => {
    if (fromG === toG) return;
    onDirty && onDirty();
    setGroups((gs) => {
      let moved = null;
      let next = gs.map((g) => {
        if (g.id === fromG) {moved = g.banners.find((x) => x.id === bid);return { ...g, banners: g.banners.filter((x) => x.id !== bid) };}
        return g;
      });
      if (!moved) return gs;
      next = next.map((g) => g.id === toG ? { ...g, banners: [...g.banners, moved] } : g);
      return next.filter((g) => g.banners.length > 0);
    });
  };
  const exclude = (gid, bid) => {
    onDirty && onDirty();
    setGroups((gs) => gs.map((g) => g.id === gid ? { ...g, banners: g.banners.filter((x) => x.id !== bid) } : g).filter((g) => g.banners.length > 0));
  };

  return (
    <div className="grp-list">
      {groups.map((g, gi) => {
        const target = drag && drag.gid !== g.id;
        const gp = (g.size || '').split('×');
        const gar = gp.length === 2 ? (parseInt(gp[0], 10) / parseInt(gp[1], 10)) : 16 / 9;
        return (
          <div key={g.id}
          className={`grp-card ${over === g.id && target ? 'drop-on' : ''} ${target ? 'droppable' : ''}`}
          onDragOver={(e) => {if (target) {e.preventDefault();setOver(g.id);}}}
          onDragLeave={(e) => {if (!e.currentTarget.contains(e.relatedTarget)) setOver((v) => v === g.id ? null : v);}}
          onDrop={(e) => {e.preventDefault();setOver(null);if (drag && drag.gid !== g.id) move(drag.gid, drag.bid, g.id);setDrag(null);}} style={{ borderStyle: "none", backgroundColor: "rgb(243, 243, 243)" }}>
            <div className="grp-head">
              <span className="grp-badge">{gi + 1}</span>
              <span className="grp-name-static">{g.name}</span>
              <span className="pill accent" style={{ flexShrink: 0 }}>신뢰도 {g.confidence || 88}%</span>
              <span className="grp-count">{g.banners.length}개</span>
              {g.size && <span className="grp-size">{g.size}</span>}
            </div>
            <div className="grp-imgs">
              {g.banners.map((b) => {
                const ar = b.src ? gar : b.w / b.h;
                return (
                  <div key={b.id} className="grp-thumb" draggable
                  onDragStart={(e) => {setDrag({ gid: g.id, bid: b.id });e.dataTransfer.effectAllowed = 'move';try {e.dataTransfer.setData('text/plain', b.id);} catch (_) {}}}
                  onDragEnd={() => {setDrag(null);setOver(null);}}
                  style={{ width: Math.round(76 * ar) }}>
                    {bannerThumb(b, 0.32)}
                    <button className="grp-thumb-x" title="제외" onClick={(e) => {e.stopPropagation();exclude(g.id, b.id);}}><S2Icon name="x" size={12} /></button>
                  </div>);
              })}
            </div>
          </div>);
      })}
    </div>);
}

/* ④ 가이드 후보 카드 — 기본은 핵심 정보만, 상세는 펼쳐보기 */
function CandidateCard({ g, on, onToggle, evidence }) {
  const [openDetail, setOpenDetail] = React.useState(false);
  const [openEv, setOpenEv] = React.useState(false);
  const pv = guidePreview(g);
  const stop = (e) => e.stopPropagation();
  return (
    <div className={`cand ${on ? 'on' : ''}`} role="button" aria-pressed={on} onClick={onToggle}>
      <span className="cand-check">{on && <S2Icon name="check" size={13} strokeWidth={3} />}</span>
      <div className="cand-preview">{g._prev ? <img src={g._prev} alt="" style={{ width: '100%', height: '100%', objectFit: 'contain', display: 'block' }} /> : <BannerMock d={pv} scale={0.5} />}</div>
      <div className="cand-main">
        <div className="cand-top">
          <div style={{ minWidth: 0 }}>
            <div className="cand-name">{g.name}</div>
            <div className="cand-type"><S2Icon name="layers" size={13} /> {g.bannerType || g.category}</div>
          </div>
          <span className="pill accent" style={{ flexShrink: 0 }}>정확도 {g.confidence}%</span>
        </div>

        <div className="cand-keys">
          {g.spec && <div className="cand-key"><span className="cand-key-l">규격</span><span className="cand-key-v mono">{g.spec.size}</span></div>}
          {(g.layoutTypes || []).length > 0 && <div className="cand-key"><span className="cand-key-l">레이아웃</span><span className="cand-key-v">{g.layoutTypes.length}종</span></div>}
          <div className="cand-key"><span className="cand-key-l">학습 배너</span><span className="cand-key-v">{g.banners}개</span></div>
          {(g.palette || []).length > 0 &&
            <div className="cand-key"><span className="cand-key-l">색상</span>
              <span className="cand-sw-row">{g.palette.slice(0, 6).map((c, k) => <i key={k} style={{ background: c.hex, borderColor: c.hex.toUpperCase() === '#FFFFFF' ? 'var(--border-strong)' : 'transparent' }} />)}</span>
            </div>}
        </div>

        <div className="cand-actions">
          <button type="button" className="cand-more" onClick={(e) => { stop(e); setOpenDetail((o) => !o); }}>
            <S2Icon name="chevronRight" size={13} style={{ transform: openDetail ? 'rotate(90deg)' : 'none', transition: 'transform .15s' }} /> 상세 정보
          </button>
          {evidence && evidence.length > 0 &&
            <button type="button" className="cand-more" onClick={(e) => { stop(e); setOpenEv((o) => !o); }}>
              <S2Icon name="image" size={13} /> 근거 배너 <b>{evidence.length}</b>
            </button>}
        </div>

        {openDetail &&
          <div className="ur-secs cand-detail" onClick={stop}>
            {g.spec &&
            <div className="ur-sec">
                <div className="ur-sec-label"><S2Icon name="ruler" size={13} /> 규격</div>
                <div className="ur-sec-body"><div className="ur-chips">
                  <span className="ur-chip mono"><b>SIZE</b> {g.spec.size}</span>
                  <span className="ur-chip"><b>배경</b> {g.spec.bg}</span>
                  <span className="ur-chip"><b>파일</b> {g.spec.file}</span>
                  {g.spec.flag && g.spec.flag !== '—' && <span className="ur-chip"><b>FLAG</b> {g.spec.flag}</span>}
                </div></div>
              </div>}
            {(g.layoutTypes || []).length > 0 &&
            <div className="ur-sec">
                <div className="ur-sec-label"><S2Icon name="layout" size={13} /> 레이아웃 유형</div>
                <div className="ur-sec-body"><div className="ur-chips">
                  {g.layoutTypes.map((l, k) => <span key={k} className="ur-chip ur-chip-accent">{l}</span>)}
                </div></div>
              </div>}
            {g.composition &&
            <div className="ur-sec">
                <div className="ur-sec-label"><S2Icon name="grid" size={13} /> 구성</div>
                <div className="ur-sec-body"><div className="ur-chips">
                  {g.composition.base.map((c, k) => <span key={'b' + k} className="ur-chip"><b>기본</b> {c}</span>)}
                  {g.composition.extra.map((c, k) => <span key={'e' + k} className="ur-chip">{c}</span>)}
                </div></div>
              </div>}
            {(g.palette || []).length > 0 &&
            <div className="ur-sec">
                <div className="ur-sec-label"><S2Icon name="palette" size={13} /> 색상</div>
                <div className="ur-sec-body"><div className="ur-colors">
                  {g.palette.map((c, k) =>
                  <span key={k} className="ur-color" title={c.role}>
                      <i className="ur-color-sw" style={{ background: c.hex, borderColor: c.hex.toUpperCase() === '#FFFFFF' ? 'var(--border-strong)' : 'transparent' }} />
                      <span className="ur-color-hex">{c.hex.toUpperCase()}</span>
                    </span>)}
                </div></div>
              </div>}
            {(g.typo || []).length > 0 &&
            <div className="ur-sec">
                <div className="ur-sec-label"><S2Icon name="type" size={13} /> 폰트</div>
                <div className="ur-sec-body"><div className="ur-chips">
                  {g.typo.map((t, k) => <span key={k} className="ur-chip"><b>{t.role}</b> {t.size}px · {t.weight}</span>)}
                </div></div>
              </div>}
            {g.logo &&
            <div className="ur-sec">
                <div className="ur-sec-label"><S2Icon name="image" size={13} /> 로고</div>
                <div className="ur-sec-body" style={{ display: 'flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
                  <span className="ur-logo-badge brand">자사 · 필수</span>
                  <span className="ur-logo-meta">{g.logo.brand}</span>
                  {g.logo.advertiser &&
                <React.Fragment>
                      <span className="ur-logo-badge advertiser">광고주 · 선택</span>
                      <span className="ur-logo-meta">{g.logo.advertiser}</span>
                    </React.Fragment>}
                  {g.logo.position && <span className="ur-chip" style={{ marginLeft: 'auto' }}>{g.logo.position} · 최대 {g.logo.max}</span>}
                </div>
              </div>}
            {(g.copyRule || []).length > 0 &&
            <div className="ur-sec">
                <div className="ur-sec-label"><S2Icon name="text" size={13} /> 문구 규칙</div>
                <div className="ur-sec-body"><div className="ur-chips">
                  {g.copyRule.map((c, k) => <span key={k} className="ur-chip">{c}</span>)}
                </div></div>
              </div>}
          </div>}

        {openEv && evidence && evidence.length > 0 &&
          <div className="ur-ev-row" onClick={stop}>
            {evidence.map((b) => {
              const ear = g.spec ? g.spec.w / g.spec.h : 16 / 9;
              const ar = b.src ? ear : b.w / b.h;
              return (
                <div key={b.id} className="ur-ev-thumb" style={{ width: Math.round(54 * ar) }} title={b.head || ''}>
                  {bannerThumb(b, 0.24)}
                </div>);
            })}
          </div>}
      </div>
    </div>);
}

/* 그룹 → 가이드 후보 도출 (배너 타입별 레이아웃 유형으로 파생) */
const TYPE_LAYOUTS = {
  'guide-a': ['누끼형', '분할+누끼형', '분할형(2분할)', '분할형(3분할)', '분할형(4분할)', '풀이미지형'],
  'guide-b': ['기본 타입', '방송시간 기재 타입', '로고강조+오늘혜택좋은브랜드 타입', '자유 타입', '쿠폰 타입'],
  'guide-c': ['누끼형', '분할+누끼형', '분할형(2분할)', '분할형(3분할)', '분할형(4분할)', '풀이미지형'],
};
function candidatesFromGroups(groups, isUrl) {
  const byId = { 'guide-a': GUIDE_A, 'guide-b': GUIDE_B, 'guide-c': GUIDE_C };
  if (isUrl) {
    const out = [];
    groups.forEach((g) => {
      const base = byId[g.ref] || GUIDE_A;
      const layouts = TYPE_LAYOUTS[g.ref] || base.layoutTypes || [base.name];
      layouts.forEach((lt, i) => {
        out.push({
          ...base, id: g.ref + '-' + i, _gid: g.ref + '-' + i,
          name: lt, category: base.bannerType, layoutTypes: [lt],
          banners: g.banners.length, _ev: g.banners, _prev: (g.banners[i % g.banners.length] || g.banners[0] || {}).src,
        });
      });
    });
    return out;
  }
  return groups.map((g) => ({
    id: g.id, _gid: g.id, name: g.name, category: '업로드',
    confidence: g.confidence || 88, banners: g.banners.length,
    palette: (g.palette || []).map((h) => ({ role: 'Color', hex: h, name: h })),
    typo: GUIDE_IMG.typo, logo: GUIDE_IMG.logo, _ev: g.banners, _prev: g.banners[0] && g.banners[0].src,
  }));
}

function CreateFlow({ mode, onGenerate, onCancel }) {
  const isUrl = mode === 'url';
  const [phase, setPhase] = React.useState(isUrl ? 'input' : 'upload'); // input/upload | analyzing | grouping | review
  const [url, setUrl] = React.useState('');
  const [images, setImages] = React.useState([]);
  const [drag, setDrag] = React.useState(false);
  const [progress, setProgress] = React.useState(0);
  const [groups, setGroups] = React.useState([]);
  const [picked, setPicked] = React.useState([]);
  const fileRef = React.useRef(null);
  const examples = ['myshop.com', 'gsshop.com', 'ssg.com'];
  const [dirty, setDirty] = React.useState(false);

  const totalBanners = groups.reduce((a, g) => a + g.banners.length, 0);
  const candidates = React.useMemo(() => candidatesFromGroups(groups, isUrl), [groups, isUrl]);
  const toggle = (gid) => setPicked((s) => s.includes(gid) ? s.filter((x) => x !== gid) : [...s, gid]);

  const addFiles = (fileList) => {
    [...fileList].forEach((f) => {
      if (!f.type || !f.type.startsWith('image/')) return;
      const r = new FileReader();
      r.onload = () => setImages((p) => [...p, { id: Math.random().toString(36).slice(2), src: r.result, name: f.name }]);
      r.readAsDataURL(f);
    });
  };

  const startUrl = () => {if (!url.trim()) return;setPhase('analyzing');};
  const startImage = () => {if (!images.length) return;setPhase('analyzing');};

  // ② 분석 → 그룹 분류
  React.useEffect(() => {
    if (phase !== 'analyzing') return;
    let imgGroups = null;
    if (!isUrl && window.analyzeBanners) {
      window.analyzeBanners(images.map((i) => i.src)).then((r) => {imgGroups = r && r.groups;}).catch(() => {});
    }
    let p = 0;
    const id = setInterval(() => {
      p += 4;setProgress(Math.min(100, p));
      if (p >= 100) {
        clearInterval(id);
        let gs;
        if (isUrl) {
          gs = (window.clusterCollected ? window.clusterCollected(COLLECTED) : []).map((g) => ({
            id: g.id, ref: g.ref, name: g.name, confidence: g.confidence,
            size: g.desc ? g.desc.split(' · ')[0] : '', banners: g.banners.map((b) => ({ ...b }))
          }));
        } else {
          const raw = imgGroups && imgGroups.length ? imgGroups : window.autoGroup ? window.autoGroup(images.map((i) => i.src)) : [];
          gs = raw.map((g) => ({ id: g.id, name: g.name, confidence: g.confidence, palette: g.palette, banners: (g.imgs || []).map((im) => ({ ...im })) }));
        }
        setGroups(gs);
        setDirty(false);
        setPhase('grouping');
      }
    }, 110);
    return () => clearInterval(id);
  }, [phase]);

  const STEPS = isUrl ?
  [{ key: 'crawl', t: '페이지 크롤링' }, { key: 'collect', t: 'PNG 배너 수집' }, { key: 'extract', t: '색상·효과 추출' }, { key: 'cluster', t: '디자인 유사도 분류' }] :
  [{ key: 'detect', t: '요소 인식' }, { key: 'extract', t: '색상·타이포 추출' }, { key: 'cluster', t: '디자인 유사도 분류' }, { key: 'guide', t: '그룹화' }];
  const stepIdx = Math.min(STEPS.length, Math.floor(progress / 100 * STEPS.length + 0.0001));

  const confirmGroups = () => {
    setPicked(candidates.map((c) => c._gid));
    setPhase('reanalyzing');
  };
  React.useEffect(() => {
    if (phase !== 'reanalyzing') return;
    const t = setTimeout(() => {setDirty(false);setPhase('review');}, 1400);
    return () => clearTimeout(t);
  }, [phase]);
  const selCount = candidates.filter((c) => picked.includes(c._gid)).length;
  const allSel = candidates.length > 0 && candidates.every((c) => picked.includes(c._gid));
  const handleGenerate = () => {
    if (isUrl) {
      onGenerate(candidates.filter((c) => picked.includes(c._gid)));
    } else {
      const chosen = groups.filter((g) => picked.includes(g.id));
      onGenerate(chosen.map((g) => ({ id: g.id, name: g.name, confidence: g.confidence, palette: g.palette, imgs: g.banners })));
    }
  };

  const s1 = phase === (isUrl ? 'input' : 'upload') ? 'active' : 'done';
  const s2 = phase === 'analyzing' ? 'active' : ['grouping', 'review'].includes(phase) ? 'done' : 'locked';
  const s3 = phase === 'grouping' ? 'active' : phase === 'review' ? 'done' : 'locked';
  const s4 = phase === 'review' || phase === 'reanalyzing' ? 'active' : 'locked';
  const srcWord = isUrl ? '크롤링한' : '업로드한';

  return (
    <div className="content"><div className="content-pad" style={{ maxWidth: 1000, paddingBottom: 96 }}>
      <div className="page-head" style={{ marginBottom: 22 }}>
        <h1 className="page-title">{isUrl ? 'URL로 배너 가이드 만들기' : '이미지로 배너 가이드 만들기'}</h1>
        <p className="page-sub">{isUrl ?
            '커머스 URL 입력으로 배너 분류부터 가이드 도출까지' :
            '유사 배너 자동 분류부터 스타일 가이드 도출까지'}</p>
      </div>

      <div className="flow">
        {/* ① 입력 */}
        {isUrl ?
          <FlowStep n={1} title="URL 입력" sub="운영 대상 커머스 주소를 입력하세요." state={s1}
          onEdit={() => {setPhase('input');setProgress(0);setGroups([]);}}
          summary={<span style={{ display: 'inline-flex', alignItems: 'center', gap: 8, flexWrap: 'wrap' }}>
              https://{url}
            </span>}>
            <div style={{ display: 'flex', gap: 10, alignItems: 'stretch' }}>
              <div className="input-group" style={{ flex: 1 }}>
                <span className="input-prefix">https://</span>
                <input className="input" value={url} onChange={(e) => setUrl(e.target.value)} placeholder="example.com" autoFocus />
              </div>
              <button className="btn btn-primary" style={{ borderRadius: 6, height: 42, flexShrink: 0 }} disabled={!url.trim()} onClick={startUrl}>
                <S2Icon name="zap" size={16} /> 분석 시작
              </button>
            </div>
            <div className="chip-row">
              {examples.map((ex) => <button key={ex} className="chip" onClick={() => setUrl(ex)}><S2Icon name="globe" size={13} /> {ex}</button>)}
            </div>
          </FlowStep> :
          <FlowStep n={1} title="이미지 업로드" sub="제작했던 배너 이미지를 올려주세요." state={s1}
          onEdit={() => {setPhase('upload');setProgress(0);setGroups([]);}}
          summary={<span>이미지 <b style={{ color: 'var(--text-2)' }}>{images.length}장</b> 업로드됨</span>}
          action={images.length > 0 ? <button className="btn btn-primary btn-sm" style={{ borderRadius: 6 }} onClick={startImage}><S2Icon name="zap" size={15} /> <span>{images.length}장 분석 시작</span></button> : null}>
            <input ref={fileRef} type="file" accept="image/*" multiple hidden onChange={(e) => {addFiles(e.target.files);e.target.value = '';}} />
            {images.length === 0 ?
            <div className={`upzone ${drag ? 'drag' : ''}`}
            onClick={() => fileRef.current && fileRef.current.click()}
            onDragOver={(e) => {e.preventDefault();setDrag(true);}} onDragLeave={() => setDrag(false)}
            onDrop={(e) => {e.preventDefault();setDrag(false);addFiles(e.dataTransfer.files);}}>
                <div className="upzone-ic"><S2Icon name="upload" size={26} /></div>
                <div className="upzone-t">배너 이미지를 끌어다 놓으세요</div>
                <div className="upzone-d">완성된 배너(JPG·PNG)를 여러 장 올리면<br />디자인이 비슷한 것끼리 자동으로 묶어 분석합니다.</div>
                <span className="btn btn-primary" style={{ borderRadius: 6, marginTop: 4, pointerEvents: 'none' }}><S2Icon name="image" size={15} /> 파일 선택</span>
                <div className="upzone-meta">JPG · PNG · 여러 장 가능 · 최대 30장</div>
              </div> :
            <React.Fragment>
                <div className="upload-head">
                  <div className="section-title" style={{ margin: 0 }}>업로드한 배너 <b style={{ color: 'var(--accent-text)' }}>{images.length}장</b></div>
                  <button className="btn btn-ghost btn-sm" onClick={() => setImages([])}>모두 지우기</button>
                </div>
                <div className="img-grid">
                  {images.map((im) =>
                <div key={im.id} className="img-thumb">
                      <img src={im.src} alt="" />
                      <button className="img-remove" onClick={() => setImages((p) => p.filter((x) => x.id !== im.id))}><S2Icon name="x" size={12} /></button>
                    </div>)}
                  <button className="img-add" onClick={() => fileRef.current && fileRef.current.click()}><S2Icon name="plus" size={16} /> 추가</button>
                </div>
              </React.Fragment>}
          </FlowStep>}

        {/* ② 분석 */}
        <FlowStep n={2} title="배너 분석" sub="디자인 요소를 분석하고 유사한 스타일별로 그룹을 생성합니다." state={s2}
          summary={<span>{srcWord} 배너 <b style={{ color: 'var(--text-2)' }}>{totalBanners}개</b> → <b style={{ color: 'var(--accent-text)' }}>{groups.length}개 그룹</b>으로 분류</span>}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 14, marginBottom: 20 }}>
            <div style={{ flex: 1, height: 8, borderRadius: 99, background: 'var(--bg-panel)', overflow: 'hidden', border: '1px solid var(--border)' }}>
              <div style={{ height: '100%', width: `${progress}%`, borderRadius: 99, background: 'linear-gradient(90deg,var(--accent),#7c6df0)', transition: 'width .12s linear' }} />
            </div>
            <span style={{ fontSize: 14, fontWeight: 700, color: 'var(--text-2)', fontVariantNumeric: 'tabular-nums', minWidth: 44, textAlign: 'right' }}>{Math.round(progress)}%</span>
          </div>
          <div className="flow-checks">
            {STEPS.map((s, i) => {
                const st = i < stepIdx ? 'done' : i === stepIdx ? 'active' : 'pending';
                return (
                  <div key={s.key} className={`flow-check ${st}`}>
                  <span className="flow-check-ic">
                    {st === 'done' ? <S2Icon name="check" size={12} strokeWidth={3} /> :
                      st === 'active' ? <span className="spinner" style={{ width: 13, height: 13 }} /> :
                      <span className="flow-check-dot" />}
                  </span>{s.t}
                </div>);
              })}
          </div>
        </FlowStep>

        {/* ③ 그룹 확인 */}
        <FlowStep n={3} title="그룹 확인 및 편집"
          sub={`유사한 디자인의 배너를 ${groups.length}개의 그룹으로 분류했습니다. 잘못 분류된 배너는 드래그하여 이동하거나 제외하세요.`}
          state={s3} onEdit={() => setPhase('grouping')}
          summary={<span>그룹 <b style={{ color: 'var(--text-2)' }}>{groups.length}개</b> 확정 · 배너 {totalBanners}개</span>}
          action={<button className="btn btn-primary btn-sm" style={{ borderRadius: 6 }} disabled={groups.length === 0} onClick={confirmGroups}>가이드 생성하기 <S2Icon name="arrowRight" size={14} /></button>}>
          <GroupEditor groups={groups} setGroups={setGroups} onDirty={() => setDirty(true)} />
        </FlowStep>

        {/* ④ 가이드 타입 확인 */}
        <FlowStep n={4} title="가이드 선택" sub="확정된 그룹을 바탕으로 도출된 후보 중 제작할 가이드를 선택하세요." state={s4}
          action={phase === 'review' && candidates.length ? <button type="button" className="flow-step-edit" onClick={() => setPicked(allSel ? [] : candidates.map((c) => c._gid))}>{allSel ? '전체 해제' : '전체 선택'}</button> : null}>
          {phase === 'reanalyzing' ?
            <div className="flow-reanalyze">
              <span className="spinner" />
              <div>
                <div className="flow-reanalyze-t">확정된 묶음을 기준으로 가이드 세부사항을 정리하는 중…</div>
                <div className="flow-reanalyze-d">색상·레이아웃·문구 규칙을 분석해 타입별 가이드 후보를 만듭니다.</div>
              </div>
            </div> :
            <div className="ur-cards">
              {candidates.map((c) =>
              <CandidateCard key={c._gid} g={c} on={picked.includes(c._gid)} onToggle={() => toggle(c._gid)} evidence={c._ev} />)}
            </div>}
        </FlowStep>
      </div>

      {phase === 'review' &&
        <div className="flow-genbar">
          <div style={{ fontSize: 13, color: 'var(--text-muted)' }}>
            {selCount > 0 ? `${selCount}개의 편집 가능한 가이드가 만들어집니다.` : '하나 이상 선택해 주세요.'}
          </div>
          <div style={{ marginLeft: 'auto', display: 'flex', gap: 10 }}>
            <button className="btn btn-secondary" style={{ borderRadius: 6 }} onClick={onCancel}>취소</button>
            <button className="btn btn-primary" style={{ borderRadius: 6 }} disabled={selCount === 0} onClick={handleGenerate}>
              <S2Icon name="sparkles" size={16} /> <span>선택한 가이드 <b>{selCount}</b>개 생성</span>
            </button>
          </div>
        </div>}
    </div></div>);
}

Object.assign(window, { CreateFlow, FlowStep, CandidateCard, GroupEditor, candidatesFromGroups });