/* ============================================================
   components.jsx — UI components for Banner Forge.
   All components attached to window at the bottom for cross-script use.
   ============================================================ */

const { useState, useEffect, useRef, useCallback, useMemo } = React;

/* -------- Icons (inline SVG) -------- */
const Icon = ({ name, size = 14 }) => {
  const paths = {
    upload: <><path d="M12 3v12M6 9l6-6 6 6M5 21h14" /></>,
    folder: <><path d="M3 7a2 2 0 0 1 2-2h4l2 2h8a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V7z" /></>,
    download: <><path d="M12 3v12M6 11l6 6 6-6M5 21h14" /></>,
    spec: <><path d="M4 4h16v16H4z" /><path d="M4 9h16M9 4v16" /></>,
    image: <><rect x="3" y="3" width="18" height="18" rx="2" /><path d="M3 16l5-5 4 4 3-3 6 6" /><circle cx="9" cy="9" r="1.5" /></>,
    sparkle: <><path d="M12 3l1.5 4.5L18 9l-4.5 1.5L12 15l-1.5-4.5L6 9l4.5-1.5L12 3zM19 14l.8 2.2L22 17l-2.2.8L19 20l-.8-2.2L16 17l2.2-.8L19 14z" /></>,
    play: <><polygon points="6 4 20 12 6 20 6 4" /></>,
    refresh: <><path d="M21 12a9 9 0 0 1-15.36 6.36L3 16M3 12a9 9 0 0 1 15.36-6.36L21 8M21 3v5h-5M3 21v-5h5" /></>,
    grid: <><rect x="3" y="3" width="7" height="7" /><rect x="14" y="3" width="7" height="7" /><rect x="3" y="14" width="7" height="7" /><rect x="14" y="14" width="7" height="7" /></>,
    eye: <><path d="M2 12s3.5-7 10-7 10 7 10 7-3.5 7-10 7S2 12 2 12z" /><circle cx="12" cy="12" r="3" /></>,
    eyeoff: <><path d="M3 3l18 18M10.6 6.1A11 11 0 0 1 12 6c6.5 0 10 7 10 7a17.6 17.6 0 0 1-3.4 4.3M6.1 6.1A17 17 0 0 0 2 12s3.5 7 10 7a11 11 0 0 0 5.9-1.7" /><path d="M9.9 9.9a3 3 0 0 0 4.2 4.2" /></>,
    settings: <><circle cx="12" cy="12" r="3" /><path d="M19.4 15a1.7 1.7 0 0 0 .3 1.8l.1.1a2 2 0 1 1-2.8 2.8l-.1-.1a1.7 1.7 0 0 0-1.8-.3 1.7 1.7 0 0 0-1 1.5V21a2 2 0 1 1-4 0v-.1a1.7 1.7 0 0 0-1.1-1.5 1.7 1.7 0 0 0-1.8.3l-.1.1A2 2 0 1 1 4.3 17l.1-.1a1.7 1.7 0 0 0 .3-1.8 1.7 1.7 0 0 0-1.5-1H3a2 2 0 1 1 0-4h.1a1.7 1.7 0 0 0 1.5-1.1 1.7 1.7 0 0 0-.3-1.8l-.1-.1a2 2 0 1 1 2.8-2.8l.1.1a1.7 1.7 0 0 0 1.8.3H9a1.7 1.7 0 0 0 1-1.5V3a2 2 0 1 1 4 0v.1a1.7 1.7 0 0 0 1 1.5 1.7 1.7 0 0 0 1.8-.3l.1-.1a2 2 0 1 1 2.8 2.8l-.1.1a1.7 1.7 0 0 0-.3 1.8V9c.4.5.9 1 1.5 1H21a2 2 0 1 1 0 4h-.1a1.7 1.7 0 0 0-1.5 1z" /></>,
    trash: <><path d="M3 6h18M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6" /></>,
    check: <><polyline points="20 6 9 17 4 12" /></>,
    code: <><polyline points="16 18 22 12 16 6" /><polyline points="8 6 2 12 8 18" /></>,
    bolt: <><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" /></>,
    zoom_in: <><circle cx="11" cy="11" r="7" /><path d="M21 21l-4.3-4.3M11 8v6M8 11h6" /></>,
    zoom_out: <><circle cx="11" cy="11" r="7" /><path d="M21 21l-4.3-4.3M8 11h6" /></>,
    expand: <><polyline points="15 3 21 3 21 9" /><polyline points="9 21 3 21 3 15" /><line x1="21" x2="14" y1="3" y2="10" /><line x1="3" x2="10" y1="21" y2="14" /></>,
    target: <><circle cx="12" cy="12" r="9" /><circle cx="12" cy="12" r="5" /><circle cx="12" cy="12" r="1.5" /></>,
  };
  return (
    <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.7" strokeLinecap="round" strokeLinejoin="round">
      {paths[name]}
    </svg>
  );
};

