// ui.jsx — shared primitives + i18n. Exports to window.
const { createContext, useContext, useState, useEffect, useRef, useMemo } = React;

/* ---- i18n -------------------------------------------------------------- */
const STRINGS = {
  en: {
    appName: "AI Sales", brand: "Maxab Hub",
    nav_dashboard: "Dashboard", nav_contacts: "Contacts", nav_campaigns: "Campaigns",
    nav_conversations: "Conversations", nav_agents: "AI Agents", nav_settings: "Settings",
    nav_queue: "Messaging", nav_escalations: "Escalations", nav_requests: "Custom Requests",
    search: "Search contacts, campaigns…", newCampaign: "New campaign", import: "Import contacts",
    sub_dashboard: "What needs your attention today",
    sub_contacts: "Every lead, in one place", sub_campaigns: "Outreach in motion",
    sub_conversations: "Replies and approvals", sub_agents: "Your AI sales team",
    sub_settings: "Connections and team",
    sub_queue: "Outbound WhatsApp delivery", sub_escalations: "Leads that need a human", sub_requests: "Requests beyond the standard offers",
    greeting: "Good morning", cont: "Continue", reviewAll: "Review all",
    newReplies: "New replies", waitingApprovals: "Waiting approvals", activeCampaigns: "Active campaigns",
    interestedLeads: "Interested leads", retarget: "Retarget opportunities",
    nextBest: "Your next best actions", open: "Open", approve: "Approve", send: "Send",
    status_new: "New", status_contacted: "Contacted", status_replied: "Replied",
    status_interested: "Interested", status_proposal: "Proposal sent", status_followup: "Follow up",
    status_won: "Won", status_lost: "Lost",
    allContacts: "All", table_contact: "Contact", table_status: "Status", table_campaign: "Campaign",
    table_agent: "Agent", table_priority: "Priority", table_value: "Value", table_next: "Next action",
    table_lastReply: "Last reply", board: "Board", list: "List",
    connected: "Connected", disconnected: "Disconnected", needsQR: "Needs QR", sending: "Sending",
    error: "Error", liveMode: "Live", mockMode: "Test mode",
  },
  ar: {
    appName: "المبيعات الذكية", brand: "ماكساب هَب",
    nav_dashboard: "الرئيسية", nav_contacts: "جهات الاتصال", nav_campaigns: "الحملات",
    nav_conversations: "المحادثات", nav_agents: "الموظفون الأذكياء", nav_settings: "الإعدادات",
    nav_queue: "الإرسال", nav_escalations: "التصعيدات", nav_requests: "طلبات خاصة",
    search: "ابحث عن جهة اتصال أو حملة…", newCampaign: "حملة جديدة", import: "استيراد جهات الاتصال",
    sub_dashboard: "ما يحتاج انتباهك اليوم",
    sub_contacts: "كل العملاء في مكان واحد", sub_campaigns: "حملات قيد التشغيل",
    sub_conversations: "الردود والموافقات", sub_agents: "فريق المبيعات الذكي",
    sub_settings: "الاتصالات والفريق",
    sub_queue: "إرسال رسائل واتساب", sub_escalations: "عملاء يحتاجون تدخل بشري", sub_requests: "طلبات خارج الخدمات الأساسية",
    greeting: "صباح الخير", cont: "متابعة", reviewAll: "مراجعة الكل",
    newReplies: "ردود جديدة", waitingApprovals: "بانتظار الموافقة", activeCampaigns: "حملات نشطة",
    interestedLeads: "عملاء مهتمون", retarget: "فرص إعادة الاستهداف",
    nextBest: "أفضل خطواتك التالية", open: "فتح", approve: "موافقة", send: "إرسال",
    status_new: "جديد", status_contacted: "تم التواصل", status_replied: "رد",
    status_interested: "مهتم", status_proposal: "تم إرسال العرض", status_followup: "متابعة",
    status_won: "تم الإغلاق", status_lost: "غير مهتم",
    allContacts: "الكل", table_contact: "جهة الاتصال", table_status: "الحالة", table_campaign: "الحملة",
    table_agent: "الموظف", table_priority: "الأولوية", table_value: "القيمة", table_next: "الخطوة التالية",
    table_lastReply: "آخر رد", board: "لوحة", list: "قائمة",
    connected: "متصل", disconnected: "غير متصل", needsQR: "يحتاج رمز QR", sending: "جارٍ الإرسال",
    error: "خطأ", liveMode: "مباشر", mockMode: "وضع التجربة",
  },
};
const LangContext = createContext(null);
function useLang() { return useContext(LangContext); }

