// Source / Target group screens: dedicated lists with add/edit/delete.

const { useState } = React;

// ─── Shared: Zalo conversation ID field with verify button ───────────────
// onVerified(customerName) called with name string on success, null on reset/fail
const ZaloConversationField = ({ botId, value, onChange, onVerified }) => {
  const [checking, setChecking] = useState(false);
  const [status, setStatus] = useState(null); // null | 'ok' | 'error'
  const [statusMsg, setStatusMsg] = useState("");

  const handleCheck = async () => {
    if (!value || !botId) return;
    setChecking(true);
    setStatus(null);
    try {
      const res = await fetch(`/api/zalo/verify-conversation?bot_id=${encodeURIComponent(botId)}&conversation_id=${encodeURIComponent(value)}`, {
        headers: { Authorization: `Bearer ${AuthStore.getToken()}` },
      });
      const data = await res.json();
      if (data.success) {
        setStatus("ok");
        setStatusMsg(data.customerName ? data.customerName : "Conversation found");
        onVerified(data.customerName || "");
      } else {
        setStatus("error");
        setStatusMsg(data.message || "Conversation not found");
        onVerified(null);
      }
    } catch (e) {
      setStatus("error");
      setStatusMsg("Request failed: " + e.message);
      onVerified(null);
    } finally {
      setChecking(false);
    }
  };

  return (
    <div className="field">
      <label className="field-label">Conversation ID</label>
      <div style={{ display: "flex", gap: 8 }}>
        <input
          className="input mono"
          style={{ flex: 1 }}
          value={value}
          onChange={(e) => { onChange(e.target.value); setStatus(null); onVerified(null); }}
          placeholder="Zalo conversation ID"
          disabled={!botId}
        />
        <button
          className="btn"
          onClick={handleCheck}
          disabled={checking || !value || !botId}
          style={{ whiteSpace: "nowrap" }}
        >
          {checking ? "Checking…" : "Check"}
        </button>
      </div>
      {status === "ok" && (
        <div style={{ fontSize: 12, marginTop: 4, color: "var(--green-fg, #4ade80)" }}>✓ {statusMsg}</div>
      )}
      {status === "error" && (
        <div style={{ fontSize: 12, marginTop: 4, color: "var(--red-fg)" }}>✗ {statusMsg}</div>
      )}
    </div>
  );
};