/* -------- Top Bar -------- */
const TopBar = ({ sourceName, banners, onExport, exporting, mode, onBackToEntry }) => {
  const doneCount = banners.filter(b => b.status === "done").length;
  const total = banners.length;
  const allDone = total > 0 && doneCount === total;
  const modeLabel = mode === "a" ? "A · 배너 확장"
                  : mode === "b" ? "B · 상품 일괄"
                  : "";
  return (
    <div className="topbar">
      <button
        className="logo"
        onClick={onBackToEntry}
        title={onBackToEntry ? "모드 선택 화면으로" : ""}
        style={{
          background: "transparent",
          border: "none",
          padding: 0,
          color: "inherit",
          cursor: onBackToEntry ? "pointer" : "default",
          font: "inherit",
          display: "flex",
          alignItems: "center",
          gap: 8,
        }}
      >
        <div className="logo-mark" />
        <span>Banner Forge</span>
      </button>
      <div className="crumbs">
        <span className="sep">/</span>
        {modeLabel && (
          <>
            <span className="current">{modeLabel}</span>
            <span className="sep">/</span>
          </>
        )}
        <span className="current">{sourceName || "Untitled"}</span>
      </div>
      <div className="spacer" />
      <div className="meta">
        <span className="pill">
          <span className={"dot " + (allDone ? "live" : "")} />
          {doneCount}/{total} ready
        </span>
        <span className="pill">v0.4.0</span>
      </div>
      <button
        className="btn primary"
        disabled={!allDone || exporting}
        onClick={onExport}
      >
        <Icon name="download" />
        {exporting ? "Packing…" : "Export ZIP"}
      </button>
    </div>
  );
};

/* -------- Dropzone (source upload) -------- */
const Dropzone = ({ onFile, onFiles, multi = false, title, sub }) => {
  const [over, setOver] = useState(false);
  const inputRef = useRef(null);
  const handleFiles = files => {
    if (!files || !files.length) return;
    if (multi && onFiles) {
      const imgs = Array.from(files).filter(f => f.type.startsWith("image/"));
      if (!imgs.length) return alert("이미지 파일만 업로드해주세요");
      onFiles(imgs);
      return;
    }
    const f = files[0];
    if (!f) return;
    if (!f.type.startsWith("image/")) return alert("이미지 파일만 업로드해주세요");
    if (onFile) onFile(f);
  };
  return (
    <div
      className={"dropzone " + (over ? "over" : "")}
      onDragOver={e => { e.preventDefault(); setOver(true); }}
      onDragLeave={() => setOver(false)}
      onDrop={e => { e.preventDefault(); setOver(false); handleFiles(e.dataTransfer.files); }}
      onClick={() => inputRef.current && inputRef.current.click()}
    >
      <div className="icon"><Icon name="upload" size={18} /></div>
      <div className="title">{title || (multi ? "상품 이미지 업로드 (여러 장 OK)" : "정방향 원본 배너 업로드")}</div>
      <div className="sub">{sub || "PNG · JPG · WebP"}</div>
      <input
        ref={inputRef}
        type="file"
        accept="image/*"
        multiple={multi}
        style={{ display: "none" }}
        onChange={e => handleFiles(e.target.files)}
      />
    </div>
  );
};

