/* Custex editor — studio AI chat panel. */
const { useState: uS, useRef: uR, useEffect: uE, useMemo: uM } = React;
const { NODE_W, PORT_FIRST, PORT_STRIDE } = window.CxGeom;

const cxAiChatFileToJpegDataUrl = window.cxAiChatFileToJpegDataUrl;

const AI_CHAT_STORAGE_KEY = 'custex.aiChat.panel';
const AI_CHAT_W_MIN = 280;
const AI_CHAT_W_MAX = 640;
const AI_CHAT_H_MIN = 260;
const AI_CHAT_H_MAX = 720;

function readAiChatPanel() {
  try {
    const raw = localStorage.getItem(AI_CHAT_STORAGE_KEY);
    if (!raw) return null;
    const o = JSON.parse(raw);
    return o && typeof o === 'object' ? o : null;
  } catch {
    return null;
  }
}

/** Floating AI assistant: open from canvas dock; draggable, resizable, pin-to-top, hide. */
function CxStudioAiChat() {
  const saved = typeof window !== 'undefined' ? readAiChatPanel() : null;
  const [open, setOpen] = uS(false);
  const [pinned, setPinned] = uS(!!saved?.pinned);
  const [rect, setRect] = uS(() => ({
    w: Math.min(AI_CHAT_W_MAX, Math.max(AI_CHAT_W_MIN, Number(saved?.w) || 340)),
    h: Math.min(AI_CHAT_H_MAX, Math.max(AI_CHAT_H_MIN, Number(saved?.h) || 420)),
    left: Number.isFinite(saved?.left) ? saved.left : null,
    top: Number.isFinite(saved?.top) ? saved.top : null,
  }));
  const [messages, setMessages] = uS([]);
  const [input, setInput] = uS('');
  const [attachQueue, setAttachQueue] = uS([]);
  const [busy, setBusy] = uS(false);
  const listRef = uR(null);
  const dragRef = uR(null);
  const resizeRef = uR(null);
  const fileRef = uR(null);

  uE(() => {
    if (!open) return;
    setRect((r) => {
      if (r.left != null && r.top != null) return r;
      const w = r.w;
      const h = r.h;
      const left = Math.max(12, window.innerWidth - w - 20);
      const top = Math.max(12, window.innerHeight - h - 96);
      return { ...r, left, top };
    });
  }, [open]);

  uE(() => {
    if (!open || rect.left == null || rect.top == null) return;
    try {
      localStorage.setItem(
        AI_CHAT_STORAGE_KEY,
        JSON.stringify({ left: rect.left, top: rect.top, w: rect.w, h: rect.h, pinned })
      );
    } catch {
      /* ignore */
    }
  }, [rect, pinned]);

  uE(() => {
    const el = listRef.current;
    if (el) el.scrollTop = el.scrollHeight;
  }, [messages, open]);

  const clampToViewport = (r) => {
    const margin = 8;
    const maxL = Math.max(margin, window.innerWidth - r.w - margin);
    const maxT = Math.max(margin, window.innerHeight - r.h - margin);
    return {
      ...r,
      left: Math.min(maxL, Math.max(margin, r.left)),
      top: Math.min(maxT, Math.max(margin, r.top)),
      w: Math.min(AI_CHAT_W_MAX, Math.max(AI_CHAT_W_MIN, r.w)),
      h: Math.min(AI_CHAT_H_MAX, Math.max(AI_CHAT_H_MIN, r.h)),
    };
  };

  const onChatBarMouseDown = (e) => {
    if (e.button !== 0) return;
    if (e.target.closest('button')) return;
    e.preventDefault();
    const start = { x: e.clientX, y: e.clientY, l: rect.left, t: rect.top };
    dragRef.current = start;
    const move = (ev) => {
      const d = dragRef.current;
      if (!d) return;
      setRect((r) =>
        clampToViewport({
          ...r,
          left: d.l + (ev.clientX - d.x),
          top: d.t + (ev.clientY - d.y),
        })
      );
    };
    const up = () => {
      dragRef.current = null;
      window.removeEventListener('mousemove', move);
      window.removeEventListener('mouseup', up);
    };
    window.addEventListener('mousemove', move);
    window.addEventListener('mouseup', up);
  };

  const onResizeMouseDown = (e) => {
    if (e.button !== 0) return;
    e.preventDefault();
    e.stopPropagation();
    const start = { x: e.clientX, y: e.clientY, w: rect.w, h: rect.h };
    resizeRef.current = start;
    const move = (ev) => {
      const d = resizeRef.current;
      if (!d) return;
      setRect((r) =>
        clampToViewport({
          ...r,
          w: d.w + (ev.clientX - d.x),
          h: d.h + (ev.clientY - d.y),
        })
      );
    };
    const up = () => {
      resizeRef.current = null;
      window.removeEventListener('mousemove', move);
      window.removeEventListener('mouseup', up);
    };
    window.addEventListener('mousemove', move);
    window.addEventListener('mouseup', up);
  };

  const bumpSize = (factor) => {
    setRect((r) =>
      clampToViewport({
        ...r,
        w: Math.round(r.w * factor),
        h: Math.round(r.h * factor),
      })
    );
  };

  const onAttachFiles = async (e) => {
    const files = Array.from(e.target.files || []);
    e.target.value = '';
    if (!files.length) return;
    const urls = [];
    for (const f of files) {
      if (urls.length >= AI_CHAT_MAX_ATTACH) break;
      try {
        urls.push(await cxAiChatFileToJpegDataUrl(f));
      } catch {
        /* skip unreadable */
      }
    }
    if (!urls.length) return;
    setAttachQueue((q) => [...q, ...urls].slice(0, AI_CHAT_MAX_ATTACH));
  };

  const sendChat = async () => {
    const text = input.trim();
    const imgs = attachQueue.slice(0, AI_CHAT_MAX_ATTACH);
    if ((!text && !imgs.length) || busy) return;
    setInput('');
    setAttachQueue([]);
    const userMsg =
      imgs.length > 0 ? { role: 'user', content: text, images: imgs } : { role: 'user', content: text };
    const next = [...messages, userMsg];
    setMessages(next);
    setBusy(true);
    try {
      const res = await fetch('/api/ai/chat', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ messages: next }),
      });
      const data = await res.json().catch(() => ({}));
      if (!res.ok) throw new Error(data.error || `HTTP ${res.status}`);
      const reply = typeof data.reply === 'string' ? data.reply : '';
      setMessages((m) => [...m, { role: 'assistant', content: reply || '(No reply)' }]);
    } catch (ex) {
      const msg = ex?.message || 'Request failed';
      setMessages((m) => [
        ...m,
        {
          role: 'assistant',
          content: `Request failed: ${msg}\nIf you are on a static preview or API is not deployed, set OPENROUTER_API_KEY on Vercel and redeploy.`,
        },
      ]);
    } finally {
      setBusy(false);
    }
  };

  const panelReady = open && rect.left != null && rect.top != null;
  const panelStyle = panelReady
    ? {
        position: 'fixed',
        left: rect.left,
        top: rect.top,
        width: rect.w,
        height: rect.h,
        zIndex: pinned ? 10060 : 10050,
      }
    : null;

  return (
    <>
      <button
        type="button"
        className={`cx-ai-chat-fab${open ? ' cx-ai-chat-fab-on' : ''}`}
        title={open ? 'Close assistant' : 'Open AI assistant'}
        aria-expanded={open}
        aria-label="AI assistant"
        onClick={() => setOpen((o) => !o)}
      >
        <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.35" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
          <path d="M12 3c-4.4 0-8 3-8 7 0 2.2 1.2 4.2 3 5.4V21l4.2-2.4c.6.1 1.2.2 1.8.2 4.4 0 8-3 8-7s-3.6-7-8-7z" />
          <path d="M8.5 10.5h.01M12 10.5h.01M15.5 10.5h.01" strokeWidth="2" />
        </svg>
      </button>
      {panelReady && panelStyle ? (
        <div
          className={`cx-ai-chat-panel ${pinned ? 'cx-ai-chat-panel-pinned' : ''}`}
          style={panelStyle}
          role="dialog"
          aria-label="AI assistant"
        >
          <div className="cx-ai-chat-body">
            <div className="cx-ai-chat-list" ref={listRef}>
              {messages.length === 0 ? (
                <p className="cx-ai-chat-empty">
                  Ask about fabrics, crafts, sizing, or the canvas. You can attach reference images (text suggestions only; the graph is not edited automatically). Answers require a deployed API.
                </p>
              ) : (
                messages.map((m, i) => (
                  <div key={i} className={`cx-ai-chat-msg cx-ai-chat-msg-${m.role}`}>
                    <span className="cx-ai-chat-msg-role">{m.role === 'user' ? 'You' : 'Assistant'}</span>
                    {m.role === 'user' && Array.isArray(m.images) && m.images.length > 0 ? (
                      <div className="cx-ai-chat-msg-images">
                        {m.images.map((src, j) => (
                          <img key={j} className="cx-ai-chat-msg-thumb" src={src} alt="" />
                        ))}
                      </div>
                    ) : null}
                    {m.content ? <div className="cx-ai-chat-msg-text">{m.content}</div> : null}
                  </div>
                ))
              )}
              {busy ? <div className="cx-ai-chat-typing">…</div> : null}
            </div>
            <div className="cx-ai-chat-compose">
              <input
                ref={fileRef}
                type="file"
                className="cx-ai-chat-file-input"
                accept="image/jpeg,image/png,image/webp,image/gif"
                multiple
                aria-hidden="true"
                tabIndex={-1}
                onChange={onAttachFiles}
              />
              {attachQueue.length > 0 ? (
                <div className="cx-ai-chat-attach-strip" aria-label="Images to send">
                  {attachQueue.map((src, idx) => (
                    <div key={idx} className="cx-ai-chat-attach-chip">
                      <img src={src} alt="" />
                      <button
                        type="button"
                        className="cx-ai-chat-attach-remove"
                        title="Remove"
                        disabled={busy}
                        onClick={() => setAttachQueue((q) => q.filter((_, k) => k !== idx))}
                      >
                        ×
                      </button>
                    </div>
                  ))}
                </div>
              ) : null}
              <div className="cx-ai-chat-compose-row">
                <textarea
                  className="cx-ai-chat-input"
                  rows={2}
                  value={input}
                  disabled={busy}
                  placeholder="Type a message or send images only — Enter to send · Shift+Enter for newline"
                  onChange={(e) => setInput(e.target.value)}
                  onKeyDown={(e) => {
                    if (e.key === 'Enter' && !e.shiftKey) {
                      e.preventDefault();
                      sendChat();
                    }
                  }}
                />
                <button
                  type="button"
                  className="cx-ai-chat-attach-btn"
                  title="Add reference images (up to 4)"
                  disabled={busy || attachQueue.length >= AI_CHAT_MAX_ATTACH}
                  onClick={() => fileRef.current?.click()}
                >
                  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" aria-hidden="true">
                    <rect x="3" y="5" width="18" height="14" rx="2" />
                    <circle cx="8.5" cy="10" r="1.5" fill="currentColor" stroke="none" />
                    <path d="M21 15l-5-5-4 4-2-2-5 5" strokeLinecap="round" strokeLinejoin="round" />
                  </svg>
                </button>
                <button
                  type="button"
                  className="cx-ai-chat-send"
                  disabled={busy || (!input.trim() && !attachQueue.length)}
                  onClick={sendChat}
                >
                  Send
                </button>
              </div>
            </div>
          </div>
          <div className="cx-ai-chat-bar cx-ai-chat-bar-bottom" onMouseDown={onChatBarMouseDown}>
            <span className="cx-ai-chat-bar-title">Custex assistant</span>
            <span className="cx-ai-chat-bar-drag-hint" aria-hidden="true">
              Drag
            </span>
            <div className="cx-ai-chat-bar-actions">
              <button
                type="button"
                className={pinned ? 'cx-ai-chat-ico on' : 'cx-ai-chat-ico'}
                title={pinned ? 'Unpin' : 'Pin on top'}
                aria-pressed={pinned}
                onClick={(e) => {
                  e.stopPropagation();
                  setPinned((p) => !p);
                }}
              >
                <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.35" aria-hidden="true">
                  <path d="M8 1v4M8 1l2 2M8 1 6 3M3 7h10v1H3zM5 8v6h6V8" strokeLinecap="round" strokeLinejoin="round" />
                </svg>
              </button>
              <button
                type="button"
                className="cx-ai-chat-ico"
                title="Smaller window"
                onClick={(e) => {
                  e.stopPropagation();
                  bumpSize(0.88);
                }}
              >
                <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.35" aria-hidden="true">
                  <path d="M4 8h8M3 3h10v10H3z" strokeLinecap="round" />
                </svg>
              </button>
              <button
                type="button"
                className="cx-ai-chat-ico"
                title="Larger window"
                onClick={(e) => {
                  e.stopPropagation();
                  bumpSize(1.12);
                }}
              >
                <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.35" aria-hidden="true">
                  <path d="M4 8h8M8 4v8M3 3h10v10H3z" strokeLinecap="round" />
                </svg>
              </button>
              <button
                type="button"
                className="cx-ai-chat-ico"
                title="Hide — open again from the corner icon"
                onClick={(e) => {
                  e.stopPropagation();
                  setOpen(false);
                }}
              >
                <svg viewBox="0 0 16 16" fill="none" stroke="currentColor" strokeWidth="1.35" strokeLinecap="round" aria-hidden="true">
                  <path d="M5 5l6 6M11 5 5 11" />
                </svg>
              </button>
            </div>
          </div>
          <button
            type="button"
            className="cx-ai-chat-resize"
            aria-label="Resize panel"
            title="Drag to resize"
            onMouseDown={onResizeMouseDown}
          />
        </div>
      ) : null}
    </>
  );
}
window.CxStudioAiChatPanel = { CxStudioAiChat };