// ─── Source group form modal ─────────────────────────────────────────────
const SourceGroupModal = ({ open, onClose, onSaved, initial = {}, zaloBots = [], stats }) => {
  const toast = useToast();
  const [form, setForm] = useState({
    name: "", platform_group_id: "", platform: "telegram",
    description: "", language: "vi", topics_enabled: false,
    topic_id: "", xoa_link: false,
    ...initial,
    skip_own_bot: Boolean(initial.skip_own_bot),
    image_filter_enabled: Boolean(initial.image_filter_enabled),
    image_filter_keywords: Array.isArray(initial.image_filter_keywords)
      ? initial.image_filter_keywords.map((k) => String(k || ""))
      : [],
  });
  const [saving, setSaving] = useState(false);
  const [receipt, setReceipt] = useState(null);
  const [savedId, setSavedId] = useState(initial._id || '');
  React.useEffect(() => { if (!open) { setReceipt(null); setSavedId(initial._id || ''); } }, [open]);
  const [selectedBotId, setSelectedBotId] = useState(zaloBots[0]?._id || "");
  const [zaloVerified, setZaloVerified] = useState(!!initial.platform_group_id && initial.platform === "zalo");
  const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));

  const isZalo = form.platform === "zalo";
  const keywords = Array.isArray(form.image_filter_keywords) ? form.image_filter_keywords : [];
  const updateKeyword = (i, value) => {
    set("image_filter_keywords", keywords.map((row, idx) => (idx === i ? value : row)));
  };
  const addKeyword = () => {
    if (keywords.length >= 20) return;
    set("image_filter_keywords", keywords.concat([""]));
  };
  const removeKeyword = (i) => {
    set("image_filter_keywords", keywords.filter((_, idx) => idx !== i));
  };

  const handleZaloVerified = (customerName) => {
    if (customerName !== null) {
      setZaloVerified(true);
      if (customerName) set("name", customerName);
    } else {
      setZaloVerified(false);
    }
  };

  const handleSave = async () => {
    if (!form.platform_group_id) {
      toast.error("Conversation ID is required");
      return;
    }
    if (!isZalo && !form.name) {
      toast.error("Name is required");
      return;
    }
    if (isZalo && !zaloVerified) {
      toast.error("Please verify the Conversation ID before saving");
      return;
    }

    // Check system limit for create operations
    const isEdit = !!savedId;
    if (!isEdit && !checkSystemLimit(stats?.limits, 'sourceGroups', toast)) {
      return;
    }

    setSaving(true);
    try {
      const result = await SourceGroupApi.save({
        ...form,
        _id: savedId || undefined,
        image_filter_keywords: keywords.map((k) => String(k || "").trim())
      });
      setSavedId(result.data?._id || savedId);
      setReceipt(result);
      toast.info('Đã lưu. Kiểm tra trạng thái áp dụng bên dưới.');
      onSaved();
    } catch (e) {
      toast.error(e.message);
    } finally {
      setSaving(false);
    }
  };

  if (!open) return null;
  return (
    <Modal open={open} onClose={onClose}
      title={savedId ? "Edit Source Group" : "Add Source Group"}
      subtitle="Group that messages are forwarded FROM"
      footer={
        <>
          <button className="btn ghost" onClick={onClose}>Close</button>
          <button className="btn primary" onClick={handleSave} disabled={saving || (isZalo && !zaloVerified)}>
            {saving ? "Saving…" : "Save"}
          </button>
        </>
      }
    >
      <ApplyStatusPanel type="source" resourceId={savedId} receipt={receipt} />
      <div className="field">
        <label className="field-label">Platform</label>
        <select className="select" value={form.platform} onChange={(e) => { set("platform", e.target.value); set("platform_group_id", ""); set("name", ""); setSelectedBotId(zaloBots[0]?._id || ""); setZaloVerified(false); }}>
          <option value="telegram">Telegram</option>
          <option value="zalo">Zalo</option>
        </select>
      </div>

      {isZalo ? (
        <>
          <div className="field">
            <label className="field-label">Zalo Bot</label>
            {zaloBots.length === 0 ? (
              <div className="muted" style={{ fontSize: 13 }}>No active Zalo bots. Add and start a Zalo bot first.</div>
            ) : (
              <select className="select" value={selectedBotId} onChange={(e) => { setSelectedBotId(e.target.value); set("platform_group_id", ""); set("name", ""); setZaloVerified(false); }}>
                {zaloBots.map((b) => (
                  <option key={b._id} value={b._id}>{b.name} ({b.phone || "no phone"})</option>
                ))}
              </select>
            )}
          </div>
          <ZaloConversationField
            botId={selectedBotId}
            value={form.platform_group_id}
            onChange={(v) => set("platform_group_id", v)}
            onVerified={handleZaloVerified}
          />
          <div className="field">
            <label className="field-label">Name <span className="muted">(auto-filled)</span></label>
            <input className="input" value={form.name} readOnly placeholder="Filled after Check" style={{ opacity: form.name ? 1 : 0.5 }} />
          </div>
        </>
      ) : (
        <>
          <div className="field">
            <label className="field-label">Platform Group ID</label>
            <input className="input mono" value={form.platform_group_id} onChange={(e) => set("platform_group_id", e.target.value)} placeholder="-100123456789" />
          </div>
          <div className="field">
            <label className="field-label">Name</label>
            <input className="input" value={form.name} onChange={(e) => set("name", e.target.value)} />
          </div>
          <div className="field">
            <label className="field-label">Language</label>
            <select className="select" value={form.language} onChange={(e) => set("language", e.target.value)}>
              <option value="vi">Vietnamese</option>
              <option value="en">English</option>
            </select>
          </div>
          <div className="field">
            <label className="field-label">Description</label>
            <input className="input" value={form.description} onChange={(e) => set("description", e.target.value)} />
          </div>
          <div className="field">
            <label className="field-label" style={{ display: "flex", justifyContent: "space-between" }}>
              <span>Topics enabled</span>
              <input type="checkbox" checked={form.topics_enabled} onChange={(e) => set("topics_enabled", e.target.checked)} />
            </label>
          </div>
          {form.topics_enabled && (
            <div className="field">
              <label className="field-label">Topic ID</label>
              <input className="input mono" value={form.topic_id} onChange={(e) => set("topic_id", e.target.value)} />
            </div>
          )}
          <div className="field">
            <label className="field-label" style={{ display: "flex", justifyContent: "space-between" }}>
              <span>Remove links</span>
              <input type="checkbox" checked={form.xoa_link} onChange={(e) => set("xoa_link", e.target.checked)} />
            </label>
          </div>
          <div className="field">
            <label className="field-label" style={{ display: "flex", justifyContent: "space-between" }}>
              <span>Skip own bot</span>
              <input type="checkbox" checked={!!form.skip_own_bot} onChange={(e) => set("skip_own_bot", e.target.checked)} />
            </label>
            <div className="muted" style={{ fontSize: 12, marginTop: 4 }}>
              Ignore messages sent by hub-connected accounts in this source.
            </div>
          </div>
          <div className="field">
            <label className="field-label" style={{ display: "flex", justifyContent: "space-between" }}>
              <span>Filter images</span>
              <input
                type="checkbox"
                checked={!!form.image_filter_enabled}
                onChange={(e) => {
                  const on = e.target.checked;
                  setForm((f) => ({
                    ...f,
                    image_filter_enabled: on,
                    image_filter_keywords: on && (!Array.isArray(f.image_filter_keywords) || f.image_filter_keywords.length === 0)
                      ? [""]
                      : f.image_filter_keywords
                  }));
                }}
              />
            </label>
          </div>
          {!!form.image_filter_enabled && (
          <div className="field">
            <label className="field-label">Image filter keywords <span className="muted">(optional)</span></label>
            <div className="muted" style={{ fontSize: 12, marginBottom: 8 }}>
              Case-insensitive contains. Prefer longer tokens (Gold, BMR). Short tokens like @ match many images.
            </div>
            {keywords.map((value, i) => (
              <div key={i} style={{ display: "flex", gap: 8, marginBottom: 6 }}>
                <input
                  className="input"
                  value={value}
                  maxLength={128}
                  placeholder={i === 1 ? "BMR" : "Gold"}
                  onChange={(e) => updateKeyword(i, e.target.value)}
                />
                <button type="button" className="btn ghost sm" onClick={() => removeKeyword(i)}>×</button>
              </div>
            ))}
            <button type="button" className="btn ghost" disabled={keywords.length >= 20} onClick={addKeyword}>Add keyword</button>
          </div>
          )}
        </>
      )}
    </Modal>
  );
};