/* -------- Source Card -------- */
const SourceCard = ({ src, name, dim, onReplace, onRemove }) => (
  <div className="source-card">
    <div className="thumb" style={{ backgroundImage: `url(${src})` }} />
    <div className="meta">
      <div className="name">{name}</div>
      <div className="sub">{dim ? `${dim.w} × ${dim.h}` : "—"}</div>
    </div>
    <button className="icon-btn" title="교체" onClick={onReplace}>
      <Icon name="refresh" />
    </button>
    <button className="icon-btn" title="삭제" onClick={onRemove}>
      <Icon name="trash" />
    </button>
  </div>
);

/* -------- Banner row (sidebar list) -------- */
const BannerRow = ({ spec, banner, selected, onSelect, onRegen }) => {
  // Compute scaled swatch with shared scale across the list
  const maxDim = 540; // BeautyHomeMain height
  const scale = 36 / maxDim;
  return (
    <div
      className={"banner-row " + (selected ? "selected" : "")}
      onClick={onSelect}
    >
      <div
        className="swatch"
        style={{
          width: spec.w * scale,
          height: spec.h * scale,
          backgroundImage: banner.dataUrl ? `url(${banner.dataUrl})` : "none",
          backgroundSize: "cover",
          backgroundPosition: "center",
        }}
      />
      <div style={{ minWidth: 0 }}>
        <div className="name">{spec.name}</div>
        <div className="ratio">{spec.w} × {spec.h}</div>
      </div>
      <div>
        <span className={"status " + banner.status}>
          {banner.status === "idle"  && "—"}
          {banner.status === "gen"   && `${banner.progress || 0}%`}
          {banner.status === "done"  && "OK"}
          {banner.status === "err"   && "ERR"}
        </span>
      </div>
    </div>
  );
};

/* -------- Spec block (editable JSON for one shot) -------- */
const SpecBlock = ({ spec, shotSpec, onChange }) => {
  const [open, setOpen] = useState(false);
  const text = useMemo(() => JSON.stringify(shotSpec, null, 2), [shotSpec]);
  const directive = shotSpec.user_directive || "";
  return (
    <div className="spec-block fadein">
      <div className="spec-head">
        <span>{spec.name}</span>
        <span className="ratio-tag">{spec.w} × {spec.h}</span>
      </div>

      {/* Direct instruction — highest-priority free-form input */}
      <div className="field" style={{ marginBottom: 6 }}>
        <div className="field-label" style={{ display: "flex", alignItems: "center", gap: 6 }}>
          <Icon name="bolt" size={11} />
          <span>직접 지시</span>
          {directive.trim() && (
            <span className="chip" style={{
              background: "var(--accent-soft)",
              color: "var(--accent)",
              fontSize: 9,
              padding: "1px 5px",
              marginLeft: "auto",
            }}>ACTIVE</span>
          )}
        </div>
        <textarea
          className="textarea"
          value={directive}
          placeholder={`이 배너만의 지시를 자연어로 적어주세요.
예) "배경을 따뜻한 베이지로", "상품을 더 크게 + 그림자 강조", "왼쪽에 여백 더 많이"`}
          onChange={e => onChange({ ...shotSpec, user_directive: e.target.value })}
          rows={3}
          style={{
            fontSize: 12,
            lineHeight: 1.5,
            background: directive.trim() ? "var(--accent-soft)" : undefined,
          }}
        />
        <div style={{ fontSize: 10, color: "var(--text-3)", marginTop: 4, lineHeight: 1.45 }}>
          이 텍스트는 프롬프트 최상단에 HIGH-PRIORITY로 주입돼 다른 자동 설정보다 우선합니다.
          (레이아웃 안전영역은 그대로 유지)
        </div>
      </div>

      <button
        className="btn ghost sm"
        style={{ padding: "3px 6px", marginBottom: 4 }}
        onClick={() => setOpen(o => !o)}
      >
        <Icon name="code" size={11} />
        {open ? "Hide JSON" : "Edit JSON (고급)"}
      </button>
      {open && (
        <textarea
          className="textarea"
          value={text}
          onChange={e => {
            try { onChange(JSON.parse(e.target.value)); }
            catch { /* ignore parse errors during typing */ }
          }}
          rows={10}
        />
      )}
    </div>
  );
};

