/* ============================================================
   create-push.jsx — 앱푸시 사용자 화면 (template 모드)
   · window.SCHEMA_PUSH 를 읽어 유형(택1) → 해당 필드만 노출.
   · 공통 필수(이미지·문구2줄12자·로고). 자유 타입은 완성본 이미지 업로드만(공통 미적용).
   · 로고(좌상단)·수신거부 고지(좌하단)는 SCHEMA_PUSH.fixed 로 항상 렌더. 슬롯 좌표는 SCHEMA_PUSH.slots 단일 출처.
   · .s550 스코프 + S5Block/S5Chip/S5Slot(create-550.jsx 전역) 재사용 · 720×380 라이브 프리뷰.
   ============================================================ */
const { useState: up5, useRef: rp5 } = React;

// 앱푸시 결과 후보 — 첫 항목은 '원안'(편집 그대로), 나머지는 뚜렷이 구분되는 마감 변형.
// (고정 슬롯이라 구조가 아닌 마감·정렬만 다르게. 550의 구조 다양성과 다름.)
const PUSH_VARIANTS = [
  { id: 'orig',   label: '원안',      scrim: 'none',   align: 'left',   original: true },
  { id: 'left',   label: '좌측 강조', scrim: 'left',   align: 'left' },
  { id: 'bottom', label: '하단 밴드', scrim: 'bottom', align: 'left',   copyAnchor: 'bottom' },
  { id: 'center', label: '가운데',    scrim: 'full',   align: 'center' },
];

function PushUploader({ src, onPick, onClear, label, h }) {
  const ref = rp5(null);
  const pick = (e) => { const f = e.target.files && e.target.files[0]; if (!f) return; const r = new FileReader(); r.onload = () => onPick(r.result); r.readAsDataURL(f); e.target.value = ''; };
  return (
    <>
      <input ref={ref} type="file" accept="image/*" style={{ display: 'none' }} onChange={pick} />
      {src ?
        <div className="thumbs"><div className="thumb" style={{ width: h || 74, height: h || 74 }}><img src={src} alt="" /><button onClick={onClear}>×</button></div></div> :
        <div className="slot up" style={{ height: h || 80 }} onClick={() => ref.current.click()}>＋ {label}</div>}
    </>);
}

