/**
 * @file SmsCampaignPanel.jsx
 * @description SMS Campaign management for mega_admin.
 *   Campaign cards + settings + log table + queue with next-send times
 */
import React, { useState, useEffect, useCallback, useRef } from "react";
import { useNavigate } from "react-router-dom";
import { Panel } from "../UI/Panel";
import { Badge } from "../UI/Badge";
import { supabase, supabaseUrl, supabaseAnonKey } from "../../supabaseClient";
import { Pagination } from "../UI/Pagination";
import { SmsCampaignStats } from "./SmsCampaignStats";

// ── Campaigns ────────────────────────────────────────────────────────────────
const CAMPAIGNS = [
  { key: "first_sms",      label: "Первая отправка",    icon: "📤", desc: "Первое SMS клиенту: ссылка на согласование доставки", queueStatus: "link_ready" },
  { key: "second_sms",     label: "Второе сообщение",   icon: "📨", desc: "Повторное SMS через 3ч, если клиент не согласовал",  queueStatus: "first_sms_sent" },
  { key: "manual",         label: "Ручное управление",  icon: "🔧", desc: "Перевод к ручному согласованию после двух SMS",       queueStatus: "second_sms_sent" },
  { key: "paid_storage",   label: "Платное хранение",   icon: "📦", desc: "Уведомление о платном хранении",                     queueStatus: null },
];

const HAS_SETTINGS = ["first_sms", "second_sms", "manual", "paid_storage"];
const MANUAL_CAMPAIGN = "manual";

// ── Days of week ─────────────────────────────────────────────────────────────
const DAYS = [
  { num: 1, short: "Пн" }, { num: 2, short: "Вт" }, { num: 3, short: "Ср" },
  { num: 4, short: "Чт" }, { num: 5, short: "Пт" }, { num: 6, short: "Сб" },
  { num: 7, short: "Вс" },
];

// ── Status labels ────────────────────────────────────────────────────────────
const STATUS_LABELS = {
  sent: "SMS отправлено",
  checking: "В пути к абоненту",
  delivered: "✓ Доставлено",
  send_failed: "Не отправлено",
  error: "Ошибка доставки",
  expired: "Срок истёк",
  limit_exceeded: "Лимит превышен",
  manual_override: "Ручной режим",
};

const STATUS_TONES = {
  sent: "info",
  checking: "neutral",
  delivered: "accent",
  send_failed: "danger",
  error: "danger",
  expired: "warning",
  limit_exceeded: "danger",
  manual_override: "warning",
};

const STATUS_ICONS = {
  sent: "📤",
  checking: "📡",
  delivered: "✅",
  send_failed: "❌",
  error: "⛔",
  expired: "⏰",
  limit_exceeded: "🚫",
  manual_override: "✋",
};

// ── SMS code labels ──────────────────────────────────────────────────────────
const SMS_CODE_LABELS = {
  "100": "В очереди SMS.ru", "101": "Передано оператору", "102": "В пути",
  "103": "Доставлено", "104": "Истёкло время", "105": "Удалено оператором",
  "106": "Сбой телефона", "107": "Неизвестная причина", "108": "Отклонено",
  "130": "Лимит на номер/день", "131": "Лимит одинаковых/мин",
  "132": "Лимит одинаковых/день", "200": "Неправильный api_id",
  "201": "Недостаточно средств", "202": "Неправильный получатель",
  "230": "Общий лимит/день", "231": "Лимит одинаковых/мин",
  "232": "Лимит одинаковых/день",
};

// ── Notification status labels (for queue) ──────────────────────────────────
const NOTIF_LABELS = {
  "not_started":      "⏳ В очереди",
  "link_ready":       "⏳ В очереди",
  "sms_sending":      "📡 Отправляется…",
  "first_sms_sent":   "✓ 1-я SMS отправлена",
  "second_sms_sending": "📡 2-я SMS отправляется…",
  "second_sms_sent":  "✓ 2-я SMS отправлена",
  "send_failed":      "❌ Ошибка отправки",
  "manual_required":  "🔧 Ручное управление",
  "paid_storage_sending": "📡 Отправляется…",
  "paid_storage_sent": "✓ Платное хранение: отправлено",
  "completed":         "✅ Завершено",
  "confirmed":         "✅ Согласовано клиентом",
  "address_required":  "⚠ Требуется адрес",
  "draft":            "Черновик",
};

const NOTIF_TONES = {
  "not_started":      "neutral",
  "link_ready":       "warning",
  "sms_sending":      "info",
  "first_sms_sent":   "info",
  "second_sms_sending": "info",
  "second_sms_sent":  "info",
  "send_failed":      "danger",
  "manual_required":  "warning",
  "paid_storage_sending": "info",
  "paid_storage_sent": "accent",
  "completed":         "accent",
  "confirmed":         "accent",
  "address_required":  "danger",
  "draft":            "neutral",
};

// ── Helpers ──────────────────────────────────────────────────────────────────
const fmtTime = (ts) => {
  if (!ts) return "—";
  try {
    return new Date(ts).toLocaleString("ru-RU", {
      day: "2-digit", month: "2-digit", year: "2-digit",
      hour: "2-digit", minute: "2-digit",
    });
  } catch { return ts; }
};

const fmtTimeShort = (ts) => {
  if (!ts) return "—";
  try {
    return new Date(ts).toLocaleString("ru-RU", {
      hour: "2-digit", minute: "2-digit",
    });
  } catch { return ts; }
};

const fmtPhone = (phone) => {
  if (!phone) return "—";
  const digits = String(phone).replace(/\D/g, "");
  if (digits.length === 11 && digits.startsWith("7")) {
    return `+7 (${digits.slice(1, 4)}) ${digits.slice(4, 7)}-${digits.slice(7, 9)}-${digits.slice(9)}`;
  }
  return phone;
};

const fmtElapsed = (ts) => {
  if (!ts) return "—";
  const diff = Date.now() - new Date(ts).getTime();
  const m = Math.floor(diff / 60000);
  if (m < 1) return "только что";
  if (m < 60) return `${m} мин назад`;
  const h = Math.floor(m / 60);
  const rest = m % 60;
  return `${h}ч ${rest}м назад`;
};

// ── Date filter helpers ───────────────────────────────────────────────
const isToday = (ts) => {
  if (!ts) return false;
  const d = new Date(ts);
  const now = new Date();
  return d.getDate() === now.getDate() && d.getMonth() === now.getMonth() && d.getFullYear() === now.getFullYear();
};

const isYesterday = (ts) => {
  if (!ts) return false;
  const d = new Date(ts);
  const y = new Date();
  y.setDate(y.getDate() - 1);
  return d.getDate() === y.getDate() && d.getMonth() === y.getMonth() && d.getFullYear() === y.getFullYear();
};

const isWithin7d = (ts) => {
  if (!ts) return false;
  return new Date(ts).getTime() >= Date.now() - 7 * 24 * 3600 * 1000;
};

const passesDateFilter = (ts, df) => {
  if (df === "all" || !df) return true;
  if (df === "today") return isToday(ts);
  if (df === "yesterday") return isYesterday(ts);
  if (df === "7d") return isWithin7d(ts);
  return true;
};

const passesLinkFilter = (isOpened, lf) => {
  if (lf === "all" || !lf) return true;
  if (lf === "opened") return isOpened === true;
  if (lf === "not_opened") return isOpened !== true;
  return true;
};

// smsFilter: "no_sms" = group has no sms_sent_at and no first/second_sms_sent_at
const passesSmsFilter = (group, sf) => {
  if (sf === "all" || !sf) return true;
  if (sf === "no_sms") {
    return !group.sms_sent_at && !group.first_sms_sent_at && !group.second_sms_sent_at;
  }
  return true;
};

const fmtCountdown = (ts) => {
  if (!ts) return "—";
  const diff = new Date(ts).getTime() - Date.now();
  if (diff <= 0) return "сейчас";
  const m = Math.floor(diff / 60000);
  if (m < 60) return `через ${m} мин`;
  const h = Math.floor(m / 60);
  const rest = m % 60;
  return `через ${h}ч ${rest}м`;
};

// ── Agreement formatter (delivery/pickup status) ────────────────────────────
const fmtAgreement = (g) => {
  if (!g) return { text: "—", tone: "neutral", icon: "⏳" };
  const ds = g.delivery_status;

  // Terminal states first — delivery already happened or cancelled
  if (ds === "picked_up") {
    return { text: "✅ Самовывоз завершён", tone: "accent", icon: "✅" };
  }
  if (ds === "delivered") {
    return { text: "✅ Доставлено", tone: "accent", icon: "✅" };
  }
  if (ds === "cancelled") {
    return { text: "❌ Отменён", tone: "danger", icon: "❌" };
  }

  // If client already chose date/time — show it
  if (g.delivery_type === "pickup" || g.pickup_date) {
    const date = g.pickup_date ? new Date(g.pickup_date).toLocaleDateString("ru-RU", { day: "2-digit", month: "2-digit" }) : "";
    const slot = g.pickup_time_slot || "";
    const hasAll = date && slot;
    return { text: `🏪 Самовывоз${date ? " " + date : ""}${slot ? ", " + slot : ""}${!hasAll ? " (неполно)" : ""}`, tone: "accent", icon: "🏪" };
  }
  if (g.delivery_date) {
    const date = new Date(g.delivery_date).toLocaleDateString("ru-RU", { day: "2-digit", month: "2-digit" });
    const time = g.delivery_time || "";
    // Use delivery_address, or customer_address if it's NOT 'САМОВЫВОЗ'
    const rawAddr = g.delivery_address || (g.customer_address && !/^самовывоз/i.test(g.customer_address.trim()) ? g.customer_address : "");
    const addrShort = rawAddr ? ` (${rawAddr.slice(0, 30)}${rawAddr.length > 30 ? "…" : ""})` : "";
    const missingAddr = !rawAddr && ds === "requires_address";
    return { text: `📦 Доставка${date ? " " + date : ""}${time ? ", " + time : ""}${addrShort}${missingAddr ? " ⚠ адрес" : ""}`, tone: missingAddr ? "warning" : "accent", icon: "📦" };
  }

  // No chosen date — status-based messages
  if (ds === "requires_address" || ds === "address_required") {
    return { text: "⚠ Адрес нужен", tone: "warning", icon: "⚠" };
  }
  if (ds === "manual_confirmation_required") {
    return { text: "🔧 Ручное управление", tone: "warning", icon: "🔧" };
  }
  if (ds === "paid_storage") {
    return { text: "📦 Платное хранение", tone: "warning", icon: "📦" };
  }
  if (ds === "problem") {
    return { text: "⚠ Проблема", tone: "danger", icon: "⚠" };
  }
  // pending_confirmation — not agreed yet
  return { text: "⏳ Не согласовано", tone: "neutral", icon: "⏳" };
};

const AGREEMENT_TONES = {
  accent: "#22c55e",
  warning: "var(--color-warning)",
  danger: "var(--color-danger)",
  neutral: "var(--color-text-muted)",
};

const parseWorkDays = (str) => {
  if (!str) return new Set([1, 2, 3, 4, 5]);
  return new Set(str.split(",").map(s => parseInt(s.trim())).filter(n => n >= 1 && n <= 7));
};