/* -------- Banner Card on canvas -------- */
const BannerCard = ({
  spec, banner, scale,
  selected, onSelect,
  showZones, showAnchor, showBbox,
  onAnchorChange, onRegen,
}) => {
  const w = spec.w * scale;
  const h = spec.h * scale;
  const frameRef = useRef(null);
  const [dragging, setDragging] = useState(false);

  const anchorX = (banner.anchor || spec.anchor).x;
  const anchorY = (banner.anchor || spec.anchor).y;

  const onMouseDown = e => {
    e.preventDefault(); e.stopPropagation();
    setDragging(true);
  };
  useEffect(() => {
    if (!dragging) return;
    const onMove = e => {
      if (!frameRef.current) return;
      const r = frameRef.current.getBoundingClientRect();
      const x = Math.max(0.05, Math.min(0.95, (e.clientX - r.left) / r.width));
      const y = Math.max(0.05, Math.min(0.95, (e.clientY - r.top) / r.height));
      onAnchorChange({ x, y });
    };
    const onUp = () => setDragging(false);
    window.addEventListener("mousemove", onMove);
    window.addEventListener("mouseup", onUp);
    return () => {
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseup", onUp);
    };
  }, [dragging, onAnchorChange]);

  return (
    <div className="banner-card">
      <div className="card-tools">
        <button className="btn ghost sm" onClick={onRegen} title="이 배너만 재생성">
          <Icon name="refresh" size={11} /> Regen
        </button>
      </div>
      <div
        ref={frameRef}
        className={"banner-frame " + (selected ? "selected" : "")}
        style={{ width: w, height: h }}
        onClick={onSelect}
      >
        {banner.dataUrl && (
          <img
            className={"banner-img " + (banner.status === "done" ? "ready" : "")}
            src={banner.dataUrl}
            alt={spec.name}
          />
        )}
        {(banner.status === "gen" || banner.status === "idle") && (
          <>
            <div className="skel" />
            <div className="skel-overlay">
              <div>{banner.status === "gen" ? "OUTPAINTING…" : "READY"}</div>
              <div className="skel-bar">
                <span style={{ width: `${banner.progress || 0}%` }} />
              </div>
            </div>
          </>
        )}

        {banner.status === "err" && (
          <div className="err-overlay">
            <div className="err-icon">!</div>
            <div className="err-title">AI 호출 실패</div>
            <div className="err-msg" title={banner.errorMsg || ""}>
              {banner.errorMsg || "Endpoint 확인 후 다시 시도하세요"}
            </div>
          </div>
        )}

        {/* Bbox overlay (hybrid pipeline debug) */}
        {showBbox && banner.lastCrop && banner.lastCrop.bbox && (() => {
          const c = banner.lastCrop;
          const b = c.bbox;
          // bbox in banner-card percent coords (clamped for display).
          const left   = ((b.x - c.sx) / c.sw) * 100;
          const top    = ((b.y - c.sy) / c.sh) * 100;
          const width  = (b.w / c.sw) * 100;
          const height = (b.h / c.sh) * 100;
          const clipped = (c.clippedAxes || []).length > 0;
          return (
            <div
              className={"bbox-overlay " + (clipped ? "clipped" : "ok")}
              style={{ left: left + "%", top: top + "%", width: width + "%", height: height + "%" }}
            >
              <span className="bbox-label">
                {clipped ? "⚠ CLIPPED" : "PRODUCT"} · {Math.round((b.confidence || 0) * 100)}%
              </span>
            </div>
          );
        })()}

        {/* Zones overlay — 4-side safe-area gutters per official guide */}
        <div className={"zones " + (showZones ? "show" : "")}>
          {spec.allowed.y > 0 && (
            <div className="red" style={{
              left: 0, top: 0, right: 0,
              height: `${spec.allowed.y * 100}%`,
            }} />
          )}
          {(spec.allowed.y + spec.allowed.h) < 1 && (
            <div className="red" style={{
              left: 0, right: 0, bottom: 0,
              height: `${(1 - spec.allowed.y - spec.allowed.h) * 100}%`,
            }} />
          )}
          {spec.allowed.x > 0 && (
            <div className="red" style={{
              left: 0,
              top: `${spec.allowed.y * 100}%`,
              width: `${spec.allowed.x * 100}%`,
              height: `${spec.allowed.h * 100}%`,
            }} />
          )}
          {(spec.allowed.x + spec.allowed.w) < 1 && (
            <div className="red" style={{
              right: 0,
              top: `${spec.allowed.y * 100}%`,
              width: `${(1 - spec.allowed.x - spec.allowed.w) * 100}%`,
              height: `${spec.allowed.h * 100}%`,
            }} />
          )}
          <div className="allowed" style={{
            left: `${spec.allowed.x*100}%`,
            top: `${spec.allowed.y*100}%`,
            width: `${spec.allowed.w*100}%`,
            height: `${spec.allowed.h*100}%`,
          }} />
        </div>

        {/* Anchor handle */}
        {showAnchor && (
          <div
            className="anchor-handle"
            style={{
              left: `${anchorX * 100}%`,
              top: `${anchorY * 100}%`,
            }}
            onMouseDown={onMouseDown}
            title="드래그하여 제품 위치 조정"
          />
        )}
      </div>
      <div className="banner-cap">
        <span className="nm">{spec.name}</span>
        <span className="sz">
          {spec.w} × {spec.h} · {spec.id}
          {banner.lastSource && (
            <span className="chip" style={{
              marginLeft: 6,
              background: banner.lastSource === "ai" ? "var(--accent-soft)" : "var(--bg-3)",
              color: banner.lastSource === "ai" ? "var(--accent)" : "var(--text-2)",
              fontSize: 9,
              padding: "1px 5px",
            }}>{banner.lastSource === "ai" ? "AI" : banner.lastSource}</span>
          )}
        </span>
      </div>
    </div>
  );
};