function PushPreview({ t, img, copy, logo, finalImg, fields, variant }) {
  // SSOT(schema-push.js)의 canvas/slots/fixed 좌표(720×380 절대px)를 읽어 %·컨테이너단위로 환산.
  const SCH = window.SCHEMA_PUSH;
  const CW = SCH.canvas.w, CH = SCH.canvas.h, S = SCH.slots, F = SCH.fixed;
  const V = variant || { scrim: 'none', align: 'left' };
  const cover = (s) => ({ backgroundImage: `url(${s})`, backgroundSize: 'cover', backgroundPosition: 'center' });
  const L = (v) => `${v / CW * 100}%`;              // x → left%
  const T = (v) => `${v / CH * 100}%`;              // y → top/bottom%
  const fs = (px) => `${px / CW * 100}cqw`;         // 폰트: 컨테이너 폭 기준 스케일
  const ch = (px) => `${px / CH * 100}cqh`;         // 높이: 컨테이너 높이 기준
  const box = { position: 'relative', containerType: 'size', aspectRatio: '720 / 380' };

  if (t.fullUpload) {
    return (
      <div className="canvas550" style={{ ...box, ...(finalImg ? cover(finalImg) : {}) }}>
        {!finalImg && <div className="z fill" style={{ inset: 0 }}><span>완성본 이미지</span></div>}
      </div>);
  }
  const lines = fields.lines || 'l1';
  const scrimBg = V.scrim === 'bottom' ? 'linear-gradient(0deg, rgba(0,0,0,.72), rgba(0,0,0,.12) 46%, rgba(0,0,0,0) 64%)' :
                  V.scrim === 'left'   ? 'linear-gradient(90deg, rgba(0,0,0,.72), rgba(0,0,0,.16) 58%, rgba(0,0,0,0) 80%)' :
                  V.scrim === 'full'   ? 'linear-gradient(0deg, rgba(0,0,0,.42), rgba(0,0,0,.42))' : null;
  const copyPos = V.align === 'center' ?
    { left: '50%', top: T(S.copy.cy), transform: 'translate(-50%,-50%)', textAlign: 'center', maxWidth: '84%' } :
    V.copyAnchor === 'bottom' ?
    { left: L(S.copy.x), bottom: T(96), maxWidth: L(S.copy.maxW) } :
    { left: L(S.copy.x), top: T(S.copy.cy), transform: 'translateY(-50%)', maxWidth: L(S.copy.maxW) };
  const footer = (
    <div style={{ position: 'absolute', left: L(F.footer.slot.x), bottom: T(F.footer.slot.bottom), fontSize: fs(F.footer.slot.size), color: F.footer.slot.color, zIndex: 6, whiteSpace: 'nowrap' }}>{F.footer.text}</div>);
  return (
    <div className="canvas550" style={{ ...box, ...(img ? cover(img) : {}) }}>
      {!img && <div className="z fill" style={{ inset: 0 }}><span>메인 이미지</span></div>}
      {scrimBg && <div style={{ position: 'absolute', inset: 0, zIndex: 1, background: scrimBg }} />}

      {/* 로고 (좌상단 · 시스템 고정 · 사용자 업로드) */}
      <div style={{ position: 'absolute', left: L(F.logo.slot.x), top: T(F.logo.slot.y), zIndex: 6 }}>
        {logo ? <img src={logo} alt="" style={{ height: ch(F.logo.slot.h), objectFit: 'contain' }} /> :
          <div style={{ fontSize: fs(15), fontWeight: 800, color: '#4E4CDB' }}>LOGO</div>}
      </div>

      {/* 로고강조: 브랜드 로고 (콘텐츠 영역) */}
      {t.id === 'logohit' && (fields.brandLogo ?
        <img src={fields.brandLogo} alt="" style={{ position: 'absolute', left: L(S.brandLogo.x), top: T(S.brandLogo.top), maxWidth: L(S.brandLogo.maxW), height: ch(S.brandLogo.h), objectFit: 'contain', zIndex: 5 }} /> :
        <div style={{ position: 'absolute', left: L(S.brandLogo.x), top: T(S.brandLogo.top), fontSize: fs(28), fontWeight: 900, color: '#fff', zIndex: 5 }}>BRAND</div>)}

      {/* 메인 문구 (2줄 · variant 정렬/위치) */}
      <div style={{ position: 'absolute', zIndex: 5, ...copyPos }}>
        <div style={{ fontSize: fs(S.copy.size), fontWeight: S.copy.weight, color: S.copy.color, textShadow: '0 0.3cqh 1.6cqh rgba(0,0,0,.32)', lineHeight: 1.2, whiteSpace: 'pre-line', display: '-webkit-box', WebkitLineClamp: 2, WebkitBoxOrient: 'vertical', overflow: 'hidden' }}>{copy || '메인 문구'}</div>
        {t.id === 'logohit' && <div style={{ fontSize: fs(S.subcopy.size), fontWeight: 600, color: S.subcopy.color, opacity: .95, marginTop: '1.4cqh', lineHeight: 1.3 }}>{fields.subcopy || '서브카피'}{lines === 'l2' ? <><br />두 번째 줄</> : null}</div>}
        {t.id === 'coupon' && <div style={{ fontSize: fs(S.benefit.size), fontWeight: S.benefit.weight, color: S.benefit.color, marginTop: '1.6cqh', textShadow: '0 0.3cqh 1.6cqh rgba(0,0,0,.32)' }}>{fields.benefit || '10%적립 + ~7%카드'}</div>}
      </div>

      {/* 방송시간 배지 (고지 위) */}
      {t.id === 'airtime' && <div style={{ position: 'absolute', left: L(S.airtime.x), bottom: T(S.airtime.bottom), display: 'flex', alignItems: 'center', gap: '1.4cqh', zIndex: 5 }}>
        <span style={{ background: '#e5342e', color: '#fff', fontWeight: 800, fontSize: fs(14), padding: '0.4cqh 1.4cqh', borderRadius: '0.8cqh' }}>{fields.badge || 'TV'}</span>
        <span style={{ color: '#fff', fontWeight: 800, fontSize: fs(S.airtime.size), textShadow: '0 0.3cqh 1.6cqh rgba(0,0,0,.3)' }}>{fields.airtime || '01/29(목) 11:35'}</span>
      </div>}

      {footer}
    </div>);
}