const serializeWorkDays = (set) => [...set].sort().join(",");

// Calculate next send time based on work hours/days
const calcNextSendTime = (group, settings, campaignKey) => {
  if (!settings) return null;
  const now = new Date();
  const workStart = settings.work_hours_start ?? 8;
  const workEnd = settings.work_hours_end ?? 21;
  const workDays = parseWorkDays(settings.work_days);
  const delayHours = settings.second_sms_delay_hours ?? 3;

  let base;
  if (campaignKey === "first_sms") {
    // Next send: next work window
    base = new Date(now);
  } else if (campaignKey === "second_sms") {
    // Second SMS: first_sms_sent_at + delayHours
    if (!group.first_sms_sent_at) return null;
    base = new Date(group.first_sms_sent_at);
    base.setHours(base.getHours() + delayHours);
  } else if (campaignKey === "manual") {
    // Manual: second_sms_sent_at + auto_manual_after_hours
    if (!group.second_sms_sent_at) return null;
    base = new Date(group.second_sms_sent_at);
    base.setHours(base.getHours() + (settings.auto_manual_after_hours ?? 3));
  } else {
    base = new Date(now);
  }

  // If base is in the past, find next work window from now
  if (base < now) base = new Date(now);

  // Adjust to work hours/days
  let candidate = new Date(base);
  // Round up to next minute
  candidate.setSeconds(0, 0);

  // If outside work hours or not a work day, find next valid slot
  for (let i = 0; i < 14 * 24; i++) { // max 14 days
    const day = candidate.getDay() === 0 ? 7 : candidate.getDay();
    const hour = candidate.getHours();
    if (workDays.has(day) && hour >= workStart && hour < workEnd) {
      return candidate.toISOString();
    }
    candidate.setHours(candidate.getHours() + 1, 0, 0, 0);
  }
  return null;
};

// ── Toggle Switch ────────────────────────────────────────────────────────────
const ToggleSwitch = ({ on, onClick, disabled, size = "md" }) => {
  const dims = size === "sm" ? { w: "w-9", h: "h-5", knob: "h-4 w-4", on: "left-[18px]", off: "left-0.5" }
    : { w: "w-12", h: "h-7", knob: "h-6 w-6", on: "left-[22px]", off: "left-0.5" };
  return (
    <button
      type="button"
      onClick={onClick}
      disabled={disabled}
      className={`relative ${dims.w} ${dims.h} rounded-full transition-all duration-200 disabled:opacity-50 ${
        on ? "bg-[var(--color-accent)]" : "bg-[var(--color-border)]"
      }`}
    >
      <span className={`absolute top-0.5 ${dims.knob} rounded-full bg-white shadow-sm transition-all duration-200 ${on ? dims.on : dims.off}`} />
    </button>
  );
};

// ── Mini Toggle (for timer in card) ──────────────────────────────────────────
const MiniToggle = ({ on, onClick, disabled }) => (
  <button
    type="button"
    onClick={onClick}
    disabled={disabled}
    className={`relative h-3.5 w-7 rounded-full transition-all duration-200 disabled:opacity-50 ${
      on ? "bg-[#22c55e]" : "bg-[var(--color-border)]"
    }`}
  >
    <span className={`absolute top-0.5 h-2.5 w-2.5 rounded-full bg-white shadow-sm transition-all duration-200 ${on ? "left-[14px]" : "left-0.5"}`} />
  </button>
);

// ── KPI Tile ─────────────────────────────────────────────────────────────────
const KpiTile = ({ icon, value, label, color }) => (
  <div className="rounded-xl bg-[var(--color-surface-strong)] border border-[var(--color-border)] px-3 py-3 text-center transition hover:shadow-sm">
    <div className="text-xs uppercase tracking-wide text-[var(--color-text-muted)] mb-1.5">{label}</div>
    <div className="text-lg font-bold" style={{ color: color || "var(--color-text)" }}>
      {icon && <span className="mr-1 text-base">{icon}</span>}{value}
    </div>
  </div>
);

// ── SettingField ─────────────────────────────────────────────────────────────
const SettingField = ({ label, value, onChange }) => (
  <label className="block">
    <span className="mb-1 block text-[10px] font-medium uppercase tracking-wide text-[var(--color-text-muted)]">{label}</span>
    <input
      type="text"
      value={value ?? ""}
      onChange={(e) => onChange(e.target.value)}
      className="w-full rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)] px-3 py-2 text-xs text-[var(--color-text)] transition focus:border-[var(--color-accent)] focus:outline-none focus:ring-2 focus:ring-[var(--color-accent-soft)]"
    />
  </label>
);

// ── FilterButton ─────────────────────────────────────────────────────────────
const FilterButton = ({ active, onClick, children }) => (
  <button
    onClick={onClick}
    className={`rounded-full px-3.5 py-1.5 text-xs font-medium transition-all duration-200 ${
      active
        ? "bg-[var(--color-accent)] text-white shadow-sm"
        : "border border-[var(--color-border)] bg-[var(--color-surface)] text-[var(--color-text-muted)] hover:bg-[var(--color-surface-strong)] hover:text-[var(--color-text)]"
    }`}
  >
    {children}
  </button>
);

// ── Card Button ──────────────────────────────────────────────────────────────
const CardButton = ({ onClick, disabled, tone, children }) => {
  const tones = {
    warning: "border-[var(--color-warning)] text-[var(--color-warning)] hover:bg-[var(--color-accent-soft)]",
    accent: "border-[var(--color-accent)] text-[var(--color-accent)] hover:bg-[var(--color-accent-soft)]",
    danger: "border-[var(--color-danger)] text-[var(--color-danger)] hover:bg-[rgba(239,68,68,0.08)]",
  };
  return (
    <button
      type="button"
      onClick={onClick}
      disabled={disabled}
      className={`rounded-xl border px-4 py-2 text-xs font-medium transition-all duration-200 disabled:opacity-40 ${tones[tone] || tones.accent}`}
    >
      {children}
    </button>
  );
};

// ── Table helpers (desktop) ──────────────────────────────────────────────────
const TableHeader = ({ cols }) => (
  <div
    className="hidden sm:grid border-b border-[var(--color-border)] bg-[var(--color-surface-strong)] text-[10px] font-semibold uppercase tracking-wider text-[var(--color-text-muted)]"
    style={{ gridTemplateColumns: cols.map(c => c.width).join(" ") }}
  >
    {cols.map((c, i) => (
      <div key={i} className="px-4 py-2.5">{c.label}</div>
    ))}
  </div>
);

const TableRow = ({ cols, onClick, children }) => (
  <div
    onClick={onClick}
    className="hidden sm:grid border-t border-[var(--color-border)] text-xs transition-colors duration-150 hover:bg-[var(--color-accent-soft)] cursor-pointer"
    style={{ gridTemplateColumns: cols.map(c => c.width).join(" ") }}
  >
    {children}
  </div>
);

// ── Mobile card row ───────────────────────────────────────────────────────────
const MobileCard = ({ onClick, children, className = "" }) => (
  <div
    onClick={onClick}
    className={`sm:hidden rounded-2xl border border-[var(--color-border)] bg-[var(--color-surface)] p-3 mb-2 cursor-pointer transition hover:bg-[var(--color-surface-strong)] ${className}`}
  >
    {children}
  </div>
);

const MobileRow = ({ label, value, color }) => (
  <div className="flex items-center justify-between gap-2 py-1">
    <span className="text-[10px] uppercase tracking-wide text-[var(--color-text-muted)]">{label}</span>
    <span className="text-xs font-medium text-right" style={color ? { color } : { color: "var(--color-text)" }}>{value}</span>
  </div>
);

