/* ===========================================================
   DTF.uk · Ops — Inbox (Gmail threads) + AI reply composer
   =========================================================== */
const { useState, useEffect, useRef, useMemo } = React;

// Sanitize email HTML before rendering (strips scripts/handlers). Falls back to
// plain text if DOMPurify failed to load.
function sanitizeHtml(html){
  if (window.DOMPurify) return window.DOMPurify.sanitize(html, { USE_PROFILES: { html: true }, ADD_ATTR: ["target"] });
  const d = document.createElement("div"); d.textContent = html; return d.innerHTML;
}

/* ---- AI draft ---- */
function buildPrompt({ thread, customer, order, tone }) {
  const convo = thread.messages.map(m =>
    `${m.from === "customer" ? customer.name : "DTF.uk Support"}: ${m.body}`
  ).join("\n\n");
  const orderCtx = order
    ? `\nLinked order ${order.id}: ${order.items.map(i=>i.qty+"× "+i.name).join("; ")}. ` +
      `Total ${DTF.fmtGBP(order.total)}. Delivery: ${(DTF.methodMeta[order.method]||{}).label}. ` +
      `Status: ${(DTF.statusMeta[order.status]||{}).label}. Dispatch by ${order.fulfillBy}. Payment: ${order.pay}.`
    : "";
  const toneLine = {
    friendly: "warm, friendly and personable but still professional",
    concise:  "concise and to the point — short sentences, no fluff",
    formal:   "polished, formal and professional",
  }[tone];
  return [
    "You are a customer-support agent for DTF.uk, a UK DTF (direct-to-film) transfer printing studio based in Huddersfield.",
    "Couriers: Special Delivery 1pm (next-day guaranteed by 1pm), Royal Mail 24, Royal Mail 48, DPD, and studio Collection.",
    "Write the next reply from DTF.uk to the customer's most recent message.",
    `Tone: ${toneLine}.`,
    "Be specific and helpful, reference the order details where relevant, and never invent tracking numbers or promises you can't infer.",
    "Sign off as 'The DTF.uk team'. Return ONLY the email body — no subject line, no quoted thread.",
    orderCtx,
    "\n--- Email thread so far ---\n" + convo,
  ].join("\n");
}

function fallbackDraft(thread, customer, tone, intent) {
  const first = (customer.name || "there").split(" ")[0];
  const open = tone === "formal" ? `Dear ${first},` : `Hi ${first},`;
  const body = intent
    ? `\n\n${intent}\n`
    : `\n\nThanks for getting in touch — happy to help with this. I've checked the details on our side and will make sure everything's sorted for you.\n`;
  const me = (DTF.userName || "").trim();
  return `${open}${body}\n${me ? me + "\nDTF.UK" : "Thanks,\nThe DTF.uk team"}`;
}