/* ---- avatar color from string ----------------------------------------- */
const AV_COLORS = ["#00654B", "#2563EB", "#7E22CE", "#EA580C", "#0891B2", "#BE185D", "#4A6400", "#B45309"];
function avatarColor(s = "") {
  let h = 0; for (let i = 0; i < s.length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0;
  return AV_COLORS[h % AV_COLORS.length];
}
function initials(name = "") {
  const p = name.trim().split(/\s+/);
  return ((p[0]?.[0] || "") + (p[1]?.[0] || "")).toUpperCase() || "?";
}
function Avatar({ name, size = "", color, square, src }) {
  const cls = "avatar" + (square ? " avatar--sq" : "") + (size ? " avatar--" + size : "");
  if (src) return <img src={src} className={cls} alt={name} style={{ objectFit: "cover" }} />;
  return <span className={cls} style={{ background: color || avatarColor(name) }}>{initials(name)}</span>;
}

/* ---- Button ------------------------------------------------------------ */
function Button({ variant = "primary", size, icon, iconEnd, block, children, ...rest }) {
  const cls = ["btn", "btn--" + variant, size ? "btn--" + size : "", block ? "btn--block" : "", !children ? "btn--icon" : ""].filter(Boolean).join(" ");
  return (
    <button className={cls} {...rest}>
      {icon && <Icon name={icon} size={size === "sm" ? 16 : 18} />}
      {children ? <span className="btn__label">{children}</span> : null}
      {iconEnd && <Icon name={iconEnd} size={size === "sm" ? 16 : 18} />}
    </button>
  );
}

/* ---- Badge ------------------------------------------------------------- */
function Badge({ tone = "neutral", dot, children }) {
  return <span className={"badge badge--" + tone}>{dot && <span className="dot" style={{ background: dot }} />}{children}</span>;
}

/* AI fallback-mode banner — honest, not scary. Shows whenever the live AI provider
   is unavailable (no key, invalid/expired key, rate-limited, unreachable) so the
   operator KNOWS replies are coming from safe built-in templates, not Claude.
   Driven by bootstrap meta (aiMode/live), which now reflects a REAL provider probe. */
const AI_FALLBACK_REASON = {
  no_key: "No AI provider key is configured",
  invalid_key: "The AI provider key was rejected (invalid or expired)",
  rate_limited_or_quota: "The AI provider is rate-limited or out of quota",
  model_unavailable: "The selected model isn't available for this key",
  network: "The AI provider couldn't be reached",
  unverified: "The AI provider key hasn't passed a health check yet",
  provider_error: "The AI provider returned an error",
};
function FallbackBanner({ compact }) {
  let meta = {};
  try { meta = (useStore() || {}).meta || {}; } catch { meta = {}; }
  let go = null;
  try { go = (useApp() || {}).go; } catch {}
  const fallback = meta.aiMode ? meta.aiMode === "fallback" : meta.live === false;
  if (!fallback) return null;
  const reason = AI_FALLBACK_REASON[meta.aiLastError] || "Live AI replies are currently unavailable";
  return (
    <div className="row" role="status" style={{ gap: 10, alignItems: "flex-start", padding: compact ? "8px 12px" : "12px 16px", marginBottom: 16, background: "var(--color-orange-soft)", border: "1px solid var(--color-orange)", borderRadius: "var(--radius-md)", fontSize: 13, color: "var(--fg-1)" }}>
      <Icon name="alert" size={16} style={{ color: "var(--color-orange)", flexShrink: 0, marginTop: 1 }} />
      <div style={{ flex: 1, minWidth: 0 }}>
        <b>AI provider unavailable — running in fallback mode.</b>{" "}
        <span style={{ color: "var(--fg-2)" }}>{reason}. Drafts are safe but lower quality; live Claude replies are disabled. Configure and test your provider key in Settings → AI Providers.</span>
      </div>
      {go && <button className="btn btn--subtle btn--sm" style={{ flexShrink: 0 }} onClick={() => go("settings")}>Open Settings</button>}
    </div>
  );
}
window.FallbackBanner = FallbackBanner;

/* status → tone + label */
const STATUS_META = {
  new:        { tone: "neutral", key: "status_new", color: "#9CA3AF" },
  contacted:  { tone: "blue",    key: "status_contacted", color: "#2563EB" },
  replied:    { tone: "purple",  key: "status_replied", color: "#7E22CE" },
  interested: { tone: "lime",    key: "status_interested", color: "#4A6400" },
  proposal:   { tone: "orange",  key: "status_proposal", color: "#EA580C" },
  followup:   { tone: "orange",  key: "status_followup", color: "#EA580C" },
  won:        { tone: "green",   key: "status_won", color: "#22C55E" },
  lost:       { tone: "red",     key: "status_lost", color: "#B91C1C" },
};
function StatusPill({ status }) {
  const { t } = useLang();
  const m = STATUS_META[status] || STATUS_META.new;
  return <Badge tone={m.tone} dot={m.color}>{t(m.key)}</Badge>;
}

/* ---- Priority ---------------------------------------------------------- */
const PRIORITY_META = {
  urgent: { label: "Urgent", tone: "red", c: "#B91C1C" },
  high:   { label: "High", tone: "orange", c: "#EA580C" },
  medium: { label: "Medium", tone: "blue", c: "#2563EB" },
  low:    { label: "Low", tone: "neutral", c: "#9CA3AF" },
  cold:   { label: "Cold", tone: "neutral", c: "#9CA3AF" },
};
// Safe accessor — never let a contact with an unmapped/missing priority crash a row or
// the Lead Drawer (defense-in-depth, same pattern as the campaigns serviceMeta fix).
const priorityMeta = (p) => PRIORITY_META[p] || PRIORITY_META.low;
window.priorityMeta = priorityMeta;

/* ---- Score bar --------------------------------------------------------- */
function ScoreBar({ value, color }) {
  const c = color || (value >= 70 ? "var(--color-success)" : value >= 40 ? "var(--color-blue)" : "var(--color-text-disabled)");
  return (
    <span className="score">
      <span className="score__bar"><i style={{ width: value + "%", background: c }} /></span>
      <span style={{ color: "var(--fg-2)" }}>{value}</span>
    </span>
  );
}
function OppBadge({ band }) { return <span className={"opp opp--" + band}>{band}</span>; }

/* ---- Card -------------------------------------------------------------- */
function Card({ title, action, flush, hover, children, style, className = "", ...rest }) {
  return (
    <div className={"card" + (flush ? " card--flush" : "") + (hover ? " card--hover" : "") + (className ? " " + className : "")} style={style} {...rest}>
      {title && <div className="card__head"><div className="card__title">{title}</div>{action}</div>}
      {children}
    </div>
  );
}

/* ---- Field ------------------------------------------------------------- */
function Field({ label, hint, children }) {
  return (
    <label className="field">
      {label && <span className="field__label">{label}</span>}
      {children}
      {hint && <span className="field__hint">{hint}</span>}
    </label>
  );
}

/* ---- Seg toggle -------------------------------------------------------- */
function Seg({ value, onChange, options }) {
  return (
    <div className="seg">
      {options.map((o) => (
        <button key={o.value} className={value === o.value ? "is-on" : ""} onClick={() => onChange(o.value)}>
          {o.icon && <Icon name={o.icon} size={14} style={{ marginInlineEnd: o.label ? 6 : 0, verticalAlign: "-2px" }} />}
          {o.label}
        </button>
      ))}
    </div>
  );
}

/* ---- Modal / Drawer ---------------------------------------------------- */
function Scrim({ onClose, children }) {
  return (<>
    <div className="scrim" onClick={onClose} />
    {children}
  </>);
}
function Drawer({ onClose, children }) {
  useEffect(() => {
    const h = (e) => e.key === "Escape" && onClose();
    window.addEventListener("keydown", h); return () => window.removeEventListener("keydown", h);
  }, [onClose]);
  return <Scrim onClose={onClose}><div className="drawer" role="dialog">{children}</div></Scrim>;
}
function Modal({ onClose, children, style }) {
  return <Scrim onClose={onClose}><div className="modal" role="dialog" style={style} onClick={(e) => e.stopPropagation()}>{children}</div></Scrim>;
}

/* ---- Empty state ------------------------------------------------------- */
function EmptyState({ icon = "inbox", title, body, action }) {
  return (
    <div style={{ textAlign: "center", padding: "64px 24px", color: "var(--fg-3)" }}>
      <div style={{ width: 72, height: 72, borderRadius: 20, background: "var(--bg-2)", display: "inline-flex", alignItems: "center", justifyContent: "center", color: "var(--fg-disabled)", marginBottom: 20 }}>
        <Icon name={icon} size={32} />
      </div>
      <div style={{ fontSize: "var(--fs-h13)", fontWeight: 700, color: "var(--fg-1)", marginBottom: 8 }}>{title}</div>
      {body && <div style={{ maxWidth: 360, margin: "0 auto 20px", fontSize: "var(--fs-body-sm)" }}>{body}</div>}
      {action}
    </div>
  );
}

/* ---- WhatsApp status chip --------------------------------------------- */
const WA_STATES = {
  connected:    { tone: "green",  c: "#22C55E", icon: "wifi",    key: "connected" },
  disconnected: { tone: "neutral",c: "#9CA3AF", icon: "wifiOff", key: "disconnected" },
  needsQR:      { tone: "orange", c: "#EA580C", icon: "qr",      key: "needsQR" },
  sending:      { tone: "blue",   c: "#2563EB", icon: "send",    key: "sending" },
  error:        { tone: "red",    c: "#B91C1C", icon: "alert",   key: "error" },
};
function WaChip({ state }) {
  const { t } = useLang();
  const m = WA_STATES[state] || WA_STATES.disconnected;
  return <Badge tone={m.tone} dot={m.c}>{t(m.key)}</Badge>;
}

Object.assign(window, {
  STRINGS, LangContext, useLang, avatarColor, initials, Avatar, Button, Badge,
  STATUS_META, StatusPill, PRIORITY_META, ScoreBar, OppBadge, Card, Field, Seg,
  Scrim, Drawer, Modal, EmptyState, WA_STATES, WaChip,
});