// ── Component ────────────────────────────────────────────────────────────────
export const SmsCampaignPanel = () => {
  const navigate = useNavigate();
  const [activeCampaign, setActiveCampaign] = useState("first_sms");
  const [logs, setLogs] = useState([]);
  const [settings, setSettings] = useState(null);
  const [allSettings, setAllSettings] = useState({});
  const [campaignCounts, setCampaignCounts] = useState({});
  const [queueData, setQueueData] = useState([]);
  const [queueCounts, setQueueCounts] = useState({});
  const [lastSmsByCampaign, setLastSmsByCampaign] = useState({});
  const [runningNow, setRunningNow] = useState(null);
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState(null);
  const [filter, setFilter] = useState("all");
  const [savingSettings, setSavingSettings] = useState(false);
  const [settingsSaved, setSettingsSaved] = useState(false);
  const [checkingIds, setCheckingIds] = useState(new Set());
  const [autoRefresh, setAutoRefresh] = useState(true);
  const [togglingCampaign, setTogglingCampaign] = useState(null);
  const [viewMode, setViewMode] = useState("logs");
  const [showSettingsPanel, setShowSettingsPanel] = useState(false);
  const [dateFilter, setDateFilter] = useState("all"); // all|today|yesterday|7d
  const [linkFilter, setLinkFilter] = useState("all"); // all|opened|not_opened
  const [smsFilter, setSmsFilter] = useState("all"); // all|no_sms
  const [selectedIds, setSelectedIds] = useState(new Set());
  const [deleting, setDeleting] = useState(false);
  const [showSmsPreview, setShowSmsPreview] = useState(false);
  const [logPage, setLogPage] = useState(1);
  const [queuePage, setQueuePage] = useState(1);
  const PAGE_SIZE = 25;
  const [linkStats, setLinkStats] = useState({ opened: 0, total: 0, notOpened: 0 });
  const [openedGroupIds, setOpenedGroupIds] = useState(new Set());
  const refreshTimer = useRef(null);

  const handleOpenGroup = useCallback((groupId) => {
    if (groupId) navigate("/dashboard/group/" + groupId);
  }, [navigate]);

  // ── Load data ──────────────────────────────────────────────────────────────
  const loadData = useCallback(async () => {
    setError(null);
    try {
      const { data: allSettingsData, error: allErr } = await supabase
        .from("sms_campaign_settings")
        .select("*");
      if (allErr) throw allErr;
      const settingsMap = {};
      (allSettingsData || []).forEach(s => { settingsMap[s.campaign_type] = s; });
      setAllSettings(settingsMap);

      const { data: countsData, error: countsErr } = await supabase
        .from("sms_campaign_log")
        .select("campaign_type, status, was_test_mode");
      if (countsErr) throw countsErr;
      const counts = {};
      (countsData || []).forEach(r => {
        if (r.was_test_mode) return; // Skip test sends from KPI
        if (!counts[r.campaign_type]) counts[r.campaign_type] = { total: 0, delivered: 0, sent: 0, errors: 0 };
        counts[r.campaign_type].total++;
        if (r.status === "delivered") counts[r.campaign_type].delivered++;
        if (r.status === "sent" || r.status === "checking") counts[r.campaign_type].sent++;
        if (["send_failed", "error", "limit_exceeded"].includes(r.status)) counts[r.campaign_type].errors++;
      });
      setCampaignCounts(counts);

      // Load last SMS timestamp per campaign (non-test only)
      const { data: lastSmsData, error: lastSmsErr } = await supabase
        .from("sms_campaign_log")
        .select("campaign_type, created_at")
        .eq("was_test_mode", false)
        .order("created_at", { ascending: false })
        .limit(200);
      const lastSmsMap = {};
      if (!lastSmsErr && lastSmsData) {
        lastSmsData.forEach(r => {
          if (!lastSmsMap[r.campaign_type]) lastSmsMap[r.campaign_type] = r.created_at;
        });
      }
      setLastSmsByCampaign(lastSmsMap);

      const { data: queueCountsData, error: qErr } = await supabase
        .from("order_groups")
        .select("notification_status, delivery_status")
        .in("delivery_status", ["pending_confirmation", "paid_storage"]);
      if (qErr) throw qErr;
      const qCounts = { first_sms: 0, second_sms: 0, manual: 0, paid_storage: 0 };
      (queueCountsData || []).forEach(r => {
        if (r.delivery_status === "pending_confirmation") {
          if (r.notification_status === "link_ready" || r.notification_status === "not_started") qCounts.first_sms++;
          if (r.notification_status === "first_sms_sent") qCounts.second_sms++;
          if (r.notification_status === "second_sms_sent") qCounts.manual++;
        }
        if (r.delivery_status === "paid_storage" && r.notification_status !== "paid_storage_sent" && r.notification_status !== "paid_storage_sending") {
          qCounts.paid_storage++;
        }
      });
      setQueueCounts(qCounts);

      // Load link open statistics from delivery_invitations
      const { data: invStatsData, error: invErr } = await supabase
        .from("delivery_invitations")
        .select("order_group_id, opened_at, access_count")
        .not("order_group_id", "is", null);
      if (!invErr && invStatsData) {
        const opened = invStatsData.filter(i => i.opened_at).length;
        const total = invStatsData.length;
        setLinkStats({ opened, total, notOpened: total - opened });
      }

      // Load per-campaign link stats by joining with order_groups notification_status
      const { data: invWithGroups, error: invGroupErr } = await supabase
        .from("delivery_invitations")
        .select("order_group_id, opened_at, access_count")
        .not("order_group_id", "is", null)
        .not("opened_at", "is", null);
      if (!invGroupErr && invWithGroups) {
        const openedGroupIds = new Set(invWithGroups.map(i => i.order_group_id));
        setOpenedGroupIds(openedGroupIds);
      }

      // Load queue detail for active campaign (always load — needed for preview + queue view)
      if (viewMode === "queue" || showSmsPreview) {
        let qQuery = supabase
          .from("order_groups")
          .select("id, group_key, customer_name, customer_phone, notification_status, delivery_status, delivery_link, created_at, next_notification_check_at, sms_sent_at, first_sms_sent_at, second_sms_sent_at, sms_attempts, delivery_type, delivery_date, delivery_time, pickup_date, pickup_time_slot, delivery_address, customer_address, manual_confirmation_at")
          .order("created_at", { ascending: true })
          .limit(200);
        if (activeCampaign === "first_sms") {
          qQuery = qQuery.eq("delivery_status", "pending_confirmation").in("notification_status", ["link_ready", "not_started", "sms_sending", "send_failed"]);
        } else if (activeCampaign === "second_sms") {
          qQuery = qQuery.eq("delivery_status", "pending_confirmation").in("notification_status", ["first_sms_sent", "second_sms_sending"]);
        } else if (activeCampaign === "manual") {
          // Manual: groups in manual_confirmation_required OR pending with second_sms_sent/manual_required
          qQuery = qQuery.in("delivery_status", ["pending_confirmation", "manual_confirmation_required"]).in("notification_status", ["second_sms_sent", "manual_required"]);
        } else if (activeCampaign === "paid_storage") {
          qQuery = qQuery.eq("delivery_status", "paid_storage").in("notification_status", ["not_started", "paid_storage_sending", "link_ready", "first_sms_sent", "second_sms_sent"]);
        }
        const { data: qData, error: qErr2 } = await qQuery;
        if (qErr2) throw qErr2;

        // Fetch delivery invitations for these groups — to show link open tracking
        let invitationsMap = {};
        if (qData && qData.length > 0) {
          const groupIds = qData.map(g => g.id);
          const { data: invData, error: invErr } = await supabase
            .from("delivery_invitations")
            .select("order_group_id, opened_at, access_count, last_accessed_at, confirmed_at, state")
            .in("order_group_id", groupIds);
          if (!invErr && invData) {
            invData.forEach(inv => {
              invitationsMap[inv.order_group_id] = inv;
            });
          }
        }
        // Merge invitation data into queue items
        const qDataWithInv = (qData || []).map(g => ({
          ...g,
          _invitation: invitationsMap[g.id] || null,
        }));
        setQueueData(qDataWithInv);
      }

      let query = supabase
        .from("sms_campaign_log")
        .select("*")
        .eq("campaign_type", activeCampaign)
        .order("created_at", { ascending: false })
        .limit(200);
      if (filter !== "all") query = query.eq("status", filter);
      const { data: logData, error: logError } = await query;
      if (logError) throw logError;

      // Fetch order_groups for these logs to show agreement status
      let groupsMap = {};
      if (logData && logData.length > 0) {
        const groupIds = [...new Set(logData.map(l => l.order_group_id).filter(Boolean))];
        if (groupIds.length > 0) {
          const { data: groupsData, error: groupsErr } = await supabase
            .from("order_groups")
            .select("id, delivery_status, delivery_type, delivery_date, delivery_time, pickup_date, pickup_time_slot, delivery_address, customer_address")
            .in("id", groupIds);
          if (!groupsErr && groupsData) {
            groupsData.forEach(og => { groupsMap[og.id] = og; });
          }
        }
      }
      // Merge group data into log entries
      const logsWithGroups = (logData || []).map(l => ({
        ...l,
        _group: groupsMap[l.order_group_id] || null,
      }));
      setLogs(logsWithGroups);

      if (HAS_SETTINGS.includes(activeCampaign)) {
        setSettings(settingsMap[activeCampaign] || null);
      } else {
        setSettings(null);
      }
    } catch (e) {
      setError(e.message || String(e));
    } finally {
      setIsLoading(false);
    }
  }, [activeCampaign, filter, viewMode, showSmsPreview]);

  useEffect(() => { setIsLoading(true); loadData(); }, [loadData]);
  useEffect(() => {
    if (autoRefresh) {
      refreshTimer.current = setInterval(() => loadData(), 15000);
      return () => clearInterval(refreshTimer.current);
    }
  }, [autoRefresh, loadData]);

  // ── Save settings ──────────────────────────────────────────────────────────
  const handleSaveSettings = async () => {
    if (!settings) return;
    setSavingSettings(true);
    try {
      const { id, ...updates } = settings;
      updates.updated_at = new Date().toISOString();
      const { error: updateError } = await supabase
        .from("sms_campaign_settings")
        .update(updates)
        .eq("id", id);
      if (updateError) throw updateError;
      setSettingsSaved(true);
      setTimeout(() => setSettingsSaved(false), 3000);
    } catch (e) {
      setError(`Ошибка сохранения: ${e.message}`);
    } finally {
      setSavingSettings(false);
    }
  };

  const updateSetting = (key, value) => {
    setSettings(prev => prev ? { ...prev, [key]: value } : prev);
  };

  const toggleCampaignEnabled = async (campaignKey) => {
    const s = allSettings[campaignKey];
    if (!s) return;
    setTogglingCampaign(campaignKey);
    const newVal = !(s.enabled ?? true);
    try {
      const { error: updateError } = await supabase
        .from("sms_campaign_settings")
        .update({ enabled: newVal, updated_at: new Date().toISOString() })
        .eq("id", s.id);
      if (updateError) throw updateError;
      setAllSettings(prev => ({ ...prev, [campaignKey]: { ...prev[campaignKey], enabled: newVal } }));
      if (campaignKey === activeCampaign) setSettings(prev => prev ? { ...prev, enabled: newVal } : prev);
    } catch (e) { setError(`Ошибка: ${e.message}`); }
    finally { setTogglingCampaign(null); }
  };

  const toggleWorkDay = (dayNum) => {
    const current = parseWorkDays(settings?.work_days);
    if (current.has(dayNum)) current.delete(dayNum); else current.add(dayNum);
    updateSetting("work_days", serializeWorkDays(current));
  };

  const [togglingTimer, setTogglingTimer] = useState(null);
  const toggleTimer = async (campaignKey) => {
    const s = allSettings[campaignKey];
    if (!s) return;
    setTogglingTimer(campaignKey);
    const newVal = !(s.timer_active ?? false);
    try {
      const { error: updateError } = await supabase
        .from("sms_campaign_settings")
        .update({ timer_active: newVal, updated_at: new Date().toISOString() })
        .eq("id", s.id);
      if (updateError) throw updateError;
      setAllSettings(prev => ({ ...prev, [campaignKey]: { ...prev[campaignKey], timer_active: newVal } }));
      if (campaignKey === activeCampaign) setSettings(prev => prev ? { ...prev, timer_active: newVal } : prev);
    } catch (e) { setError(`Ошибка: ${e.message}`); }
    finally { setTogglingTimer(null); }
  };

  const [testSending, setTestSending] = useState(null);
  const handleTestSend = async (campaignKey) => {
    const s = allSettings[campaignKey];
    if (!s) return;
    if (!s.test_mode) { setError("Тестовая отправка доступна только в тестовом режиме"); return; }
    setTestSending(campaignKey);
    try {
      const { error: updateError } = await supabase
        .from("sms_campaign_settings")
        .update({ test_send_requested: true, updated_at: new Date().toISOString() })
        .eq("id", s.id);
      if (updateError) throw updateError;
      setTimeout(() => loadData(), 5000);
    } catch (e) { setError(`Ошибка: ${e.message}`); }
    finally { setTimeout(() => setTestSending(null), 3000); }
  };

  const [restarting, setRestarting] = useState(null);
  const handleRestart = async (campaignKey) => {
    // Reset all groups in this campaign to beginning
    const s = allSettings[campaignKey];
    if (!s) return;
    setRestarting(campaignKey);
    try {
      const { error: updateError } = await supabase
        .from("sms_campaign_settings")
        .update({ run_requested: true, updated_at: new Date().toISOString() })
        .eq("id", s.id);
      if (updateError) throw updateError;
      setTimeout(() => loadData(), 5000);
      setTimeout(() => loadData(), 15000);
    } catch (e) { setError(`Ошибка: ${e.message}`); }
    finally { setTimeout(() => setRestarting(null), 3000); }
  };

  const handleRunNow = async (campaignKey) => {
    // Trigger immediate run via run_requested flag
    const s = allSettings[campaignKey];
    if (!s) return;
    setRunningNow(campaignKey);
    try {
      const { error: updateError } = await supabase
        .from("sms_campaign_settings")
        .update({ run_requested: true, updated_at: new Date().toISOString() })
        .eq("id", s.id);
      if (updateError) throw updateError;
      // Quick refresh to show updated status
      setTimeout(() => loadData(), 3000);
      setTimeout(() => loadData(), 10000);
    } catch (e) { setError(`Ошибка: ${e.message}`); }
    finally { setTimeout(() => setRunningNow(null), 3000); }
  };

  const handleRecheck = async (logId) => {
    setCheckingIds(prev => new Set([...prev, logId]));
    try {
      const resp = await fetch(`${supabaseUrl}/functions/v1/check-sms-status`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          "Authorization": `Bearer ${supabaseAnonKey}`,
          "apikey": supabaseAnonKey,
        },
        body: JSON.stringify({ log_id: logId }),
      });
      const data = await resp.json();
      if (!resp.ok || data.error) throw new Error(data.error || `HTTP ${resp.status}`);

      setLogs(prev => prev.map(l => {
        if (l.id !== logId) return l;
        return {
          ...l,
          status: data.status,
          sms_code: data.code,
          needs_check: false,
          checked_at: new Date().toISOString(),
          updated_at: new Date().toISOString(),
        };
      }));
    } catch (e) { setError(`Ошибка проверки: ${e.message}`); }
    finally { setTimeout(() => setCheckingIds(prev => { const n = new Set(prev); n.delete(logId); return n; }), 1000); }
  };

  const handleDeleteLogs = async (ids) => {
    if (!ids || ids.length === 0) return;
    setDeleting(true);
    try {
      const { error } = await supabase
        .from("sms_campaign_log")
        .delete()
        .in("id", ids);
      if (error) throw error;
      setLogs(prev => prev.filter(l => !ids.includes(l.id)));
      setSelectedIds(new Set());
    } catch (e) {
      setError(`Ошибка удаления: ${e.message}`);
    } finally {
      setDeleting(false);
    }
  };

  const handleDeleteTestLogs = async () => {
    const testIds = logs.filter(l => l.was_test_mode === true).map(l => l.id);
    if (testIds.length === 0) return;
    if (!window.confirm(`Удалить ${testIds.length} тестовых записей?`)) return;
    await handleDeleteLogs(testIds);
  };

  const handleDeleteSelected = async () => {
    const ids = [...selectedIds];
    if (ids.length === 0) return;
    if (!window.confirm(`Удалить ${ids.length} записей?`)) return;
    await handleDeleteLogs(ids);
  };

  const toggleSelect = (id) => {
    setSelectedIds(prev => {
      const next = new Set(prev);
      if (next.has(id)) next.delete(id); else next.add(id);
      return next;
    });
  };

  const toggleSelectAll = () => {
    const filtered = logs.filter((entry) => {
      const isOpened = openedGroupIds.has(entry.order_group_id);
      return passesDateFilter(entry.created_at, dateFilter) && passesLinkFilter(isOpened, linkFilter);
    });
    const allIds = filtered.map(l => l.id);
    const allSelected = allIds.length > 0 && allIds.every(id => selectedIds.has(id));
    setSelectedIds(allSelected ? new Set() : new Set(allIds));
  };

  const stats = logs.reduce((acc, log) => { acc[log.status] = (acc[log.status] || 0) + 1; return acc; }, {});

  const showSettings = HAS_SETTINGS.includes(activeCampaign);
  const isManualCampaign = activeCampaign === MANUAL_CAMPAIGN;
  const isPaidStorageCampaign = activeCampaign === "paid_storage";
  const showSmsFields = !isManualCampaign;

  const getRunnerStatus = (s, lastSmsAt, queueCnt, timerActive) => {
    if (!s) return { label: "—", color: "var(--color-text-muted)" };
    if (!s.enabled) return { label: "⏸ Остановлена", color: "var(--color-text-muted)" };
    // If timer active and queue is 0 — all good, nothing to send
    if (timerActive && queueCnt === 0) {
      const lastRunDiff = s.last_run_at ? Math.floor((Date.now() - new Date(s.last_run_at).getTime()) / 60000) : null;
      if (lastRunDiff !== null && lastRunDiff < 10)
        return { label: `✅ Очереди нет (тик ${lastRunDiff}м)`, color: "#22c55e" };
      if (lastRunDiff !== null && lastRunDiff < 60)
        return { label: `✅ Очереди нет (тик ${lastRunDiff}м назад)`, color: "#22c55e" };
      return { label: "✅ Очереди нет", color: "#22c55e" };
    }
    // If timer active and queue > 0 — waiting for delay, show calm status
    if (timerActive && queueCnt > 0) {
      if (lastSmsAt) {
        const diffMin = Math.floor((Date.now() - new Date(lastSmsAt).getTime()) / 60000);
        if (diffMin < 10) return { label: `🟢 SMS ${diffMin}м назад · ${queueCnt} в очереди`, color: "#22c55e" };
        if (diffMin < 60) return { label: `⏳ SMS ${diffMin}м назад · ${queueCnt} в очереди`, color: "var(--color-warning)" };
        if (diffMin < 1440) return { label: `⏳ SMS ${Math.floor(diffMin/60)}ч назад · ${queueCnt} ждут`, color: "var(--color-warning)" };
        return { label: `⏳ ${queueCnt} в очереди · SMS ${Math.floor(diffMin/1440)}д назад`, color: "var(--color-warning)" };
      }
      // No SMS in log but queue has items — waiting to send
      const lastRunDiff = s.last_run_at ? Math.floor((Date.now() - new Date(s.last_run_at).getTime()) / 60000) : null;
      if (lastRunDiff !== null && lastRunDiff < 10)
        return { label: `🟢 Запуск ${lastRunDiff}м назад · ${queueCnt} ждут`, color: "#22c55e" };
      return { label: `⏳ ${queueCnt} в очереди · ждём задержку`, color: "var(--color-warning)" };
    }
    // Timer NOT active — show warning
    if (!timerActive) return { label: "⏸ Таймер выключен", color: "var(--color-danger)" };
    // Fallback
    if (lastSmsAt) {
      const diffMin = Math.floor((Date.now() - new Date(lastSmsAt).getTime()) / 60000);
      if (diffMin < 10) return { label: `🟢 SMS ${diffMin}м назад`, color: "#22c55e" };
      if (diffMin < 60) return { label: `🟡 SMS ${diffMin}м назад`, color: "var(--color-warning)" };
      return { label: `🔴 SMS ${Math.floor(diffMin/1440)}д назад`, color: "var(--color-danger)" };
    }
    return { label: "⚠️ Не запускалась", color: "var(--color-danger)" };
  };

  const balance = allSettings.first_sms?.last_balance
    ?? allSettings.second_sms?.last_balance
    ?? allSettings.paid_storage?.last_balance ?? null;

  if (isLoading && logs.length === 0 && viewMode === "logs") {
    return (
      <Panel className="p-6">
        <div className="flex items-center gap-3 animate-pulse">
          <span className="text-lg">⏳</span>
          <span className="text-sm text-[var(--color-text-muted)]">Загрузка данных кампании…</span>
        </div>
      </Panel>
    );
  }

  // Queue table column definitions
  const queueCols = [
    { label: "Клиент", width: "minmax(130px,1.5fr)" },
    { label: "Телефон", width: "minmax(120px,1fr)" },
    { label: "Статус SMS", width: "minmax(110px,1fr)" },
    { label: "Согласование", width: "minmax(150px,1.3fr)" },
    { label: "Добавлен", width: "minmax(110px,0.9fr)" },
    { label: activeCampaign === "manual" ? "⏰ Перевод в ручное" : "Следующая отправка", width: "minmax(120px,1fr)" },
    { label: "Следующая проверка", width: "minmax(110px,0.9fr)" },
    { label: "Ссылка открыта", width: "minmax(100px,0.9fr)" },
  ];

  // Log table column definitions
  const logCols = [
    { label: "☐", width: "minmax(40px,0.3fr)" },
    { label: "Клиент", width: "minmax(140px,1.5fr)" },
    { label: "Статус SMS", width: "minmax(130px,1.2fr)" },
    { label: "Согласование", width: "minmax(150px,1.3fr)" },
    { label: "Отправлено", width: "minmax(100px,0.9fr)" },
    { label: "Проверено", width: "minmax(120px,1fr)" },
    { label: "Ссылка", width: "minmax(90px,0.8fr)" },
    { label: "Действие", width: "minmax(100px,0.8fr)" },
  ];

  return (
    <div className="space-y-4">
      {/* ── Balance banner ─────────────────────────────────────────────────── */}
      <Panel className="p-5">
        <div className="grid grid-cols-1 gap-4 sm:grid-cols-3">
          <div className="flex items-center gap-3">
            <div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-[var(--color-accent-soft)] text-xl shrink-0">
              💰
            </div>
            <div>
              <div className="text-xs uppercase tracking-wide text-[var(--color-text-muted)]">Баланс sms.ru</div>
              <div className="text-xl font-bold text-[var(--color-text)]">
                {balance != null ? `${Number(balance).toLocaleString("ru-RU")} ₽` : "—"}
              </div>
            </div>
          </div>
          <div className="flex items-center gap-3">
            <div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-[var(--color-accent-soft)] text-xl shrink-0">
              📊
            </div>
            <div>
              <div className="text-xs uppercase tracking-wide text-[var(--color-text-muted)]">SMS отправлено</div>
              <div className="text-xl font-bold text-[var(--color-text)]">
                {Object.values(campaignCounts).reduce((a, c) => a + (c?.total || 0), 0)}
              </div>
            </div>
          </div>
          {linkStats.total > 0 ? (
            <div className="flex items-center gap-3">
              <div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-[var(--color-accent-soft)] text-xl shrink-0">
                👁
              </div>
              <div>
                <div className="text-xs uppercase tracking-wide text-[var(--color-text-muted)]">Открыли ссылку</div>
                <div className="text-xl font-bold text-[var(--color-text)]">
                  {linkStats.opened} / {linkStats.total}
                </div>
              </div>
            </div>
          ) : null}
        </div>
      </Panel>

      {/* ── Campaign cards ────────────────────────────────────────────────── */}
      <div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4">
        {CAMPAIGNS.map(c => {
          const isActive = activeCampaign === c.key;
          const s = allSettings[c.key];
          const isEnabled = s?.enabled ?? true;
          const isTest = s?.test_mode ?? true;
          const timerActive = s?.timer_active ?? false;
          const cnt = campaignCounts[c.key] || { total: 0, delivered: 0, sent: 0, errors: 0 };
          const queueCnt = queueCounts[c.key] || 0;
          const runner = getRunnerStatus(s, lastSmsByCampaign[c.key], queueCnt, timerActive);
          const isToggling = togglingCampaign === c.key;
          const isTimerToggling = togglingTimer === c.key;
          const isTestSending = testSending === c.key;
          const isManual = c.key === MANUAL_CAMPAIGN;
          return (
            <div
              key={c.key}
              className={`rounded-2xl border p-4 transition-all duration-200 cursor-pointer ${
                isActive
                  ? "border-[var(--color-accent)] bg-[var(--color-accent-soft)] shadow-sm"
                  : "border-[var(--color-border)] bg-[var(--color-surface)] hover:bg-[var(--color-surface-strong)] hover:shadow-sm hover:border-[var(--color-text-muted)]"
              }`}
              onClick={() => { setActiveCampaign(c.key); setFilter("all"); setViewMode(c.key === "manual" ? "queue" : "logs"); }}
            >
              {/* Header */}
              <div className="flex items-center justify-between">
                <div className="flex items-center gap-2.5">
                  <span className="text-2xl">{c.icon}</span>
                  <div>
                    <div className="text-sm font-semibold text-[var(--color-text)]">{c.label}</div>
                    <div className="text-xs text-[var(--color-text-muted)] mt-0.5">{c.desc}</div>
                  </div>
                </div>
                <ToggleSwitch
                  on={isEnabled}
                  onClick={() => toggleCampaignEnabled(c.key)}
                  disabled={isToggling}
                  size="md"
                />
              </div>

              {/* Status row */}
              <div className="mt-3 flex flex-wrap items-center gap-2">
                <span
                  className="rounded-full border px-2.5 py-1 text-xs font-semibold transition"
                  style={{
                    borderColor: !isEnabled ? "var(--color-border)" : isManual ? "var(--color-accent)" : isTest ? "var(--color-warning)" : "#22c55e",
                    color: !isEnabled ? "var(--color-text-muted)" : isManual ? "var(--color-accent)" : isTest ? "var(--color-warning)" : "#22c55e",
                  }}
                >
                  {!isEnabled ? "⏸ Выкл" : isManual ? "🔧 Ручное" : isTest ? "🧪 Тест" : "🚀 Боевой"}
                </span>
                <span className="text-xs font-medium" style={{ color: runner.color }}>{runner.label}</span>
              </div>

              {/* Timer status bar */}
              <div className={`mt-2.5 flex items-center gap-2 rounded-xl px-3 py-2 text-xs ${
                timerActive
                  ? queueCnt > 0
                    ? "bg-[rgba(34,197,94,0.06)]"
                    : "bg-[var(--color-surface-strong)]"
                  : "bg-[rgba(239,68,68,0.06)]"
              }`}>
                {timerActive ? (
                  <>
                    <span className={`h-2 w-2 rounded-full ${queueCnt > 0 ? "bg-[#22c55e] animate-pulse" : "bg-[#22c55e]"} shrink-0`} />
                    <span className="text-[var(--color-text)]">
                      Таймер активен{queueCnt > 0 ? ` · ${queueCnt} в очереди` : " · очереди нет"}
                    </span>
                  </>
                ) : (
                  <>
                    <span className="h-2 w-2 rounded-full bg-[var(--color-danger)] shrink-0" />
                    <span className="text-[var(--color-danger)]">Таймер выключен — SMS не отправляются</span>
                  </>
                )}
              </div>

              {/* Action buttons — uniform size, full width row */}
              <div className="mt-3 flex flex-wrap items-center gap-2" onClick={(e) => e.stopPropagation()}>
                <button
                  type="button"
                  onClick={() => toggleTimer(c.key)}
                  disabled={isTimerToggling}
                  className={`flex items-center gap-2 rounded-xl px-4 py-2 text-xs font-medium transition-all duration-200 ${
                    timerActive
                      ? "bg-[rgba(34,197,94,0.12)] text-[#22c55e] border border-[rgba(34,197,94,0.2)]"
                      : "bg-[var(--color-surface-strong)] text-[var(--color-text-muted)] border border-[var(--color-border)]"
                  }`}
                >
                  <MiniToggle on={timerActive} onClick={() => {}} disabled={isTimerToggling} />
                  Таймер
                </button>
                {!isManual && isTest && (
                  <CardButton
                    onClick={() => handleTestSend(c.key)}
                    disabled={isTestSending}
                    tone="warning"
                  >
                    {isTestSending ? "…" : "🧪 Тест"}
                  </CardButton>
                )}
                <CardButton
                  onClick={() => handleRunNow(c.key)}
                  disabled={runningNow === c.key}
                  tone="accent"
                >
                  {runningNow === c.key ? "…" : "▶ Запустить"}
                </CardButton>
                <CardButton
                  onClick={() => handleRestart(c.key)}
                  disabled={restarting === c.key}
                  tone="danger"
                >
                  {restarting === c.key ? "…" : "🔄 Сброс"}
                </CardButton>
              </div>

              {/* Stats grid — 2×2 KPI tiles (real only, test excluded) */}
              <div className="mt-3 grid grid-cols-2 gap-1.5">
                <KpiTile icon="⏳" value={queueCnt} label="В очереди" color="var(--color-warning)" />
                <KpiTile icon="📤" value={cnt.sent + cnt.delivered} label="Отправлено" color="var(--color-text)" />
                <KpiTile icon="✅" value={cnt.delivered} label="Доставлено" color="#22c55e" />
                <KpiTile
                  icon="📊"
                  value={cnt.total > 0 ? `${Math.round((cnt.delivered / cnt.total) * 100)}%` : "—"}
                  label="% доставки"
                  color={cnt.total > 0 && cnt.delivered / cnt.total >= 0.8 ? "#22c55e" : cnt.total > 0 && cnt.delivered / cnt.total >= 0.5 ? "var(--color-warning)" : "var(--color-danger)"}
                />
              </div>
            </div>
          );
        })}
      </div>

      {error && (
        <Panel className="p-4">
          <div className="flex items-center justify-between">
            <div className="text-xs text-[var(--color-danger)]">⚠ {error}</div>
            <button
              onClick={() => { setError(null); loadData(); }}
              className="rounded-full px-3 py-1 text-xs font-medium text-[var(--color-accent)] hover:bg-[var(--color-accent-soft)] transition"
            >
              Повторить
            </button>
          </div>
        </Panel>
      )}

      {/* ── View mode toggle ──────────────────────────────────────────────── */}
      <div className="flex gap-2">
        <FilterButton active={viewMode === "logs"} onClick={() => setViewMode("logs")}>📋 Журнал отправок</FilterButton>
        <FilterButton active={viewMode === "queue"} onClick={() => setViewMode("queue")}>⏳ Очередь отправки</FilterButton>
      </div>


      {/* ── Queue view ────────────────────────────────────────────────────── */}
      {viewMode === "queue" && (
        <>
        <Panel className="p-0">
          {/* Date + link filters */}
          <div className="flex flex-wrap items-center gap-2">
            <span className="text-[10px] uppercase tracking-wide text-[var(--color-text-muted)]">📅 Добавлен:</span>
            {[
              { v: "all", l: "Все" },
              { v: "today", l: "Сегодня" },
              { v: "yesterday", l: "Вчера" },
              { v: "7d", l: "7 дней" },
            ].map(opt => (
              <FilterButton key={opt.v} active={dateFilter === opt.v} onClick={() => setDateFilter(opt.v)}>{opt.l}</FilterButton>
            ))}
            <span className="ml-3 text-[10px] uppercase tracking-wide text-[var(--color-text-muted)]">🔗 Ссылка:</span>
            {[
              { v: "all", l: "Все" },
              { v: "opened", l: "Открыта" },
              { v: "not_opened", l: "Не открыта" },
            ].map(opt => (
              <FilterButton key={opt.v} active={linkFilter === opt.v} onClick={() => setLinkFilter(opt.v)}>{opt.l}</FilterButton>
            ))}
            <span className="ml-3 text-[10px] uppercase tracking-wide text-[var(--color-text-muted)]">📨 SMS:</span>
            {[
              { v: "all", l: "Все" },
              { v: "no_sms", l: "Без SMS" },
            ].map(opt => (
              <FilterButton key={opt.v} active={smsFilter === opt.v} onClick={() => setSmsFilter(opt.v)}>{opt.l}</FilterButton>
            ))}
          </div>

          {/* Desktop table */}
          <div className="hidden sm:block overflow-x-auto">
            <div className="min-w-[1100px]">
              <TableHeader cols={queueCols} />
              {queueData.length === 0 ? (
                <div className="px-4 py-8 text-center text-xs text-[var(--color-text-muted)]">
                  Очередь пуста — нет групп, ожидающих отправки SMS
                </div>
              ) : (
                queueData.filter((g) => {
                  const inv = g._invitation;
                  const opened = inv && inv.opened_at;
                  return passesDateFilter(g.created_at, dateFilter) && passesLinkFilter(opened, linkFilter) && passesSmsFilter(g, smsFilter);
                }).slice((queuePage - 1) * PAGE_SIZE, queuePage * PAGE_SIZE).map((g) => {
                  const nextSend = calcNextSendTime(g, settings, activeCampaign);
                  const inv = g._invitation;
                  const opened = inv && inv.opened_at;
                  const accessCount = inv?.access_count || 0;
                  const manualTransfer = activeCampaign === "manual" && g.second_sms_sent_at
                    ? new Date(new Date(g.second_sms_sent_at).getTime() + (settings?.auto_manual_after_hours ?? 3) * 3600000)
                    : null;
                  const isOverdue = manualTransfer && manualTransfer.getTime() < Date.now();
                  return (
                    <TableRow
                      key={g.id}
                      cols={queueCols}
                      onClick={() => handleOpenGroup(g.id)}
                    >
                      <div className="px-4 py-2.5 text-[var(--color-text)] font-medium">
                        {g.customer_name || g.group_key || "—"}
                      </div>
                      <div className="px-4 py-2.5 text-[var(--color-text-muted)]">{fmtPhone(g.customer_phone)}</div>
                      <div className="px-4 py-2.5">
                        <Badge tone={NOTIF_TONES[g.notification_status] || "neutral"}>
                          {NOTIF_LABELS[g.notification_status] || g.notification_status || "—"}
                        </Badge>
                        {g.sms_attempts > 0 && <div className="mt-0.5 text-[10px] text-[var(--color-text-muted)]">попыток: {g.sms_attempts}</div>}
                      </div>
                      <div className="px-4 py-2.5">
                        {(() => { const a = fmtAgreement(g); return (
                          <span className="text-xs font-medium" style={{ color: AGREEMENT_TONES[a.tone] }}>{a.text}</span>
                        ); })()}
                      </div>
                      <div className="px-4 py-2.5 text-[var(--color-text-muted)]">
                        <div className="text-[10px] uppercase tracking-wide mb-0.5">в базу</div>
                        <div className="text-[var(--color-text)] text-xs">{fmtTimeShort(g.created_at)}</div>
                        <div className="text-[10px]">{fmtElapsed(g.created_at)}</div>
                        {g.sms_sent_at && (
                          <>
                            <div className="text-[10px] uppercase tracking-wide mt-1.5 mb-0.5">SMS старт</div>
                            <div className="text-[var(--color-accent)] text-xs">{fmtTimeShort(g.sms_sent_at)}</div>
                            <div className="text-[10px]">{fmtElapsed(g.sms_sent_at)}</div>
                          </>
                        )}
                      </div>
                      <div className="px-4 py-2.5">
                        {activeCampaign === "manual" ? (
                          manualTransfer ? (
                            <div>
                              <div className="text-[var(--color-text-muted)] text-[10px] mb-0.5">2-я SMS: {fmtTimeShort(g.second_sms_sent_at)}</div>
                              <div className="font-medium" style={{ color: isOverdue ? "var(--color-danger)" : "var(--color-warning)" }}>
                                {fmtTimeShort(manualTransfer.toISOString())}
                              </div>
                              <div className="text-[10px]" style={{ color: isOverdue ? "var(--color-danger)" : "var(--color-text-muted)" }}>
                                {isOverdue ? "⏰ просрочено!" : fmtCountdown(manualTransfer.toISOString())}
                              </div>
                              {g.manual_confirmation_at && (
                                <div className="mt-1 text-[10px] text-[var(--color-accent)]">
                                  🔧 В ручном: {fmtTimeShort(g.manual_confirmation_at)}
                                </div>
                              )}
                            </div>
                          ) : g.manual_confirmation_at ? (
                            <div>
                              <div className="text-[var(--color-accent)] font-medium text-xs">🔧 В ручном</div>
                              <div className="text-[10px] text-[var(--color-text-muted)]">{fmtTimeShort(g.manual_confirmation_at)}</div>
                              <div className="text-[10px] text-[var(--color-text-muted)]">{fmtElapsed(g.manual_confirmation_at)}</div>
                            </div>
                          ) : (
                            <span className="text-[var(--color-text-muted)]">—</span>
                          )
                        ) : nextSend ? (
                          <div>
                            <div className="text-[var(--color-accent)] font-medium">{fmtTimeShort(nextSend)}</div>
                            <div className="text-[10px] text-[var(--color-text-muted)]">{fmtCountdown(nextSend)}</div>
                          </div>
                        ) : (
                          <span className="text-[var(--color-text-muted)]">—</span>
                        )}
                      </div>
                      <div className="px-4 py-2.5 text-[var(--color-text-muted)]">
                        {fmtTimeShort(g.next_notification_check_at)}
                        {g.next_notification_check_at && <div className="text-[10px]">{fmtCountdown(g.next_notification_check_at)}</div>}
                      </div>
                      <div className="px-4 py-2.5">
                        {opened ? (
                          <div>
                            <span className="font-medium" style={{ color: "#22c55e" }}>✅ Открыта</span>
                            {inv.last_accessed_at && (
                              <div className="text-[10px] text-[var(--color-text-muted)]">
                                {accessCount > 0 ? `${accessCount}× ` : ""}{fmtElapsed(inv.last_accessed_at)}
                              </div>
                            )}
                          </div>
                        ) : inv ? (
                          <span className="text-[var(--color-text-muted)]">— не открыта</span>
                        ) : (
                          <span className="text-[var(--color-text-muted)]">— нет ссылки</span>
                        )}
                      </div>
                    </TableRow>
                  );
                })
              )}
            </div>
          </div>

          {/* Mobile cards */}
          <div className="sm:hidden p-2">
            {queueData.length === 0 ? (
              <div className="px-4 py-8 text-center text-xs text-[var(--color-text-muted)]">
                Очередь пуста — нет групп, ожидающих отправки SMS
              </div>
            ) : (
              queueData.filter((g) => {
                const inv = g._invitation;
                const opened = inv && inv.opened_at;
                return passesDateFilter(g.created_at, dateFilter) && passesLinkFilter(opened, linkFilter) && passesSmsFilter(g, smsFilter);
              }).map((g) => {
                const nextSend = calcNextSendTime(g, settings, activeCampaign);
                const inv = g._invitation;
                const opened = inv && inv.opened_at;
                const accessCount = inv?.access_count || 0;
                const manualTransfer = activeCampaign === "manual" && g.second_sms_sent_at
                  ? new Date(new Date(g.second_sms_sent_at).getTime() + (settings?.auto_manual_after_hours ?? 3) * 3600000)
                  : null;
                const isOverdue = manualTransfer && manualTransfer.getTime() < Date.now();
                return (
                  <MobileCard key={g.id} onClick={() => handleOpenGroup(g.id)}>
                    {/* Header: name + phone */}
                    <div className="flex items-center justify-between mb-2">
                      <div className="text-sm font-semibold text-[var(--color-text)] truncate">
                        {g.customer_name || g.group_key || "—"}
                      </div>
                      <Badge tone={NOTIF_TONES[g.notification_status] || "neutral"}>
                        {NOTIF_LABELS[g.notification_status] || g.notification_status || "—"}
                      </Badge>
                    </div>
                    <div className="text-xs text-[var(--color-text-muted)] mb-2">{fmtPhone(g.customer_phone)}</div>
                    {/* Info rows */}
                    <div className="border-t border-[var(--color-border)] pt-2">
                      {(() => { const a = fmtAgreement(g); return (
                        <MobileRow label="Согласование" value={a.text} color={AGREEMENT_TONES[a.tone]} />
                      ); })()}
                      <MobileRow
                        label="Добавлен"
                        value={fmtTimeShort(g.created_at)}
                      />
                      {g.sms_sent_at && (
                        <MobileRow
                          label="SMS старт"
                          value={fmtTimeShort(g.sms_sent_at)}
                          color="var(--color-accent)"
                        />
                      )}
                      {activeCampaign === "manual" && manualTransfer ? (
                        <MobileRow
                          label="⏰ Перевод в ручное"
                          value={`${fmtTimeShort(manualTransfer.toISOString())} ${isOverdue ? "⚠ просрочено!" : fmtCountdown(manualTransfer.toISOString())}`}
                          color={isOverdue ? "var(--color-danger)" : "var(--color-warning)"}
                        />
                      ) : nextSend ? (
                        <MobileRow
                          label="Отправка"
                          value={`${fmtTimeShort(nextSend)} (${fmtCountdown(nextSend)})`}
                          color="var(--color-accent)"
                        />
                      ) : null}
                      {g.next_notification_check_at && (
                        <MobileRow
                          label="Проверка"
                          value={`${fmtTimeShort(g.next_notification_check_at)} (${fmtCountdown(g.next_notification_check_at)})`}
                        />
                      )}
                      {g.sms_attempts > 0 && (
                        <MobileRow label="Попыток" value={String(g.sms_attempts)} />
                      )}
                      <MobileRow
                        label="Ссылка"
                        value={opened ? `✅ Открыта${accessCount > 0 ? ` ${accessCount}×` : ""}${inv?.last_accessed_at ? ` ${fmtElapsed(inv.last_accessed_at)}` : ""}` : inv ? "— не открыта" : "— нет ссылки"}
                        color={opened ? "#22c55e" : "var(--color-text-muted)"}
                      />
                    </div>
                  </MobileCard>
                );
              })
            )}
          </div>
        </Panel>
        {(() => {
          const filteredQueue = queueData.filter((g) => {
            const inv = g._invitation;
            const opened = inv && inv.opened_at;
            return passesDateFilter(g.created_at, dateFilter) && passesLinkFilter(opened, linkFilter) && passesSmsFilter(g, smsFilter);
          });
          const totalPages = Math.ceil(filteredQueue.length / PAGE_SIZE);
          if (totalPages <= 1) return null;
          return <Pagination page={queuePage} totalPages={totalPages} onChange={setQueuePage} itemsPerPage={PAGE_SIZE} totalItems={filteredQueue.length} />;
        })()}
        </>
      )}

      {/* ── Logs view ─────────────────────────────────────────────────────── */}
      {viewMode === "logs" && (
        <>
          <SmsCampaignStats campaignType={activeCampaign} />

          {showSettings && settings && (
            <div className="space-y-3">
              <button
                type="button"
                onClick={() => setShowSettingsPanel(!showSettingsPanel)}
                className="flex w-full items-center justify-between rounded-2xl border border-[var(--color-border)] bg-[var(--color-surface)] px-5 py-4 text-left transition hover:bg-[var(--color-surface-strong)]"
              >
                <div className="flex items-center gap-2">
                  <span className="text-sm font-semibold text-[var(--color-text)]">
                    {CAMPAIGNS.find(c => c.key === activeCampaign)?.icon} Настройки кампании
                  </span>
                  {settings.test_mode
                    ? <span className="text-[10px] rounded-full bg-[var(--color-warning-soft)] px-2 py-0.5 font-medium text-[var(--color-warning)]">ТЕСТ</span>
                    : <span className="text-[10px] rounded-full bg-[var(--color-accent-soft)] px-2 py-0.5 font-medium text-[var(--color-accent)]">БОЙ</span>
                  }
                </div>
                <div className="flex items-center gap-3">
                  {settingsSaved && showSettingsPanel && (
                    <span className="text-[10px] text-[var(--color-accent)]">✓ Сохранено</span>
                  )}
                  <svg
                    className="h-4 w-4 text-[var(--color-text-muted)] transition-transform"
                    style={{ transform: showSettingsPanel ? "rotate(180deg)" : "rotate(0deg)" }}
                    fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2}
                  >
                    <path strokeLinecap="round" strokeLinejoin="round" d="M19 9l-7 7-7-7" />
                  </svg>
                </div>
              </button>
              {showSettingsPanel && (
            <Panel className="p-5">
              <div className="mb-4 flex items-center justify-between">
                <h3 className="text-sm font-semibold text-[var(--color-text)]">
                  {CAMPAIGNS.find(c => c.key === activeCampaign)?.icon} {CAMPAIGNS.find(c => c.key === activeCampaign)?.label}
                </h3>
                {settingsSaved && (
                  <span className="rounded-full bg-[var(--color-accent-soft)] px-3 py-1 text-xs font-medium text-[var(--color-accent)]">
                    ✓ Сохранено
                  </span>
                )}
              </div>

              {/* Test/Battle mode toggle */}
              {!isManualCampaign && (
                <div className={`mb-4 rounded-2xl border p-4 transition ${settings.test_mode ? "border-[var(--color-warning)] bg-[var(--color-accent-soft)]" : "border-[var(--color-accent)] bg-[var(--color-accent-soft)]"}`}>
                  <label className="flex items-center justify-between">
                    <div>
                      <div className="text-sm font-semibold text-[var(--color-text)]">
                        {settings.test_mode ? "🧪 Тестовый режим" : "🚀 Боевой режим"}
                      </div>
                      <div className="mt-0.5 text-xs text-[var(--color-text-muted)]">
                        {settings.test_mode ? `SMS только на: ${settings.test_phone || "—"}` : "SMS реальным клиентам из базы"}
                      </div>
                    </div>
                    <ToggleSwitch
                      on={!settings.test_mode}
                      onClick={() => updateSetting("test_mode", !settings.test_mode)}
                      size="md"
                    />
                  </label>
                  {settings.test_mode && (
                    <div className="mt-3">
                      <SettingField label="Тестовый номер" value={settings.test_phone || ""} onChange={(v) => updateSetting("test_phone", v)} />
                    </div>
                  )}
                </div>
              )}

              {/* SMS text editor */}
              {!isManualCampaign && (
                <div className="mb-4">
                  <div className="flex items-center justify-between mb-2">
                    <label className="text-[10px] font-semibold uppercase tracking-wide text-[var(--color-text-muted)]">
                      Текст SMS (используйте {"{link}"} для ссылки)
                    </label>
                    <button
                      type="button"
                      onClick={() => setShowSmsPreview(!showSmsPreview)}
                      className="rounded-full px-2.5 py-1 text-[10px] font-medium text-[var(--color-accent)] hover:bg-[var(--color-accent-soft)] transition"
                    >
                      {showSmsPreview ? "Скрыть предпросмотр" : "👁 Предпросмотр"}
                    </button>
                  </div>
                  <textarea
                    value={settings.sms_text_template || ""}
                    onChange={(e) => updateSetting("sms_text_template", e.target.value)}
                    rows={3}
                    className="w-full rounded-xl border border-[var(--color-border)] bg-[var(--color-surface)] px-3 py-2.5 text-xs text-[var(--color-text)] transition focus:border-[var(--color-accent)] focus:outline-none focus:ring-2 focus:ring-[var(--color-accent-soft)]"
                    placeholder="Ваш заказ готов. Согласуйте дату доставки по ссылке: {link}"
                  />
                  <div className="mt-1.5 text-[10px] text-[var(--color-text-muted)]">
                    {(settings.sms_text_template || "").replace("{link}", "https://dost.supersamsev.ru/d/XXXXX").length} символов
                  </div>
                  {showSmsPreview && (
                    <div className="mt-3 rounded-2xl border border-[var(--color-border)] bg-[var(--color-surface-strong)] p-4">
                      <div className="mb-2 text-[10px] font-semibold uppercase tracking-wide text-[var(--color-text-muted)]">Пример SMS (по первой группе из очереди):</div>
                      {queueData.length > 0 ? (
                        <>
                          <div className="text-[10px] text-[var(--color-text-muted)] mb-2">
                            Клиент: {queueData[0].customer_name || "—"} → {fmtPhone(queueData[0].customer_phone)}
                          </div>
                          <div className="rounded-xl border border-[var(--color-border)] bg-white px-3 py-2.5 text-xs text-gray-800" style={{ fontFamily: "monospace", whiteSpace: "pre-wrap", wordBreak: "break-all" }}>
                            {(settings.sms_text_template || "").replace("{link}", queueData[0].delivery_link || "https://dost.supersamsev.ru/d/XXXXX")}
                          </div>
                          <div className="mt-2 text-[10px] text-[var(--color-text-muted)]">
                            Длина: {(settings.sms_text_template || "").replace("{link}", queueData[0].delivery_link || "").length} символов
                            {(settings.sms_text_template || "").replace("{link}", queueData[0].delivery_link || "").length > 160 && " ⚠️ >160 символов = 2 SMS"}
                          </div>
                        </>
                      ) : (
                        <>
                          <div className="text-[10px] text-[var(--color-text-muted)] mb-2">Нет групп в очереди — пример с тестовой ссылкой:</div>
                          <div className="rounded-xl border border-[var(--color-border)] bg-white px-3 py-2.5 text-xs text-gray-800" style={{ fontFamily: "monospace", whiteSpace: "pre-wrap", wordBreak: "break-all" }}>
                            {(settings.sms_text_template || "").replace("{link}", "https://dost.supersamsev.ru/delivery/abc123example")}
                          </div>
                          <div className="mt-2 text-[10px] text-[var(--color-text-muted)]">
                            Длина: {(settings.sms_text_template || "").replace("{link}", "https://dost.supersamsev.ru/delivery/abc123example").length} символов
                          </div>
                        </>
                      )}
                    </div>
                  )}
                </div>
              )}

              {/* Intervals and attempts */}
              <div className="mb-1 text-xs font-semibold text-[var(--color-text)]">
                {isManualCampaign ? "⚙️ Настройки" : "📊 Интервалы и попытки"}
              </div>
              <div className="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-3">
                {!isManualCampaign && (
                  <>
                    <SettingField label="Пауза между проверками (сек)" value={settings.wait_between_checks_seconds} onChange={(v) => updateSetting("wait_between_checks_seconds", parseInt(v) || 25)} />
                    <SettingField label="Макс. время ожидания (мин)" value={settings.max_check_duration_minutes} onChange={(v) => updateSetting("max_check_duration_minutes", parseInt(v) || 90)} />
                    <SettingField label="Макс. попыток отправки" value={settings.max_attempts} onChange={(v) => updateSetting("max_attempts", parseInt(v) || 2)} />
                    <SettingField label="Пауза между SMS (сек)" value={settings.send_interval_seconds} onChange={(v) => updateSetting("send_interval_seconds", parseInt(v) || 15)} />
                    {!isPaidStorageCampaign && (
                      <SettingField label="Вторая SMS через (часов)" value={settings.second_sms_delay_hours} onChange={(v) => updateSetting("second_sms_delay_hours", parseInt(v) || 3)} />
                    )}
                  </>
                )}
                <SettingField label="Ручное согласование через (часов)" value={settings.auto_manual_after_hours} onChange={(v) => updateSetting("auto_manual_after_hours", parseInt(v) || 3)} />
                <SettingField label="Telegram chat ID" value={settings.telegram_chat_id || ""} onChange={(v) => updateSetting("telegram_chat_id", v)} />
              </div>

              {/* Work hours / days */}
              <div className="mt-5 rounded-2xl border border-[var(--color-border)] bg-[var(--color-surface)] p-4">
                <div className="mb-3 text-xs font-semibold text-[var(--color-text)]">⏰ Время отправки SMS</div>
                <div className="mb-4 flex items-center gap-2">
                  <select
                    value={settings.work_hours_start ?? 8}
                    onChange={(e) => updateSetting("work_hours_start", parseInt(e.target.value))}
                    className="rounded-xl border border-[var(--color-border)] bg-[var(--color-surface-strong)] px-3 py-2 text-xs text-[var(--color-text)] transition focus:border-[var(--color-accent)] focus:outline-none"
                  >
                    {Array.from({ length: 24 }, (_, h) => <option key={h} value={h}>{h}:00</option>)}
                  </select>
                  <span className="text-xs text-[var(--color-text-muted)]">—</span>
                  <select
                    value={settings.work_hours_end ?? 21}
                    onChange={(e) => updateSetting("work_hours_end", parseInt(e.target.value))}
                    className="rounded-xl border border-[var(--color-border)] bg-[var(--color-surface-strong)] px-3 py-2 text-xs text-[var(--color-text)] transition focus:border-[var(--color-accent)] focus:outline-none"
                  >
                    {Array.from({ length: 24 }, (_, h) => <option key={h} value={h}>{h}:00</option>)}
                  </select>
                  <span className="text-[10px] text-[var(--color-text-muted)]">по Москве</span>
                </div>
                <div className="flex flex-wrap gap-2">
                  {DAYS.map(d => {
                    const activeDays = parseWorkDays(settings.work_days);
                    const isOn = activeDays.has(d.num);
                    return (
                      <button
                        key={d.num}
                        type="button"
                        onClick={() => toggleWorkDay(d.num)}
                        className={`rounded-xl px-3.5 py-2 text-xs font-medium transition-all duration-200 ${
                          isOn
                            ? "bg-[var(--color-accent)] text-white shadow-sm"
                            : "border border-[var(--color-border)] bg-[var(--color-surface-strong)] text-[var(--color-text-muted)] hover:bg-[var(--color-surface)] hover:text-[var(--color-text)]"
                        }`}
                      >
                        {d.short}
                      </button>
                    );
                  })}
                </div>
                <div className="mt-3 text-[10px] text-[var(--color-text-muted)]">
                  Проверка статусов работает круглосуточно. Отправка — только в выбранные часы и дни.
                </div>
              </div>

              {/* Footer: enable toggle + save */}
              <div className="mt-5 flex items-center gap-3">
                <ToggleSwitch
                  on={settings.enabled ?? true}
                  onClick={() => updateSetting("enabled", !(settings.enabled ?? true))}
                  size="md"
                />
                <span className="text-xs font-medium text-[var(--color-text)]">
                  {settings.enabled ? "Кампания включена" : "Кампания выключена"}
                </span>
                <button
                  onClick={handleSaveSettings}
                  disabled={savingSettings}
                  className={`ml-auto rounded-full px-5 py-2 text-xs font-semibold transition-all duration-200 ${
                    settingsSaved
                      ? "bg-[#22c55e] text-white shadow-sm"
                      : "bg-[var(--color-accent)] text-white hover:opacity-90 shadow-sm"
                  } disabled:opacity-50`}
                >
                  {savingSettings ? "Сохранение…" : settingsSaved ? "✓ Сохранено" : "Сохранить"}
                </button>
              </div>
            </Panel>
            )}
            </div>
          )}

          {/* Stats summary */}
          <Panel className="p-4">
            <div className="flex flex-wrap items-center gap-2">
              <span className="mr-1 text-xs font-semibold text-[var(--color-text)]">Всего: {logs.length}</span>
              {Object.entries(STATUS_LABELS).map(([status, label]) => {
                const count = stats[status] || 0;
                if (count === 0) return null;
                return <Badge key={status} tone={STATUS_TONES[status] || "neutral"}>{label}: {count}</Badge>;
              })}
            </div>
          </Panel>

          {/* Filter + auto-refresh */}
          <div className="space-y-2">
            <div className="flex flex-wrap items-center justify-between gap-2">
              <div className="flex flex-wrap gap-2">
                <FilterButton active={filter === "all"} onClick={() => setFilter("all")}>Все ({logs.length})</FilterButton>
                {Object.entries(STATUS_LABELS).map(([status, label]) => {
                  const count = stats[status] || 0;
                  if (count === 0) return null;
                  return <FilterButton key={status} active={filter === status} onClick={() => setFilter(status)}>{label} ({count})</FilterButton>;
                })}
              </div>
              <label className="flex items-center gap-2 text-xs text-[var(--color-text-muted)] cursor-pointer">
                <ToggleSwitch
                  on={autoRefresh}
                  onClick={() => setAutoRefresh(!autoRefresh)}
                  size="sm"
                />
                Авто-обновление (15с)
              </label>
            </div>
            {/* Date + link filters */}
            <div className="flex flex-wrap items-center gap-2">
              <span className="text-[10px] uppercase tracking-wide text-[var(--color-text-muted)]">📅 Дата:</span>
              {[
                { v: "all", l: "Все" },
                { v: "today", l: "Сегодня" },
                { v: "yesterday", l: "Вчера" },
                { v: "7d", l: "7 дней" },
              ].map(opt => (
                <FilterButton key={opt.v} active={dateFilter === opt.v} onClick={() => setDateFilter(opt.v)}>{opt.l}</FilterButton>
              ))}
              <span className="ml-3 text-[10px] uppercase tracking-wide text-[var(--color-text-muted)]">🔗 Ссылка:</span>
              {[
                { v: "all", l: "Все" },
                { v: "opened", l: "Открыта" },
                { v: "not_opened", l: "Не открыта" },
              ].map(opt => (
                <FilterButton key={opt.v} active={linkFilter === opt.v} onClick={() => setLinkFilter(opt.v)}>{opt.l}</FilterButton>
              ))}
            </div>
          </div>

          {/* ── Delete toolbar ── */}
          <div className="flex flex-wrap items-center gap-2">
            <FilterButton active={false} onClick={toggleSelectAll}>
              {(() => {
                const filtered = logs.filter((entry) => {
                  const isOpened = openedGroupIds.has(entry.order_group_id);
                  return passesDateFilter(entry.created_at, dateFilter) && passesLinkFilter(isOpened, linkFilter);
                });
                const allIds = filtered.map(l => l.id);
                const allSelected = allIds.length > 0 && allIds.every(id => selectedIds.has(id));
                return allSelected ? "✓ Снять выделение" : "☐ Выбрать все";
              })()}
            </FilterButton>
            {selectedIds.size > 0 && (
              <button
                onClick={handleDeleteSelected}
                disabled={deleting}
                className="rounded-full border border-[var(--color-danger)] px-3.5 py-1.5 text-xs font-medium text-[var(--color-danger)] transition hover:bg-[var(--color-danger)] hover:text-white disabled:opacity-50"
              >
                {deleting ? "Удаление…" : `🗑 Удалить (${selectedIds.size})`}
              </button>
            )}
            {logs.some(l => l.was_test_mode === true) && (
              <button
                onClick={handleDeleteTestLogs}
                disabled={deleting}
                className="rounded-full border border-[var(--color-warning)] px-3.5 py-1.5 text-xs font-medium text-[var(--color-warning)] transition hover:bg-[var(--color-warning)] hover:text-white disabled:opacity-50"
              >
                🧪 Удалить все тестовые ({logs.filter(l => l.was_test_mode === true).length})
              </button>
            )}
            {selectedIds.size > 0 && (
              <span className="text-xs text-[var(--color-text-muted)]">Выбрано: {selectedIds.size}</span>
            )}
          </div>

          {/* ── Log table ── */}
          <Panel className="p-0">
            {/* Desktop table */}
            <div className="hidden sm:block overflow-x-auto">
              <div className="min-w-[1000px]">
                <TableHeader cols={logCols} />
                {logs.length === 0 ? (
                  <div className="px-4 py-8 text-center text-xs text-[var(--color-text-muted)]">Нет записей</div>
                ) : (
                  logs.filter((entry) => {
                    const isOpened = openedGroupIds.has(entry.order_group_id);
                    return passesDateFilter(entry.created_at, dateFilter) && passesLinkFilter(isOpened, linkFilter);
                  }).slice((logPage - 1) * PAGE_SIZE, logPage * PAGE_SIZE).map((entry) => {
                    const canRecheck = entry.status === "sent" || entry.status === "checking";
                    const isChecking = checkingIds.has(entry.id);
                    const wasTest = entry.was_test_mode === true;
                    const isReplaced = entry.sent_to && entry.sent_to !== entry.customer_phone;
                    const isOpened = openedGroupIds.has(entry.order_group_id);
                    return (
                      <TableRow
                        key={entry.id}
                        cols={logCols}
                        onClick={() => handleOpenGroup(entry.order_group_id)}
                      >
                        <div className="px-4 py-2.5" onClick={(e) => e.stopPropagation()}>
                          <input
                            type="checkbox"
                            checked={selectedIds.has(entry.id)}
                            onChange={() => toggleSelect(entry.id)}
                            className="h-4 w-4 cursor-pointer rounded border-[var(--color-border)] accent-[var(--color-accent)]"
                          />
                        </div>
                        <div className="px-4 py-2.5 text-[var(--color-text)]">
                          <div className="font-medium">{fmtPhone(entry.customer_phone)}</div>
                          {wasTest
                            ? <div className="text-[10px] text-[var(--color-warning)]">🧪 тестовая</div>
                            : <div className="text-[10px]" style={{ color: "#22c55e" }}>🚀 боевая</div>
                          }
                          {isReplaced && <div className="text-[10px] text-[var(--color-warning)]">→ {fmtPhone(entry.sent_to)}</div>}
                        </div>
                        <div className="px-4 py-2.5">
                          <Badge tone={STATUS_TONES[entry.status] || "neutral"}>
                            {STATUS_ICONS[entry.status] || ""} {STATUS_LABELS[entry.status] || entry.status}
                          </Badge>
                          {entry.attempts > 1 && <div className="mt-0.5 text-[10px] text-[var(--color-text-muted)]">попытка {entry.attempts}</div>}
                          {entry.sms_code && SMS_CODE_LABELS[entry.sms_code] && (
                            <div className="mt-0.5 text-[10px] text-[var(--color-text-muted)]" title={`Код: ${entry.sms_code}`}>
                              {entry.sms_code}: {SMS_CODE_LABELS[entry.sms_code]}
                            </div>
                          )}
                        </div>
                        <div className="px-4 py-2.5">
                          {(() => { const a = fmtAgreement(entry._group); return (
                            <span className="text-xs font-medium" style={{ color: AGREEMENT_TONES[a.tone] }}>{a.text}</span>
                          ); })()}
                        </div>
                        <div className="px-4 py-2.5 text-[var(--color-text-muted)]">
                          {fmtTime(entry.created_at)}
                          <div className="text-[10px]">{fmtElapsed(entry.created_at)}</div>
                        </div>
                        <div className="px-4 py-2.5 text-[var(--color-text-muted)]">
                          {entry.checked_at ? (
                            <div>
                              <div className="text-[var(--color-text)]">{fmtTime(entry.checked_at)}</div>
                              <div className="text-[10px]">{fmtElapsed(entry.checked_at)}</div>
                            </div>
                          ) : (
                            <span className="text-[var(--color-text-muted)]">— не проверено</span>
                          )}
                          {entry.error_message && <div className="mt-0.5 text-[10px] text-[var(--color-danger)]">⚠ {entry.error_message.slice(0, 80)}</div>}
                        </div>
                        <div className="px-4 py-2.5">
                          {isOpened
                            ? <span className="font-medium" style={{ color: "#22c55e" }}>✅ Открыта</span>
                            : <span className="text-[var(--color-text-muted)]">— нет</span>
                          }
                        </div>
                        <div className="px-4 py-2.5" onClick={(e) => e.stopPropagation()}>
                          {canRecheck ? (
                            <button
                              onClick={() => handleRecheck(entry.id)}
                              disabled={isChecking}
                              className="rounded-xl border border-[var(--color-border)] px-2.5 py-1.5 text-[11px] font-medium text-[var(--color-text)] transition hover:bg-[var(--color-surface-strong)] hover:border-[var(--color-accent)] disabled:opacity-40"
                            >
                              {isChecking ? "⏳ Проверка…" : "↻ Проверить"}
                            </button>
                          ) : (
                            <span className="text-[10px] text-[var(--color-text-muted)]">→ к доставке</span>
                          )}
                        </div>
                      </TableRow>
                    );
                  })
                )}
              </div>
            </div>

            {/* Mobile cards */}
            <div className="sm:hidden p-2">
              {logs.length === 0 ? (
                <div className="px-4 py-8 text-center text-xs text-[var(--color-text-muted)]">Нет записей</div>
              ) : (
                logs.filter((entry) => {
                  const isOpened = openedGroupIds.has(entry.order_group_id);
                  return passesDateFilter(entry.created_at, dateFilter) && passesLinkFilter(isOpened, linkFilter);
                }).slice((logPage - 1) * PAGE_SIZE, logPage * PAGE_SIZE).map((entry) => {
                  const canRecheck = entry.status === "sent" || entry.status === "checking";
                  const isChecking = checkingIds.has(entry.id);
                  const wasTest = entry.was_test_mode === true;
                  const isReplaced = entry.sent_to && entry.sent_to !== entry.customer_phone;
                  const isOpened = openedGroupIds.has(entry.order_group_id);
                  return (
                    <MobileCard key={entry.id} onClick={() => handleOpenGroup(entry.order_group_id)}>
                      {/* Header: checkbox + phone + status badge */}
                      <div className="flex items-center justify-between mb-2">
                        <div className="flex items-center gap-2">
                          <input
                            type="checkbox"
                            checked={selectedIds.has(entry.id)}
                            onChange={() => toggleSelect(entry.id)}
                            onClick={(e) => e.stopPropagation()}
                            className="h-4 w-4 cursor-pointer rounded border-[var(--color-border)] accent-[var(--color-accent)]"
                          />
                          <div className="text-sm font-semibold text-[var(--color-text)]">
                            {fmtPhone(entry.customer_phone)}
                          </div>
                        </div>
                        <Badge tone={STATUS_TONES[entry.status] || "neutral"}>
                          {STATUS_ICONS[entry.status] || ""} {STATUS_LABELS[entry.status] || entry.status}
                        </Badge>
                      </div>
                      {/* Test/real badge */}
                      <div className="mb-2">
                        {wasTest
                          ? <span className="text-[10px] text-[var(--color-warning)]">🧪 тестовая</span>
                          : <span className="text-[10px]" style={{ color: "#22c55e" }}>🚀 боевая</span>
                        }
                        {isReplaced && <span className="text-[10px] text-[var(--color-warning)] ml-2">→ {fmtPhone(entry.sent_to)}</span>}
                      </div>
                      {/* Info rows */}
                      <div className="border-t border-[var(--color-border)] pt-2">
                        {(() => { const a = fmtAgreement(entry._group); return (
                          <MobileRow label="Согласование" value={a.text} color={AGREEMENT_TONES[a.tone]} />
                        ); })()}
                        {entry.sms_code && SMS_CODE_LABELS[entry.sms_code] && (
                          <MobileRow label="Код SMS" value={`${entry.sms_code}: ${SMS_CODE_LABELS[entry.sms_code]}`} />
                        )}
                        <MobileRow label="Отправлено" value={`${fmtTime(entry.created_at)} (${fmtElapsed(entry.created_at)})`} />
                        <MobileRow
                          label="Проверено"
                          value={entry.checked_at
                            ? `${fmtTime(entry.checked_at)} (${fmtElapsed(entry.checked_at)})`
                            : null
                              ? ""
                              : "— не проверено"
                          }
                          color="var(--color-text)"
                        />
                        <MobileRow
                          label="Ссылка"
                          value={isOpened ? "✅ Открыта" : "— нет"}
                          color={isOpened ? "#22c55e" : "var(--color-text-muted)"}
                        />
                        {entry.error_message && (
                          <div className="mt-1 text-[10px] text-[var(--color-danger)]">⚠ {entry.error_message.slice(0, 80)}</div>
                        )}
                      </div>
                      {/* Action button */}
                      {canRecheck && (
                        <div className="mt-2 flex justify-end" onClick={(e) => e.stopPropagation()}>
                          <button
                            onClick={() => handleRecheck(entry.id)}
                            disabled={isChecking}
                            className="rounded-xl border border-[var(--color-border)] px-3 py-1.5 text-xs font-medium text-[var(--color-text)] transition hover:bg-[var(--color-surface-strong)] hover:border-[var(--color-accent)] disabled:opacity-40"
                          >
                            {isChecking ? "⏳ Проверка…" : "↻ Проверить"}
                          </button>
                        </div>
                      )}
                    </MobileCard>
                  );
                })
              )}
            </div>
          </Panel>
          {(() => {
            const filteredLogs = logs.filter((entry) => {
              const isOpened = openedGroupIds.has(entry.order_group_id);
              return passesDateFilter(entry.created_at, dateFilter) && passesLinkFilter(isOpened, linkFilter);
            });
            const totalPages = Math.ceil(filteredLogs.length / PAGE_SIZE);
            if (totalPages <= 1) return null;
            return <Pagination page={logPage} totalPages={totalPages} onChange={setLogPage} itemsPerPage={PAGE_SIZE} totalItems={filteredLogs.length} />;
          })()}
        </>
      )}

      {/* ── Footer ─────────────────────────────────────────────────────────── */}
      <div className="flex justify-end">
        <button
          onClick={loadData}
          className="rounded-full border border-[var(--color-border)] bg-[var(--color-surface)] px-5 py-2.5 text-sm font-medium text-[var(--color-text)] transition hover:bg-[var(--color-surface-strong)] hover:border-[var(--color-text-muted)]"
        >
          ↻ Обновить
        </button>
      </div>
    </div>
  );
};