function PushCreate({ guide, onBack, onGenerate }) {
  const SCH = window.SCHEMA_PUSH;
  const [type, setType] = up5((guide && guide.pushType && SCH.types.some((t) => t.id === guide.pushType)) ? guide.pushType : SCH.types[0].id);
  const [img, setImg] = up5(null);
  const [copy, setCopy] = up5('');
  const [logo, setLogo] = up5(null);
  const [finalImg, setFinalImg] = up5(null);
  const [fields, setFields] = up5({});
  const setF = (k, v) => setFields((s) => ({ ...s, [k]: v }));
  const t = SCH.types.find((x) => x.id === type) || SCH.types[0];

  const canGen = t.fullUpload ? !!finalImg : !!img;

  return (
    <React.Fragment>
    <CreateHead category={guide.category} onBack={onBack}>
      <TypeSelect value={type} options={SCH.types} onChange={setType} />
    </CreateHead>
    <div className="s550"><div className="wrap">
      <div className="left">
        {t.fullUpload ?
          <S5Block node={{ label: '완성본 이미지', role: 'required', input: 'image', note: '완전 자유 · 완성본 업로드' }}>
            <PushUploader src={finalImg} onPick={setFinalImg} onClear={() => setFinalImg(null)} label="완성본 이미지 업로드" h={120} />
          </S5Block> :
          <>
            {/* 공통 필수 */}
            <S5Block node={{ label: '메인 이미지', role: 'required', input: 'image' }}>
              <PushUploader src={img} onPick={setImg} onClear={() => setImg(null)} label="이미지 업로드" />
            </S5Block>
            <S5Block node={{ label: '문구', role: 'required', input: 'text', note: '2줄 · 줄당 최대 12자' }}>
              <div className="textfield"><textarea className="tf" rows={2} value={copy} maxLength={25} placeholder={"예: 약손명가 탄력관리\n단하루 전고객 케이스"} onChange={(e) => setCopy(e.target.value)} style={{ resize: 'none', fontFamily: 'inherit' }} /><div className="tf-note">{copy.length}/25자 · 줄바꿈(Enter)으로 2줄</div></div>
            </S5Block>
            <S5Block node={{ label: '로고 (좌상단)', role: 'required', input: 'image', note: '사용자 업로드' }}>
              <PushUploader src={logo} onPick={setLogo} onClear={() => setLogo(null)} label="로고 업로드" h={64} />
            </S5Block>
            {/* 유형별 필드 */}
            {t.fields.map((f) => (
              <S5Block key={f.id} node={{ label: f.label, role: f.required ? 'required' : 'optional', input: f.input, note: f.note }}>
                {f.input === 'image' ?
                  <PushUploader src={fields[f.id]} onPick={(u) => setF(f.id, u)} onClear={() => setF(f.id, null)} label={f.label + ' 업로드'} h={64} /> :
                f.input === 'choice' && f.options ?
                  <div className="chips">{f.options.map((o) => <S5Chip key={o.id} on={(fields[f.id] || f.options[0].id) === o.id} label={o.label} onClick={() => setF(f.id, o.id)} />)}</div> :
                  <div className="textfield"><input className="tf" value={fields[f.id] || ''} maxLength={f.maxLen || undefined} placeholder={f.note || f.label} onChange={(e) => setF(f.id, e.target.value)} /></div>}
              </S5Block>))}
          </>}

        <button className="btn btn-primary btn-lg btn-block" style={{ marginTop: 6, borderRadius: '6px' }} disabled={!canGen}
          onClick={() => onGenerate({ content: { head: copy }, image: t.fullUpload ? finalImg : img, sty: window.STYLE_A, push: { type, fields, logo, copy, finalImg } })}>
          <S2Icon name="sparkles" size={17} /> 배너 생성하기
        </button>
      </div>

      <div className="right">
        <div className="preview-card">
          <PushPreview t={t} img={img} copy={copy} logo={logo} finalImg={finalImg} fields={fields} />
          <div className="summary">
            <div className="summary-h">활성 노드</div>
            <div className="summary-list">
              <div className="srow"><span className="sk">사이즈</span><span className="sv">720 × 380</span></div>
              <div className="srow"><span className="sk">유형</span><span className="sv">{t.label}</span></div>
              <div className="srow"><span className="sk">문구</span><span className="sv">{t.fullUpload ? '—' : (copy || '없음')}</span></div>
              {t.fields.map((f) => <div className="srow" key={f.id}><span className="sk">{f.label}</span><span className="sv">{f.input === 'choice' ? (f.options.find((o) => o.id === (fields[f.id] || f.options[0].id)) || {}).label : (fields[f.id] || '없음')}</span></div>)}
            </div>
          </div>
        </div>
      </div>
    </div></div>
    </React.Fragment>);
}