/* =================== INBOX =================== */
function Inbox({ threads, setThreads, initialId, go }) {
  const [seg, setSeg] = useState("all"); // all | unread | open | closed
  const [activeId, setActiveId] = useState(initialId || (threads.find(t=>t.unread)||threads[0])?.id || null);
  const appliedInitial = useRef(null);

  // Prefer an explicitly-requested thread once it's loaded (a deep link can arrive
  // before the thread has merged in); otherwise keep/seed a valid selection.
  useEffect(() => {
    if (!threads.length) return;
    if (initialId && initialId !== appliedInitial.current && threads.some(t => t.id === initialId)) {
      appliedInitial.current = initialId;
      setActiveId(initialId);
      return;
    }
    if (!threads.some(t => t.id === activeId)) {
      setActiveId((threads.find(t => t.unread) || threads[0]).id);
    }
  }, [threads, initialId]);

  const [reloading, setReloading] = useState(false);
  // Reload just the inbox thread list from the server (re-fetch /api/inbox) without
  // a full page reload; merge so the open/searched thread and selection are kept.
  async function reloadList() {
    if (reloading) return;
    setReloading(true);
    try {
      const r = await DTF.loadInbox();
      if (r && r.configured) {
        DTF.setLiveThreads(r.threads);
        setThreads(prev => {
          const map = new Map(prev.map(t => [t.id, t]));
          (r.threads || []).forEach(t => map.set(t.id, t)); // fresh copies win, new ones added
          return [...map.values()];
        });
      }
    } catch (e) {} finally { setReloading(false); }
  }

  const filtered = useMemo(() => {
    let list = threads.slice().sort((a,b)=> a.updated < b.updated ? 1 : -1);
    if (seg === "all")    list = list.filter(t=>t.status!=="closed");        // hide handled threads
    if (seg === "unread") list = list.filter(t=>t.unread && t.status!=="closed");
    if (seg === "open")   list = list.filter(t=>t.status==="open");
    if (seg === "closed") list = list.filter(t=>t.status==="closed");
    return list;
  }, [threads, seg]);

  function assignToCustomer(threadId, customer) {
    DTF.assignThread(threadId, customer);
    setThreads(prev => prev.map(t => t.id===threadId ? {...t, customerId: customer.id} : t));
  }

  function setClosed(threadId, closed) {
    DTF.setThreadClosed(threadId, closed);
    // Closing a thread also marks it read (so it stops counting toward unread).
    setThreads(prev => prev.map(t => t.id===threadId ? {...t, status: closed?"closed":"open", unread: closed ? false : t.unread} : t));
    if (closed && threadId === activeId) {
      const next = threads.find(t => t.id !== threadId && t.status !== "closed");
      setActiveId(next ? next.id : null);
    }
  }

  const active = threads.find(t=>t.id===activeId);

  function openThread(t) {
    setActiveId(t.id);
    if (t.unread) setThreads(prev => prev.map(x => x.id===t.id ? {...x, unread:false} : x));
    if (go) go({ view:"inbox", id:t.id }); // reflect in URL so a reload restores this thread
  }

  function sendReply(threadId, body, atts) {
    setThreads(prev => prev.map(t => t.id===threadId ? {
      ...t,
      updated: "Just now",
      messages: [...t.messages, { from:"us", name:"You (DTF.uk)", addr:"hello@dtf.uk", time:"Just now", body, atts }],
    } : t));
  }

  const unreadCount = threads.filter(t=>t.unread && t.status!=="closed").length;

  return (
    <div className="inbox">
      {/* list */}
      <div className="inbox__list">
        <div className="inbox__listhead" style={{ display:"flex", alignItems:"center", gap:8 }}>
          <div className="inbox__seg">
            <button className={seg==="all"?"is-active":""} onClick={()=>setSeg("all")}>All</button>
            <button className={seg==="unread"?"is-active":""} onClick={()=>setSeg("unread")}>Unread {unreadCount>0&&`· ${unreadCount}`}</button>
            <button className={seg==="open"?"is-active":""} onClick={()=>setSeg("open")}>Open</button>
            <button className={seg==="closed"?"is-active":""} onClick={()=>setSeg("closed")}>Closed</button>
          </div>
          <button className="icon-btn" style={{ marginLeft:"auto" }} disabled={reloading} onClick={reloadList} title="Reload inbox list">
            {reloading ? <span className="spin spin--dark" /> : <Icon name="refresh" />}
          </button>
        </div>
        {filtered.map(t => {
          const c = DTF.getCustomer(t.customerId);
          const last = t.messages[t.messages.length-1] || {};
          const fromName = (c && c.name) || (t.from && (t.from.name || t.from.email)) || "Unknown sender";
          return (
            <div key={t.id} className={"thread-item"+(t.id===activeId?" is-active":"")+(t.unread?" unread":"")}
                 onClick={()=>openThread(t)}>
              <div className="ti__top">
                <span className="ti__from">{fromName}</span>
                <span className="ti__time">{t.updated}</span>
              </div>
              <div className="ti__subj">{t.subject}{t.messages.length>1 && <span className="ti__count"> ({t.messages.length})</span>}</div>
              <div className="ti__prev">{last.from==="us"?"You: ":""}{(last.body||"").replace(/\n/g," ").slice(0,64)}…</div>
              <div className="ti__labels">
                {(t.labels||[]).map(l => <span key={l} className="tag">{l}</span>)}
              </div>
            </div>
          );
        })}
      </div>

      {/* conversation */}
      {active
        ? <Conversation key={active.id} thread={active} onSend={sendReply} onClose={setClosed} onAssign={assignToCustomer} go={go} />
        : <div className="conv"><div className="empty"><Icon name="inbox" /><div className="t">No conversation selected</div></div></div>}

      {/* context */}
      {active && <ContextPanel thread={active} go={go} />}
    </div>
  );
}