/* -------- Empty state -------- */
const EmptyState = ({ title, desc }) => (
  <div className="empty-state fadein">
    <div className="big-icon">
      <Icon name="sparkle" size={28} />
    </div>
    <h2>{title || "정방향 원본 배너를 업로드하세요"}</h2>
    <p>
      {desc || (<>1:1 비율 제품 컷 1장을 올리면, AI 아웃페인팅으로
        <br />
        <strong style={{color: "var(--text-1)"}}>4가지 배너 사이즈(375×540, 335×335, 150×332, 197×482)</strong>를
        자동 생성하고 ZIP으로 한 번에 다운로드합니다.</>)}
    </p>
    {!title && (
      <p style={{fontSize: 12, color: "var(--text-3)"}}>
        좌측 패널의 <span className="kbd">SOURCE</span> 탭에서 시작하세요
      </p>
    )}
  </div>
);

/* -------- DraggableStrip --------------------------------------------
   화면 위 떠 있는 상태 바 (progress-strip 류) 를 드래그로 옮길 수 있게
   감싸는 wrapper. 사용자가 끌면 position 을 fixed 로 전환하고 viewport
   좌표를 localStorage 에 저장해서 새로고침 후에도 위치 유지.
   ------------------------------------------------------------------- */
const DRAG_HANDLE_DOT = {
  width: 3, height: 3, borderRadius: 1.5, background: "currentColor",
};
const DragHandle = () => (
  <span
    className="dh"
    aria-hidden="true"
    title="끌어서 옮기기"
    style={{
      display: "inline-grid",
      gridTemplateColumns: "auto auto",
      gap: 3,
      marginRight: 2,
      opacity: 0.5,
      userSelect: "none",
      flexShrink: 0,
    }}
  >
    <span style={DRAG_HANDLE_DOT} /><span style={DRAG_HANDLE_DOT} />
    <span style={DRAG_HANDLE_DOT} /><span style={DRAG_HANDLE_DOT} />
    <span style={DRAG_HANDLE_DOT} /><span style={DRAG_HANDLE_DOT} />
  </span>
);