// 앱푸시 720×380 템플릿을 캔버스에 래스터화 → PNG Blob. (프리뷰 PushPreview와 동일 슬롯/변형 기준)
async function rasterizePush({ t, img, copy, logo, fields = {}, variant }) {
  const SCH = window.SCHEMA_PUSH;
  const CW = SCH.canvas.w, CH = SCH.canvas.h, S = SCH.slots, F = SCH.fixed;
  const V = variant || { scrim: 'none', align: 'left' };
  const cv = document.createElement('canvas'); cv.width = CW; cv.height = CH;
  const ctx = cv.getContext('2d');
  const load = (src) => new Promise((r) => { if (!src) return r(null); const im = new Image(); im.crossOrigin = 'anonymous'; im.onload = () => r(im); im.onerror = () => r(null); im.src = src; });
  const toBlob = () => new Promise((r) => cv.toBlob(r, 'image/png'));
  const drawCover = (im) => { const s = Math.max(CW / im.width, CH / im.height); const w = im.width * s, h = im.height * s; ctx.drawImage(im, (CW - w) / 2, (CH - h) / 2, w, h); };

  ctx.fillStyle = '#111'; ctx.fillRect(0, 0, CW, CH);
  const bg = await load(img); if (bg) drawCover(bg);
  if (t.fullUpload) return toBlob();

  // scrim (프리뷰 PushPreview와 동일 강도)
  if (V.scrim === 'left') { const g = ctx.createLinearGradient(0, 0, CW * 0.8, 0); g.addColorStop(0, 'rgba(0,0,0,.72)'); g.addColorStop(0.58, 'rgba(0,0,0,.16)'); g.addColorStop(1, 'rgba(0,0,0,0)'); ctx.fillStyle = g; ctx.fillRect(0, 0, CW, CH); }
  else if (V.scrim === 'bottom') { const g = ctx.createLinearGradient(0, CH, 0, CH * 0.36); g.addColorStop(0, 'rgba(0,0,0,.72)'); g.addColorStop(0.54, 'rgba(0,0,0,.12)'); g.addColorStop(1, 'rgba(0,0,0,0)'); ctx.fillStyle = g; ctx.fillRect(0, 0, CW, CH); }
  else if (V.scrim === 'full') { ctx.fillStyle = 'rgba(0,0,0,.42)'; ctx.fillRect(0, 0, CW, CH); }

  // 로고(좌상단)
  const lg = await load(logo); if (lg) { const h = F.logo.slot.h * 1.5, w = lg.width * (h / lg.height); ctx.drawImage(lg, F.logo.slot.x, F.logo.slot.y, w, h); }
  // 브랜드 로고(로고강조)
  if (t.id === 'logohit') { const bl = await load(fields.brandLogo); if (bl) { const h = S.brandLogo.h, w = Math.min(S.brandLogo.maxW, bl.width * (h / bl.height)); ctx.drawImage(bl, S.brandLogo.x, S.brandLogo.top, w, h); } }

  // 메인 문구(최대 2줄)
  const lines = String(copy || '').split('\n').slice(0, 2);
  ctx.fillStyle = S.copy.color; ctx.textBaseline = 'middle';
  ctx.textAlign = V.align === 'center' ? 'center' : 'left';
  ctx.font = `${S.copy.weight} ${S.copy.size}px Pretendard, system-ui, sans-serif`;
  const cx = V.align === 'center' ? CW / 2 : S.copy.x, lh = S.copy.size * 1.2;
  const cyBase = V.copyAnchor === 'bottom' ? (CH - 104) : S.copy.cy;
  const y0 = cyBase - (lines.length - 1) * lh / 2;
  lines.forEach((ln, i) => ctx.fillText(ln, cx, y0 + i * lh));

  // 쿠폰 혜택(좌측 카피)
  if (t.id === 'coupon' && fields.benefit) { ctx.font = `${S.benefit.weight} ${S.benefit.size}px Pretendard, system-ui, sans-serif`; ctx.fillStyle = S.benefit.color; ctx.fillText(fields.benefit, cx, S.benefit.top + S.benefit.size / 2); }
  // 방송시간 배지
  if (t.id === 'airtime') {
    ctx.textAlign = 'left'; ctx.textBaseline = 'alphabetic';
    const by = CH - S.airtime.bottom, badge = fields.badge || 'TV';
    ctx.font = `800 14px Pretendard, system-ui, sans-serif`; const bw = ctx.measureText(badge).width + 16;
    ctx.fillStyle = '#e5342e'; ctx.fillRect(S.airtime.x, by - 15, bw, 22); ctx.fillStyle = '#fff'; ctx.fillText(badge, S.airtime.x + 8, by + 1);
    ctx.font = `800 ${S.airtime.size}px Pretendard, system-ui, sans-serif`; ctx.fillStyle = '#fff'; ctx.fillText(fields.airtime || '', S.airtime.x + bw + 10, by + 2);
  }

  // 수신거부 고지(좌하단 · 필수)
  ctx.textAlign = 'left'; ctx.textBaseline = 'alphabetic';
  ctx.font = `400 ${F.footer.slot.size}px Pretendard, system-ui, sans-serif`; ctx.fillStyle = F.footer.slot.color;
  ctx.fillText(F.footer.text, F.footer.slot.x, CH - F.footer.slot.bottom);
  return toBlob();
}

Object.assign(window, { PushCreate, PushPreview, PUSH_VARIANTS, rasterizePush });