// ─── Target group form modal ─────────────────────────────────────────────
const TargetGroupModal = ({ open, onClose, onSaved, initial = {}, zaloBots = [], stats }) => {
  const toast = useToast();
  const [form, setForm] = useState({
    name: "", platform_group_id: "", platform: "telegram",
    description: "", language: "vi", topics_enabled: false,
    topic_id: "", replace_username: "", watermark_image: "",
    keep_origin_forwarded: false,
    keep_origin_from_channel: false,
    ...initial,
    link_action: initial.link_action || "keep",
    replace_link: initial.replace_link || "",
    keep_origin_forwarded: Boolean(initial.keep_origin_forwarded),
    keep_origin_from_channel: Boolean(initial.keep_origin_from_channel),
    watermark_opacity_percent: initial.watermark_opacity_percent ?? 25,
    watermark_size_percent: initial.watermark_size_percent ?? 40,
    keyword_replacements: Array.isArray(initial.keyword_replacements)
      ? initial.keyword_replacements.map((r) => ({ from: r.from || "", to: r.to || "" }))
      : [],
  });
  const [saving, setSaving] = useState(false);
  const [receipt, setReceipt] = useState(null);
  const [savedId, setSavedId] = useState(initial._id || '');
  React.useEffect(() => { if (!open) { setReceipt(null); setSavedId(initial._id || ''); } }, [open]);
  const [wmUploading, setWmUploading] = useState(false);
  const [selectedBotId, setSelectedBotId] = useState(zaloBots[0]?._id || "");
  const [zaloVerified, setZaloVerified] = useState(!!initial.platform_group_id && initial.platform === "zalo");
  const set = (k, v) => setForm((f) => ({ ...f, [k]: v }));

  const isZalo = form.platform === "zalo";
  const keywordRows = Array.isArray(form.keyword_replacements) ? form.keyword_replacements : [];
  const updateKeywordRow = (i, key, value) => {
    set("keyword_replacements", keywordRows.map((row, idx) => (idx === i ? { ...row, [key]: value } : row)));
  };
  const addKeywordRow = () => {
    if (keywordRows.length >= 20) return;
    set("keyword_replacements", keywordRows.concat([{ from: "", to: "" }]));
  };
  const removeKeywordRow = (i) => {
    set("keyword_replacements", keywordRows.filter((_, idx) => idx !== i));
  };

  const previewWatermarkSrc = (v) => {
    const s = String(v || "");
    if (/^https?:\/\//i.test(s)) return s;
    if (s.startsWith("/uploads/")) return s;
    return "";
  };

  const handleWatermarkFile = async (e) => {
    const file = e.target.files && e.target.files[0];
    e.target.value = "";
    if (!file) return;
    if (!/^image\/(png|jpeg|webp)$/.test(file.type)) {
      toast.error("Only PNG, JPEG, or WebP images are allowed");
      return;
    }
    if (file.size > 2097152) {
      toast.error("Image must be ≤ 2MB. Compress it on tinypng.com.");
      return;
    }
    setWmUploading(true);
    try {
      const data = await TargetGroupApi.uploadWatermark(file);
      set("watermark_image", data.url);
    } catch (err) {
      toast.error(err.message || "Upload failed");
    } finally {
      setWmUploading(false);
    }
  };

  const handleZaloVerified = (customerName) => {
    if (customerName !== null) {
      setZaloVerified(true);
      if (customerName) set("name", customerName);
    } else {
      setZaloVerified(false);
    }
  };

  const handleSave = async () => {
    if (!form.platform_group_id) {
      toast.error("Conversation ID is required");
      return;
    }
    if (!isZalo && !form.name) {
      toast.error("Name is required");
      return;
    }
    if (isZalo && !zaloVerified) {
      toast.error("Please verify the Conversation ID before saving");
      return;
    }
    if (form.link_action === "replace" && !String(form.replace_link || "").trim()) {
      toast.error("Replacement link is required");
      return;
    }

    // Check system limit for create operations
    const isEdit = !!savedId;
    if (!isEdit && !checkSystemLimit(stats?.limits, 'targetGroups', toast)) {
      return;
    }

    setSaving(true);
    try {
      const result = await TargetGroupApi.save({
        ...form,
        _id: savedId || undefined,
        keyword_replacements: keywordRows
          .map((row) => ({ from: String(row.from || "").trim(), to: String(row.to || "").trim() }))
          .filter((row) => row.from),
      });
      setSavedId(result.data?._id || savedId);
      setReceipt(result);
      toast.info('Đã lưu. Kiểm tra trạng thái áp dụng bên dưới.');
      onSaved();
    } catch (e) {
      toast.error(e.message);
    } finally {
      setSaving(false);
    }
  };

  if (!open) return null;
  return (
    <Modal open={open} onClose={onClose}
      title={savedId ? "Edit Target Group" : "Add Target Group"}
      subtitle="Group that messages are forwarded TO"
      footer={
        <>
          <button className="btn ghost" onClick={onClose}>Close</button>
          <button className="btn primary" onClick={handleSave} disabled={saving || (isZalo && !zaloVerified)}>
            {saving ? "Saving…" : "Save"}
          </button>
        </>
      }
    >
      <ApplyStatusPanel type="target" resourceId={savedId} receipt={receipt} />
      <div className="field">
        <label className="field-label">Platform</label>
        <select className="select" value={form.platform} onChange={(e) => { set("platform", e.target.value); set("platform_group_id", ""); set("name", ""); setSelectedBotId(zaloBots[0]?._id || ""); setZaloVerified(false); }}>
          <option value="telegram">Telegram</option>
          <option value="zalo">Zalo</option>
        </select>
      </div>

      {isZalo ? (
        <>
          <div className="field">
            <label className="field-label">Zalo Bot</label>
            {zaloBots.length === 0 ? (
              <div className="muted" style={{ fontSize: 13 }}>No active Zalo bots. Add and start a Zalo bot first.</div>
            ) : (
              <select className="select" value={selectedBotId} onChange={(e) => { setSelectedBotId(e.target.value); set("platform_group_id", ""); set("name", ""); setZaloVerified(false); }}>
                {zaloBots.map((b) => (
                  <option key={b._id} value={b._id}>{b.name} ({b.phone || "no phone"})</option>
                ))}
              </select>
            )}
          </div>
          <ZaloConversationField
            botId={selectedBotId}
            value={form.platform_group_id}
            onChange={(v) => set("platform_group_id", v)}
            onVerified={handleZaloVerified}
          />
          <div className="field">
            <label className="field-label">Name <span className="muted">(auto-filled)</span></label>
            <input className="input" value={form.name} readOnly placeholder="Filled after Check" style={{ opacity: form.name ? 1 : 0.5 }} />
          </div>
        </>
      ) : (
        <>
          <div className="field">
            <label className="field-label">Platform Group ID</label>
            <input className="input mono" value={form.platform_group_id} onChange={(e) => set("platform_group_id", e.target.value)} placeholder="-100123456789" />
          </div>
          <div className="field">
            <label className="field-label">Name</label>
            <input className="input" value={form.name} onChange={(e) => set("name", e.target.value)} />
          </div>
          <div className="field">
            <label className="field-label">Language</label>
            <select className="select" value={form.language} onChange={(e) => set("language", e.target.value)}>
              <option value="vi">Vietnamese</option>
              <option value="en">English</option>
            </select>
          </div>
          <div className="field">
            <label className="field-label">Description</label>
            <input className="input" value={form.description} onChange={(e) => set("description", e.target.value)} />
          </div>
          <div className="field">
            <label className="field-label" style={{ display: "flex", justifyContent: "space-between" }}>
              <span>Topics enabled</span>
              <input type="checkbox" checked={form.topics_enabled} onChange={(e) => set("topics_enabled", e.target.checked)} />
            </label>
          </div>
          {form.topics_enabled && (
            <div className="field">
              <label className="field-label">Topic ID</label>
              <input className="input mono" value={form.topic_id} onChange={(e) => set("topic_id", e.target.value)} />
            </div>
          )}
          <div className="field">
            <label className="field-label">Replace username <span className="muted">(optional)</span></label>
            <input className="input" value={form.replace_username} onChange={(e) => set("replace_username", e.target.value)} placeholder="@newhandle (5-32 ký tự)" />
          </div>
          <div className="field">
            <label className="field-label">Link action</label>
            <select className="select" value={form.link_action || "keep"} onChange={(e) => set("link_action", e.target.value)}>
              <option value="keep">Keep original links</option>
              <option value="remove">Remove links</option>
              <option value="replace">Replace with my link</option>
            </select>
          </div>
          {form.link_action === "replace" && (
            <div className="field">
              <label className="field-label">Replacement link</label>
              <input className="input" value={form.replace_link} onChange={(e) => set("replace_link", e.target.value)} placeholder="https://t.me/yourchannel" />
            </div>
          )}
          <div className="field">
            <label className="field-label">Auto-edit keywords <span className="muted">(optional)</span></label>
            {keywordRows.map((row, i) => (
              <div key={i} className="keyword-row" style={{ display: "flex", gap: 8, marginBottom: 8 }}>
                <input
                  className="input"
                  value={row.from}
                  maxLength={128}
                  placeholder="TuanAnhVipPro"
                  onChange={(e) => updateKeywordRow(i, "from", e.target.value)}
                />
                <input
                  className="input"
                  value={row.to}
                  maxLength={128}
                  placeholder="KEvinDaiGia"
                  onChange={(e) => updateKeywordRow(i, "to", e.target.value)}
                />
                <button type="button" className="btn ghost" onClick={() => removeKeywordRow(i)}>×</button>
              </div>
            ))}
            <button type="button" className="btn ghost" disabled={keywordRows.length >= 20} onClick={addKeywordRow}>
              Add keyword
            </button>
            <div className="muted" style={{ marginTop: 6 }}>Matches ignore case. Replacement uses the text you type.</div>
          </div>
          <div className="field">
            <label className="field-label">Keep origin forwarded</label>
            <label style={{ display: "flex", gap: 16, alignItems: "center" }}>
              <label style={{ display: "flex", gap: 6, alignItems: "center" }}>
                <input type="checkbox" checked={!!form.keep_origin_forwarded}
                  onChange={(e) => set("keep_origin_forwarded", e.target.checked)} />
                <span>User</span>
              </label>
              <label style={{ display: "flex", gap: 6, alignItems: "center" }}>
                <input type="checkbox" checked={!!form.keep_origin_from_channel}
                  onChange={(e) => set("keep_origin_from_channel", e.target.checked)} />
                <span>Channel</span>
              </label>
            </label>
            <div className="muted">Native-forward with original author. Unchecked types use the copy pipeline.</div>
          </div>
          <div className="field">
            <label className="field-label">Watermark image <span className="muted">(optional)</span></label>
            <input
              type="file"
              accept="image/png,image/jpeg,image/webp"
              disabled={wmUploading}
              onChange={handleWatermarkFile}
            />
            {form.watermark_image ? (
              <div className="wm-preview">
                {previewWatermarkSrc(form.watermark_image) ? (
                  <img src={previewWatermarkSrc(form.watermark_image)} alt="" />
                ) : null}
                <button type="button" className="btn ghost sm" onClick={() => set("watermark_image", "")}>Clear</button>
              </div>
            ) : null}
            <input
              className="input"
              value={form.watermark_image}
              onChange={(e) => set("watermark_image", e.target.value)}
              placeholder="https://… or upload above"
            />
            <div className="field-help">
              Max 2MB. Compress first at <a href="https://tinypng.com" target="_blank" rel="noopener noreferrer">tinypng.com</a>.
            </div>
            <div style={{ display: "flex", gap: 12, marginTop: 10 }}>
              <div className="field" style={{ flex: 1, marginBottom: 0 }}>
                <label className="field-label">Opacity (%)</label>
                <input
                  className="input"
                  type="number"
                  min={1}
                  max={100}
                  value={form.watermark_opacity_percent}
                  onChange={(e) => set("watermark_opacity_percent", e.target.value)}
                />
              </div>
              <div className="field" style={{ flex: 1, marginBottom: 0 }}>
                <label className="field-label">Size (%)</label>
                <input
                  className="input"
                  type="number"
                  min={1}
                  max={100}
                  value={form.watermark_size_percent}
                  onChange={(e) => set("watermark_size_percent", e.target.value)}
                />
              </div>
            </div>
            <div className="field-help">
              Opacity and logo size per target. Size is % of photo width. Default 25 / 40.
            </div>
          </div>
        </>
      )}
    </Modal>
  );
};

const groupPlatform = (g) => g.platform || "telegram";

const GROUP_SEARCH_FIELDS = [
  (g) => g.name,
  (g) => g.platform_group_id,
  (g) => g.telegram_id,
  groupPlatform,
  (g) => g.language,
];

const GROUP_SORT_GETTERS = {
  name: (g) => g.name || "",
  platform_group_id: (g) => g.platform_group_id || "",
  platform: groupPlatform,
  language: (g) => g.language || "",
  topics: (g) => (g.topics_enabled ? 1 : 0),
  updated_at: (g) => (typeof modifiedAtMs === "function" ? modifiedAtMs(g) : null),
};

const TARGET_SEARCH_FIELDS = window.TARGET_SEARCH_FIELDS || GROUP_SEARCH_FIELDS;
const TARGET_SORT_GETTERS = window.TARGET_SORT_GETTERS || GROUP_SORT_GETTERS;
const SOURCE_SEARCH_FIELDS = window.SOURCE_SEARCH_FIELDS || GROUP_SEARCH_FIELDS;
const SOURCE_SORT_GETTERS = window.SOURCE_SORT_GETTERS || GROUP_SORT_GETTERS;
const SOURCE_COLUMN_COUNT = window.SOURCE_COLUMN_COUNT || 8;
const TARGET_COLUMN_COUNT = window.TARGET_COLUMN_COUNT || 10;
const formatTopicsLabel = window.formatTopicsLabel || ((g) => (g.topics_enabled ? "On" : "Off"));
const formatReplaceUsername = window.formatReplaceUsername || ((g) => g.replace_username || "—");
const formatLinkAction = window.formatLinkAction || ((g) => g.link_action || "keep");
const formatRemoveLinks = window.formatRemoveLinks || ((g) => (g.xoa_link ? "Remove links" : "Keep links"));
const formatKeepOriginForwarded = window.formatKeepOriginForwarded || ((g) => {
  const user = Boolean(g.keep_origin_forwarded);
  const channel = Boolean(g.keep_origin_from_channel);
  if (user && channel) return "User + Channel";
  if (user) return "User";
  if (channel) return "Channel";
  return "Off";
});

// ─── Groups screen (Source | Target) ─────────────────────────────────────
const GroupsScreen = ({ role = "source", listQuery = "", onQuery, stats }) => {
  const toast = useToast();
  const confirm = useConfirm();
  const isSource = role === "source";
  const [showAdd, setShowAdd] = useState(false);
  const [editItem, setEditItem] = useState(null);
  const [deletedReceipt, setDeletedReceipt] = useState(null);
  const [platform, setPlatform] = useState("all");
  const [tick, setTick] = useState(0);
  const refetch = () => setTick((t) => t + 1);
  const { sortKey, sortDir, toggleSort } = useListSort("updated_at", "desc");

  const { data, loading, error } = useApi(async () => {
    const [items, allBots] = await Promise.all([
      isSource ? SourceGroupApi.list() : TargetGroupApi.list(),
      BotApi.list(),
    ]);
    return {
      groups: (items || []).map((g) => ({ ...g, _role: isSource ? "source" : "target" })),
      zaloBots: (allBots || []).filter((b) => b.platform === "zalo" && b.status === "active"),
    };
  }, [tick, role]);

  const groups = data?.groups || [];
  const zaloBots = data?.zaloBots || [];
  const searchFields = isSource ? SOURCE_SEARCH_FIELDS : TARGET_SEARCH_FIELDS;
  const sortGetters = isSource ? SOURCE_SORT_GETTERS : TARGET_SORT_GETTERS;
  const columnCount = isSource ? SOURCE_COLUMN_COUNT : TARGET_COLUMN_COUNT;
  const filtered = groups.filter((g) =>
    (platform === "all" || groupPlatform(g) === platform) &&
    matchQuery(g, listQuery, searchFields)
  );
  const visible = sortItems(filtered, { key: sortKey, dir: sortDir, getters: sortGetters });
  const filterActive = platform !== "all" || String(listQuery || "").trim() !== "";

  const handleDelete = async (item) => {
    const ok = await confirm("Delete group", `Delete "${item.name}"? This cannot be undone.`);
    if (!ok) return;
    try {
      const result = isSource ? await SourceGroupApi.delete(item._id) : await TargetGroupApi.delete(item._id);
      setDeletedReceipt({ result, id: item._id, type: isSource ? 'source' : 'target' });
      toast.info('Đã xóa. Kiểm tra trạng thái áp dụng bên dưới.');
      refetch();
    } catch (e) {
      toast.error(e.message);
    }
  };

  if (error) return (
    <div className="page">
      <Empty icon={I.AlertTriangle} title={`Failed to load ${isSource ? "source" : "target"} groups`} sub={error} action={<button className="btn" onClick={refetch}><I.Refresh /> Retry</button>} />
    </div>
  );

  return (
    <div className="page">
      <div className="page-head sticky-title">
        <div>
          <h1 className="page-title">{isSource ? "Source" : "Target"}</h1>
          <div className="page-sub">
            {isSource
              ? "Groups that messages are forwarded FROM."
              : "Groups that forwarded messages land IN."}
            {!loading && filterActive ? ` · ${visible.length} shown` : ""}
          </div>
        </div>
        <div style={{ display: "flex", gap: 8 }}>
          <button className="btn primary" onClick={() => setShowAdd(true)} disabled={isSystemAtLimit(stats?.limits, isSource ? 'sourceGroups' : 'targetGroups')} title={isSystemAtLimit(stats?.limits, isSource ? 'sourceGroups' : 'targetGroups') ? 'System limit reached' : undefined}>
            <I.Plus /> {isSource ? "Add source" : "Add target"}
          </button>
        </div>
      </div>

      {deletedReceipt?.type === role && <ApplyStatusPanel type={role} resourceId={deletedReceipt.id} receipt={deletedReceipt.result} deleted />}
      <div className="kpi-row" style={{ gridTemplateColumns: "1fr" }}>
        <Kpi label={isSource ? "Total sources" : "Total targets"} value={groups.length} />
      </div>

      <div className="toolbar">
        <PlatformFilter value={platform} onChange={setPlatform} />
        <ListSearch
          value={listQuery}
          onChange={onQuery}
          placeholder="Search name or Telegram ID…"
        />
      </div>

      <div className="card flush">
        <div style={{ overflow: "auto" }}>
          <table className="table">
            <thead>
              <tr>
                <SortableTh label="Name" column="name" sortKey={sortKey} sortDir={sortDir} onToggle={toggleSort} />
                <SortableTh label="Group ID" column="platform_group_id" sortKey={sortKey} sortDir={sortDir} onToggle={toggleSort} />
                <SortableTh label="Platform" column="platform" sortKey={sortKey} sortDir={sortDir} onToggle={toggleSort} />
                <SortableTh label="Language" column="language" sortKey={sortKey} sortDir={sortDir} onToggle={toggleSort} />
                <SortableTh label="Topics" column="topics" sortKey={sortKey} sortDir={sortDir} onToggle={toggleSort} />
                {isSource && (
                  <SortableTh label="Remove links" column="xoa_link" sortKey={sortKey} sortDir={sortDir} onToggle={toggleSort} />
                )}
                {!isSource && (
                  <>
                    <SortableTh label="Username" column="replace_username" sortKey={sortKey} sortDir={sortDir} onToggle={toggleSort} />
                    <SortableTh label="Link" column="link_action" sortKey={sortKey} sortDir={sortDir} onToggle={toggleSort} />
                    <SortableTh label="Origin" column="keep_origin_forwarded" sortKey={sortKey} sortDir={sortDir} onToggle={toggleSort} />
                  </>
                )}
                <SortableTh label="Modified Date" column="updated_at" sortKey={sortKey} sortDir={sortDir} onToggle={toggleSort} />
                <th style={{ width: 80 }}>Actions</th>
              </tr>
            </thead>
            <tbody>
              {loading ? (
                <SkeletonRows cols={columnCount} rows={4} />
              ) : groups.length === 0 ? (
                <tr>
                  <td colSpan={columnCount}>
                    <Empty
                      icon={isSource ? I.Radio : I.MessagesSquare}
                      title={isSource ? "No source groups" : "No target groups"}
                      sub={isSource ? "Add a source group to start receiving messages." : "Add a target group to start forwarding messages."}
                    />
                  </td>
                </tr>
              ) : visible.length === 0 ? (
                <tr>
                  <td colSpan={columnCount}>
                    <Empty
                      icon={I.Search}
                      title={isSource ? "No sources match" : "No targets match"}
                      sub="Clear search or filters to see all groups."
                    />
                  </td>
                </tr>
              ) : (
                visible.map((g) => (
                  <tr key={`${g._role}-${g._id}`}>
                    <td style={{ fontWeight: 500 }}>{g.name}</td>
                    <td className="mono muted">{g.platform_group_id}</td>
                    <td><PlatformChip platform={groupPlatform(g)} /></td>
                    <td className="muted">{g.language || "—"}</td>
                    <td>
                      {g.topics_enabled
                        ? <span className="badge accent"><span className="dot"></span>{formatTopicsLabel(g)}</span>
                        : <span className="muted">{formatTopicsLabel(g)}</span>
                      }
                    </td>
                    {isSource && (
                      <td>
                        {g.xoa_link
                          ? <span className="badge yellow">{formatRemoveLinks(g)}</span>
                          : <span className="muted">{formatRemoveLinks(g)}</span>}
                      </td>
                    )}
                    {!isSource && (
                      <>
                        <td className={formatReplaceUsername(g) !== "—" ? "mono" : "muted"}>{formatReplaceUsername(g)}</td>
                        <td title={g.link_action === "replace" ? (g.replace_link || "") : formatLinkAction(g)}>
                          {g.link_action === "remove" ? (
                            <span className="badge yellow">{formatLinkAction(g)}</span>
                          ) : g.link_action === "replace" ? (
                            <div>
                              <span className="badge accent">Replace with my link</span>
                              {g.replace_link ? (
                                <div className="mono muted" style={{ marginTop: 4, maxWidth: 220, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                                  {g.replace_link}
                                </div>
                              ) : null}
                            </div>
                          ) : (
                            <span className="muted">{formatLinkAction(g)}</span>
                          )}
                        </td>
                        <td>
                          {(() => {
                            const originLabel = formatKeepOriginForwarded(g);
                            return originLabel !== "Off" && originLabel !== "—"
                              ? <span className="badge accent">{originLabel}</span>
                              : <span className="muted">{originLabel}</span>;
                          })()}
                        </td>
                      </>
                    )}
                    <td className="muted tnum">{formatModifiedDate(g)}</td>
                    <td>
                      <div style={{ display: "flex", gap: 4 }}>
                        <button className="btn ghost icon-only sm" onClick={() => setEditItem(g)}><I.Edit size={12} /></button>
                        <button className="btn ghost icon-only sm" onClick={() => handleDelete(g)} style={{ color: "var(--red-fg)" }}><I.Trash size={12} /></button>
                      </div>
                    </td>
                  </tr>
                ))
              )}
            </tbody>
          </table>
        </div>
      </div>

      {isSource ? (
        <>
          <SourceGroupModal key={showAdd ? 'open' : 'closed'} open={showAdd} onClose={() => setShowAdd(false)} onSaved={refetch} zaloBots={zaloBots} stats={stats} />
        {editItem && (
            <SourceGroupModal open={true} onClose={() => setEditItem(null)} onSaved={refetch} initial={editItem} zaloBots={zaloBots} stats={stats} />
          )}
        </>
      ) : (
        <>
          <TargetGroupModal key={showAdd ? 'open' : 'closed'} open={showAdd} onClose={() => setShowAdd(false)} onSaved={refetch} zaloBots={zaloBots} stats={stats} />
        {editItem && (
            <TargetGroupModal open={true} onClose={() => setEditItem(null)} onSaved={refetch} initial={editItem} zaloBots={zaloBots} stats={stats} />
          )}
        </>
      )}
    </div>
  );
};

window.GroupsScreen = GroupsScreen;