const DraggableStrip = ({ children, className = "", storageKey = "banner-forge-strip-pos" }) => {
  const ref = useRef(null);
  const [pos, setPos] = useState(() => {
    try {
      const raw = localStorage.getItem(storageKey);
      if (!raw) return null;
      const p = JSON.parse(raw);
      if (typeof p?.x !== "number" || typeof p?.y !== "number") return null;
      return p;
    } catch { return null; }
  });
  const [dragging, setDragging] = useState(false);
  const dragRef = useRef({ active: false });

  // 뷰포트 리사이즈 시 strip 이 화면 밖으로 나가면 안으로 끌어들임
  useEffect(() => {
    if (!pos) return;
    const onResize = () => {
      const el = ref.current; if (!el) return;
      const r = el.getBoundingClientRect();
      const margin = 8;
      let x = pos.x, y = pos.y;
      if (x + r.width > window.innerWidth - margin) x = window.innerWidth - r.width - margin;
      if (y + r.height > window.innerHeight - margin) y = window.innerHeight - r.height - margin;
      if (x < margin) x = margin;
      if (y < margin) y = margin;
      if (x !== pos.x || y !== pos.y) setPos({ x, y });
    };
    window.addEventListener("resize", onResize);
    return () => window.removeEventListener("resize", onResize);
  }, [pos]);

  // 위치 변경 시 저장
  useEffect(() => {
    if (!pos) return;
    try { localStorage.setItem(storageKey, JSON.stringify(pos)); } catch {}
  }, [pos, storageKey]);

  const onPointerDown = useCallback((e) => {
    // 버튼/링크/입력에서 시작된 이벤트는 드래그 안 함
    if (e.target.closest("button, a, input, textarea, select, [data-no-drag]")) return;
    const el = ref.current; if (!el) return;
    const r = el.getBoundingClientRect();
    dragRef.current = {
      active: true,
      offsetX: e.clientX - r.left,
      offsetY: e.clientY - r.top,
      w: r.width,
      h: r.height,
    };
    setDragging(true);
    // 즉시 위치 동기화 (CSS 기본 위치에서 inline fixed 좌표로 점프 방지)
    setPos({ x: r.left, y: r.top });
    e.preventDefault();
    // 포인터 캡처
    try { el.setPointerCapture && el.setPointerCapture(e.pointerId); } catch {}
  }, []);

  useEffect(() => {
    if (!dragging) return;
    const onMove = (e) => {
      const d = dragRef.current; if (!d.active) return;
      const margin = 8;
      let x = e.clientX - d.offsetX;
      let y = e.clientY - d.offsetY;
      x = Math.max(margin, Math.min(window.innerWidth  - d.w - margin, x));
      y = Math.max(margin, Math.min(window.innerHeight - d.h - margin, y));
      setPos({ x, y });
    };
    const onUp = () => {
      dragRef.current.active = false;
      setDragging(false);
    };
    window.addEventListener("pointermove", onMove);
    window.addEventListener("pointerup", onUp);
    window.addEventListener("pointercancel", onUp);
    return () => {
      window.removeEventListener("pointermove", onMove);
      window.removeEventListener("pointerup", onUp);
      window.removeEventListener("pointercancel", onUp);
    };
  }, [dragging]);

  const style = pos
    ? {
        position: "fixed",
        left: pos.x, top: pos.y,
        right: "auto", bottom: "auto", transform: "none",
        cursor: dragging ? "grabbing" : "grab",
      }
    : { cursor: "grab" };

  return (
    <div
      ref={ref}
      className={"progress-strip draggable " + className}
      style={style}
      onPointerDown={onPointerDown}
    >
      <DragHandle />
      {children}
    </div>
  );
};

Object.assign(window, {
  Icon, TopBar, Dropzone, SourceCard, BannerRow,
  SpecBlock, BannerCard, EmptyState,
  DraggableStrip,
});