/* =================== CONVERSATION + COMPOSER =================== */
function Conversation({ thread, onSend, onClose, onAssign, go }) {
  const [asgOpen, setAsgOpen] = useState(false);
  const [asgQ, setAsgQ] = useState("");
  const [asgRes, setAsgRes] = useState(null);
  useEffect(() => {
    if (!asgOpen || !asgQ.trim()) { setAsgRes(null); return; }
    let alive = true; const t = setTimeout(() => {
      DTF.searchShopifyCustomers(asgQ).then(r => alive && setAsgRes(r)).catch(() => alive && setAsgRes([]));
    }, 300);
    return () => { alive = false; clearTimeout(t); };
  }, [asgQ, asgOpen]);
  const c = DTF.getCustomer(thread.customerId);
  // Fall back to the email sender when there's no matching customer record.
  const cName = (c && c.name) || (thread.from && (thread.from.name || thread.from.email)) || "Unknown sender";
  const cEmail = (c && c.email) || (thread.from && thread.from.email) || "";
  const order = thread.orderId ? DTF.getOrder(thread.orderId) : null;
  const [draft, setDraft] = useState("");
  const [tone, setTone] = useState("friendly");
  const [busy, setBusy] = useState(false);
  const [aiNote, setAiNote] = useState(null); // {type, msg}
  const [atts, setAtts] = useState([]);
  const [showPop, setShowPop] = useState(false);
  const [sending, setSending] = useState(false);
  const [askAI, setAskAI] = useState(false);
  const [intent, setIntent] = useState("");
  // "Add to order" — AI-summarise this thread into an order's printing instructions.
  const [ordOpen, setOrdOpen] = useState(false);
  const [ordQ, setOrdQ] = useState("");
  const [ordBusy, setOrdBusy] = useState(false);
  const [ordMsg, setOrdMsg] = useState(null); // { type:"ok"|"err", order?, msg }
  const [ordReplace, setOrdReplace] = useState(false); // overwrite existing instructions vs append
  const scrollRef = useRef(null);
  const taRef = useRef(null);

  // On opening a thread (or after a reply is added), land at the BEGINNING of the
  // latest message — not the bottom of the thread. The newest email is what you want
  // to read first, and a long one (e.g. a big inline image) would otherwise have its
  // own start scrolled off-screen. Re-runs as inline images load, since images in
  // earlier messages shift the latest message's offset.
  useEffect(() => {
    const box = scrollRef.current;
    if (!box) return;
    const toLatest = () => {
      const msgs = box.querySelectorAll(".msg");
      const last = msgs[msgs.length - 1];
      if (!last) return;
      const delta = last.getBoundingClientRect().top - box.getBoundingClientRect().top;
      box.scrollTop = Math.max(0, box.scrollTop + delta - 12); // 12px breathing room above
    };
    const raf = requestAnimationFrame(toLatest);
    const imgs = Array.from(box.querySelectorAll("img")).filter(i => !i.complete);
    imgs.forEach(i => i.addEventListener("load", toLatest));
    return () => {
      cancelAnimationFrame(raf);
      imgs.forEach(i => i.removeEventListener("load", toLatest));
    };
  }, [thread.id, thread.messages.length]);

  // Summarise the whole thread into printing instructions on an order (appends to
  // that order's editable Printing Instructions field, and links the thread to it).
  async function pushToOrder() {
    const num = ordQ.replace(/\D/g, "");
    if (!num) return;
    setOrdBusy(true); setOrdMsg(null);
    try {
      const convo = "Subject: " + (thread.subject || "") + "\n\n" +
        thread.messages.map(m => `${m.from === "customer" ? cName : "DTF.uk"}: ${(m.body || "").slice(0, 1500)}`).join("\n\n");
      const r = await fetch("/api/orders/" + encodeURIComponent(num) + "/instructions/from-thread", {
        method: "POST", headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ threadId: thread.id, threadEmail: cEmail, convo, mode: ordReplace ? "replace" : "append" }),
      });
      const d = await r.json().catch(() => ({}));
      if (!r.ok) throw new Error(d.error || "Failed");
      window.dispatchEvent(new CustomEvent("dtf:instructions-updated", { detail: { order: "#" + num } }));
      setOrdMsg({ type: "ok", order: num, msg: "Added to #" + num + " printing instructions:\n\n" + (d.summary || "") });
    } catch (e) { setOrdMsg({ type: "err", msg: e.message }); }
    finally { setOrdBusy(false); }
  }

  async function draftWithAI() {
    setBusy(true);
    setAiNote({ type:"thinking", msg: intent ? "Drafting a reply from your note…" : "Reading the thread and drafting a reply…" });
    // Whole thread for context (each message trimmed to bound size), then the
    // single most recent customer message flagged as the one to reply to.
    const msgs = thread.messages;
    const convo = msgs
      .map(m => `${m.from==="customer" ? cName : "DTF.uk"}: ${(m.body || "").slice(0, 1500)}`)
      .join("\n\n");
    const lastCustomer = msgs.filter(m => m.from === "customer").slice(-1)[0] || msgs[msgs.length-1];
    const latest = lastCustomer ? (lastCustomer.body || "").slice(0, 2500) : "";
    const orderCtx = order
      ? `Linked order ${order.id}: ${order.items.map(i=>i.qty+"× "+i.name).join("; ")}. Total ${DTF.fmtGBP(order.total)}. ` +
        `Status: ${(DTF.statusMeta[order.status]||{}).label}. Delivery: ${(DTF.methodMeta[order.method]||{}).label}. Dispatch by ${order.fulfillBy}.\n\n`
      : "";
    const context = orderCtx
      + "--- Full email thread (for context) ---\n" + convo
      + "\n\n--- MOST RECENT MESSAGE FROM THE CUSTOMER (reply to this) ---\n" + cName + ": " + latest;
    let text, ok = false, errMsg = "";
    try {
      const r = await fetch("/api/ai/draft", { method:"POST", headers:{"Content-Type":"application/json"}, body: JSON.stringify({ context, intent, tone, userName: DTF.userName }) });
      const d = await r.json();
      if (!r.ok) throw new Error(d.error || "AI request failed");
      text = (d.text || "").trim(); ok = !!text;
    } catch (e) { errMsg = e.message; }
    if (!ok) text = fallbackDraft(thread, c, tone, intent);
    setDraft(text);
    setBusy(false); setAskAI(false);
    setAiNote({ type: ok ? "ok" : "fallback",
      msg: ok ? "Draft generated — review and edit before sending."
              : "AI unavailable" + (errMsg ? ` (${errMsg})` : "") + " — inserted an offline draft. Edit before sending." });
    setTimeout(()=>{ if (taRef.current) taRef.current.focus(); }, 50);
  }

  function addAtt(kind, id) {
    setAtts(prev => prev.find(a=>a.kind===kind&&a.id===id) ? prev : [...prev, { kind, id }]);
    setShowPop(false);
  }
  function removeAtt(i){ setAtts(prev => prev.filter((_,x)=>x!==i)); }

  async function send() {
    if (!draft.trim() && !atts.length) return;
    const isLive = /^thr:/.test(String(thread.id));
    const to = (c && c.email) || (thread.messages.find(m => m.from === "customer") || {}).addr || "";
    if (isLive && to) {
      setSending(true); setAiNote(null);
      try {
        const subject = /^re:/i.test(thread.subject) ? thread.subject : "Re: " + thread.subject;
        const ids = thread.messages.map(m => m.messageId).filter(Boolean);
        const inReplyTo = ids.length ? ids[ids.length - 1] : null;
        const references = ids.join(" ");
        const r = await fetch("/api/send", { method:"POST", headers:{"Content-Type":"application/json"}, body: JSON.stringify({ to, subject, text: draft.trim(), inReplyTo, references }) });
        const d = await r.json();
        if (!r.ok) throw new Error(d.error || "Send failed");
      } catch (e) {
        setSending(false);
        setAiNote({ type:"fallback", msg:"Couldn't send: " + e.message });
        return;
      }
      setSending(false);
    }
    onSend(thread.id, draft.trim() || "(See attached)", atts);
    setDraft(""); setAtts([]); setAiNote(null);
  }

  return (
    <div className="conv">
      <div className="conv__head">
        <div style={{display:"flex",alignItems:"center",gap:10}}>
          <div className="conv__subj">{thread.subject}</div>
          <div className="right" style={{display:"flex",gap:6,alignItems:"center",position:"relative"}}>
            {thread.labels.map(l=><span key={l} className="tag">{l}</span>)}
            {onAssign && <button className="btn btn--ghost btn--sm" onClick={()=>setAsgOpen(o=>!o)}><Icon name="customers" /> Assign</button>}
            <button className="btn btn--ghost btn--sm" onClick={()=>{ setOrdOpen(o=>!o); if(!ordQ && thread.orderId) setOrdQ(String(thread.orderId).replace(/^#/,"")); }} title="AI-summarise this thread into an order's printing instructions"><Icon name="doc" /> Add to order</button>
            {onClose && (thread.status === "closed"
              ? <button className="btn btn--ghost btn--sm" onClick={()=>onClose(thread.id, false)}><Icon name="refresh" /> Reopen</button>
              : <button className="btn btn--ghost btn--sm" onClick={()=>onClose(thread.id, true)}><Icon name="check" /> Mark as closed</button>)}
            {asgOpen && (
              <div className="assign-pop">
                <div className="link-box__hd"><Icon name="customers" /> Assign to customer</div>
                <input className="link-box__in" autoFocus placeholder="Search name or email…" value={asgQ} onChange={e=>setAsgQ(e.target.value)} />
                {asgRes && (asgRes.length===0
                  ? <div className="dim" style={{fontSize:12,padding:"6px 2px"}}>No matches</div>
                  : <div className="link-box__results">{asgRes.map(r=>(
                      <button key={r.id} className="link-box__row" onClick={()=>{ onAssign(thread.id, r); setAsgOpen(false); setAsgQ(""); }}>
                        <span className="link-box__name">{r.name}</span>
                        <span className="link-box__email mono">{r.email}{r.orders?` · ${r.orders} orders`:""}</span>
                      </button>))}</div>)}
              </div>
            )}
            {ordOpen && (
              <div className="assign-pop">
                <div className="link-box__hd"><Icon name="doc" /> Summarise onto an order</div>
                <div className="dim" style={{fontSize:11,padding:"2px 2px 6px"}}>AI-summarises this thread into the order's printing instructions (appended, editable).</div>
                <input className="link-box__in" autoFocus placeholder="Order number, e.g. 4560" value={ordQ}
                  onChange={e=>setOrdQ(e.target.value)} onKeyDown={e=>{ if(e.key==="Enter") pushToOrder(); }} />
                <label style={{display:"flex",gap:6,alignItems:"center",marginTop:8,fontSize:11.5,cursor:"pointer"}}>
                  <input type="checkbox" checked={ordReplace} onChange={e=>setOrdReplace(e.target.checked)} />
                  Replace existing instructions (otherwise appended)
                </label>
                <div style={{display:"flex",gap:8,marginTop:8,alignItems:"center"}}>
                  <button className="btn btn--primary btn--sm" disabled={ordBusy || !ordQ.replace(/\D/g,"")} onClick={pushToOrder}>{ordBusy?"Summarising…":"Add summary"}</button>
                  {ordMsg && ordMsg.type==="ok" && <a style={{color:"var(--mint-ink)",fontWeight:600,cursor:"pointer",fontSize:12}} onClick={()=>{ setOrdOpen(false); go({view:"order",id:ordMsg.order}); }}>Open #{ordMsg.order} →</a>}
                </div>
                {ordMsg && <div style={{fontSize:11,marginTop:6,color:ordMsg.type==="err"?"#c0392b":"var(--txt)",whiteSpace:"pre-wrap",maxHeight:180,overflow:"auto"}}>{ordMsg.msg}</div>}
              </div>
            )}
          </div>
        </div>
        <div className="conv__meta">
          <Icon name="google" fill /> <span>Gmail</span>
          <span>·</span>
          <a style={{color:"var(--mint-ink)",fontWeight:600,cursor:"pointer"}} onClick={()=>{ if(c) go({view:"customer",id:c.id}); }}>{cName}</a>
          {cEmail && <span>&lt;{cEmail}&gt;</span>}
          {order && <><span>·</span><a style={{color:"var(--mint-ink)",fontWeight:600,cursor:"pointer"}} onClick={()=>{ const ids=DTF.ordersForCustomer(thread.customerId).map(o=>o.id); DTF.setOrderNav(ids.length?ids:[order.id], { source: c ? ("Customer · "+c.name) : "From inbox", route: { view:"inbox", id: thread.id } }); go({view:"order",id:order.id}); }}>{order.id}</a></>}
        </div>
      </div>

      <div className="conv__scroll" ref={scrollRef}>
        {thread.messages.map((m,i) => (
          <div className={"msg"+(m.from==="us"?" out":"")} key={i}>
            <div className="msg__head">
              <span className={"msg__av "+(m.from==="us"?"us":"cust")}>{DTF.initials(m.name)}</span>
              <div>
                <div className="msg__who">{m.name}</div>
                <div className="msg__addr">{m.addr}</div>
              </div>
              <span className="msg__time">{m.time}</span>
            </div>
            <div className="msg__body">
              {m.html
                ? <div className="email-html" dangerouslySetInnerHTML={{ __html: sanitizeHtml(m.html) }} />
                : m.body}
            </div>
            {m.emailAtts && m.emailAtts.length > 0 && (
              <div className="msg__atts att-grid">
                {m.emailAtts.map((a,j) => <AttachmentTile key={j} att={a} />)}
              </div>
            )}
            {m.atts && m.atts.length > 0 && (
              <div className="msg__atts">
                {m.atts.map((a,j)=><AttChip key={j} att={a} />)}
              </div>
            )}
          </div>
        ))}
      </div>

      {/* composer */}
      <div className="composer">
        {aiNote && (
          <div className={"ai-banner"+(aiNote.type==="thinking"?" thinking":"")}>
            {aiNote.type==="thinking" ? <span className="spin spin--dark" /> : <Icon name="spark" fill />}
            <span>{aiNote.msg}</span>
            {aiNote.type!=="thinking" && <button className="x" onClick={()=>setAiNote(null)}><Icon name="x" /></button>}
          </div>
        )}
        <div className="composer__bar">
          <button className="ai-btn" onClick={()=>setAskAI(s=>!s)} disabled={busy}>
            <Icon name="spark" fill />{busy ? "Drafting…" : "Draft with AI"}
          </button>
          <div className="ai-tone">
            {["friendly","concise","formal"].map(t =>
              <button key={t} className={tone===t?"on":""} onClick={()=>setTone(t)}>{t[0].toUpperCase()+t.slice(1)}</button>
            )}
          </div>
          <div style={{position:"relative",marginLeft:"auto"}}>
            <button className="btn btn--ghost btn--sm" onClick={()=>setShowPop(s=>!s)}><Icon name="paperclip" /> Attach</button>
            {showPop && <AttachPopover onPick={addAtt} onClose={()=>setShowPop(false)} customerId={c.id} />}
          </div>
        </div>

        {askAI && (
          <div className="ai-ask">
            <input autoFocus value={intent} onChange={e=>setIntent(e.target.value)}
              onKeyDown={e=>{ if (e.key==="Enter" && !e.shiftKey) { e.preventDefault(); draftWithAI(); } }}
              placeholder="What would you like to say? e.g. confirm it ships Friday, no charge for the heavy white" />
            <button className="btn btn--primary btn--sm" onClick={draftWithAI} disabled={busy}>
              {busy ? <span className="spin" /> : <Icon name="spark" fill />} Generate
            </button>
            <button className="btn btn--ghost btn--sm" onClick={()=>setAskAI(false)} disabled={busy}>Cancel</button>
          </div>
        )}

        <textarea ref={taRef} value={draft} onChange={e=>setDraft(e.target.value)}
          placeholder={`Reply to ${cName.split(" ")[0]}… or hit “Draft with AI”.`} />

        {atts.length > 0 && (
          <div className="composer__atts">
            {atts.map((a,i)=><AttChip key={i} att={a} onRemove={()=>removeAtt(i)} />)}
          </div>
        )}

        <div className="composer__foot">
          <button className="btn btn--primary btn--sm" onClick={send} disabled={sending}>{sending ? <span className="spin" /> : <Icon name="send" />} {sending ? "Sending…" : "Send reply"}</button>
          <button className="btn btn--ghost btn--sm">Save draft</button>
          <span className="right kick">Sends as hello@dtf.uk</span>
        </div>
      </div>
    </div>
  );
}

function AttChip({ att, onRemove }) {
  const isProd = att.kind === "product";
  const obj = isProd ? DTF.getProduct(att.id) : DTF.getPage(att.id);
  if (!obj) return null;
  return (
    <span className="att-chip">
      <Icon name={isProd ? "shirt" : "doc"} />
      <span>
        <span className="at__t">{obj.title}</span>
        <span className="at__m"> · dtf.uk{obj.handle}{isProd ? " · "+obj.price : ""}</span>
      </span>
      {onRemove && <button onClick={onRemove} style={{display:"grid",placeItems:"center"}}><Icon name="x" /></button>}
    </span>
  );
}

/* ---- attach popover ---- */
function AttachPopover({ onPick, onClose, customerId }) {
  const [tab, setTab] = useState("products");
  const [q, setQ] = useState("");
  const ref = useRef(null);
  useEffect(() => {
    function onDoc(e){ if (ref.current && !ref.current.contains(e.target)) onClose(); }
    document.addEventListener("mousedown", onDoc);
    return () => document.removeEventListener("mousedown", onDoc);
  }, []);
  const list = tab === "products" ? DTF.products : DTF.blogPages;
  const filtered = list.filter(x => x.title.toLowerCase().includes(q.toLowerCase()));
  return (
    <div className="pop" ref={ref}>
      <div className="pop__tabs">
        <button className={tab==="products"?"on":""} onClick={()=>setTab("products")}>Shopify products</button>
        <button className={tab==="blog"?"on":""} onClick={()=>setTab("blog")}>Blog pages</button>
      </div>
      <div className="pop__search"><input autoFocus placeholder={"Search "+(tab==="products"?"products":"journal")+"…"} value={q} onChange={e=>setQ(e.target.value)} /></div>
      <div className="pop__list">
        {filtered.map(x => (
          <div className="pop__item" key={x.id} onClick={()=>onPick(tab==="products"?"product":"page", x.id)}>
            <div className="pop__thumb"><Icon name={tab==="products"?"shirt":"doc"} /></div>
            <div style={{minWidth:0}}>
              <div className="pop__t">{x.title}</div>
              <div className="pop__m">dtf.uk{x.handle}</div>
            </div>
            {tab==="products" && <span className="pop__price">{x.price}</span>}
          </div>
        ))}
        {!filtered.length && <div style={{padding:16}} className="dim">No matches.</div>}
      </div>
    </div>
  );
}

/* ---- context panel ---- */
function ContextPanel({ thread, go }) {
  const c = DTF.getCustomer(thread.customerId);
  const order = thread.orderId ? DTF.getOrder(thread.orderId) : null;
  const recentOrders = DTF.ordersForCustomer(thread.customerId)
    .filter(o => !order || o.id !== order.id)
    .sort((a,b) => a.date < b.date ? 1 : -1);
  const custEmail = (c && c.email) || (thread.from && thread.from.email) || "";
  // Open an order with prev/next context across this customer's orders shown here.
  const ctxOrderIds = [order, ...recentOrders].filter(Boolean).map(o => o.id);
  const openOrder = (oid) => { DTF.setOrderNav(ctxOrderIds, { source: c ? ("Customer · " + c.name) : "From inbox", route: { view:"inbox", id: thread.id } }); go({ view: "order", id: oid }); };
  return (
    <div className="ctx">
      {c ? (
      <div className="ctx__sec">
        <h4>Customer</h4>
        <div style={{display:"flex",gap:11,alignItems:"center",marginBottom:12,cursor:"pointer"}} onClick={()=>go({view:"customer",id:c.id})}>
          <Avatar name={c.name} lg />
          <div>
            <div style={{fontWeight:700,fontSize:14}}>{c.name}</div>
            <div className="mono dim" style={{fontSize:10.5}}>{c.location}</div>
          </div>
        </div>
        <div className="statline">
          <div><div className="k">Orders</div><div className="v">{c.orders}</div></div>
          <div><div className="k">Lifetime</div><div className="v">{DTF.fmtGBP(c.spent)}</div></div>
        </div>
        <div style={{display:"flex",gap:6,flexWrap:"wrap",marginTop:12}}>
          {c.subscribed && <span className="badge badge--mint">Subscribed</span>}
          {(c.tags||[]).map(t=><span key={t} className="tag">{t}</span>)}
        </div>
        <button className="btn btn--ghost btn--sm btn--block" style={{marginTop:12}} onClick={()=>go({view:"customer",id:c.id})}>Open profile →</button>
      </div>
      ) : (
      <div className="ctx__sec">
        <h4>Customer</h4>
        <div className="dim" style={{fontSize:12.5}}>No customer record for this sender.</div>
      </div>
      )}

      {order && (
        <div className="ctx__sec">
          <h4>Linked order</h4>
          <div style={{cursor:"pointer"}} onClick={()=>openOrder(order.id)}>
            <div style={{display:"flex",alignItems:"center",gap:8,marginBottom:8}}>
              <span className="ordno">{order.id}</span><StatusBadge status={order.status} />
            </div>
            <div style={{display:"flex",justifyContent:"space-between",fontSize:12.5,marginBottom:4}}>
              <span className="dim">{order.items.reduce((s,i)=>s+i.qty,0)} units</span>
              <span style={{fontWeight:600}}>{DTF.fmtGBP(order.total)}</span>
            </div>
            <div style={{display:"flex",alignItems:"center",gap:6,fontSize:12}}>
              <span className={"urg-rail urg--"+(DTF.methodMeta[order.method]||{}).urg} style={{height:14}} />
              <span className="mono dim">{(DTF.methodMeta[order.method]||{}).label} · by {order.fulfillBy ? order.fulfillBy.slice(5).replace("-","/") : "—"}</span>
            </div>
          </div>
        </div>
      )}

      {recentOrders.length > 0 && (
        <div className="ctx__sec">
          <h4>{order ? "Other recent orders" : "Recent orders"}</h4>
          {recentOrders.slice(0,6).map(o => (
            <div key={o.id} style={{display:"flex",alignItems:"center",gap:8,padding:"6px 0",cursor:"pointer"}} onClick={()=>openOrder(o.id)}>
              <span className="ordno" style={{fontSize:11.5}}>{o.id}</span>
              <StatusBadge status={o.status} />
              <span className="mono" style={{marginLeft:"auto",fontSize:11,fontWeight:600}}>{DTF.fmtGBP(o.total)}</span>
            </div>
          ))}
        </div>
      )}

      {(() => {
        const atts = DTF.collectAttachments(DTF.threadsForCustomer(thread.customerId));
        if (!atts.length) return null;
        return (
          <div className="ctx__sec">
            <h4>Attachments ({atts.length})</h4>
            <div className="att-grid">{atts.slice(0,12).map((a,i) => <AttachmentTile key={i} att={a} meta={a.time} />)}</div>
          </div>
        );
      })()}

      {custEmail && window.BrowsingSection && <BrowsingSection email={custEmail} compact />}
    </div>
  );
}

Object.assign(window, { Inbox });
