(() => {
  const { useState, useEffect, apiFetch, fmtDate, fmtFullDate, getDateTimeConfig, formatDateTime } = window.DC;

  const WEEKDAYS = [
    { value: 0, label: "Domingo" },
    { value: 1, label: "Lunes" },
    { value: 2, label: "Martes" },
    { value: 3, label: "Miércoles" },
    { value: 4, label: "Jueves" },
    { value: 5, label: "Viernes" },
    { value: 6, label: "Sábado" },
  ];

  const MONTHS = [
    { value: 1, label: "Enero" },
    { value: 2, label: "Febrero" },
    { value: 3, label: "Marzo" },
    { value: 4, label: "Abril" },
    { value: 5, label: "Mayo" },
    { value: 6, label: "Junio" },
    { value: 7, label: "Julio" },
    { value: 8, label: "Agosto" },
    { value: 9, label: "Septiembre" },
    { value: 10, label: "Octubre" },
    { value: 11, label: "Noviembre" },
    { value: 12, label: "Diciembre" },
  ];

  const FREQUENCY_OPTIONS = [
    { value: "one_time", label: "Una sola vez" },
    { value: "interval", label: "Cada ciertos minutos" },
    { value: "hourly", label: "Cada hora" },
    { value: "daily", label: "Cada día" },
    { value: "weekly", label: "Cada semana" },
    { value: "monthly", label: "Cada mes" },
    { value: "yearly", label: "Cada año" },
    { value: "custom", label: "Cron personalizado" },
  ];

  function pad2(value) {
    return String(value).padStart(2, "0");
  }

  function toNumber(value, fallback) {
    const parsed = Number(value);
    return Number.isFinite(parsed) ? parsed : fallback;
  }

  function clamp(value, min, max) {
    return Math.max(min, Math.min(max, value));
  }

  function daysInMonth(month) {
    return new Date(Date.UTC(2024, month, 0)).getUTCDate();
  }

  function getDayOptions(month) {
    const total = daysInMonth(month);
    return Array.from({ length: total }, (_, index) => index + 1);
  }

  function formatLocalDateTimeInput(isoString) {
    if (!isoString) return "";
    const date = new Date(isoString);
    if (Number.isNaN(date.getTime())) return "";
    const year = date.getFullYear();
    const month = pad2(date.getMonth() + 1);
    const day = pad2(date.getDate());
    const hour = pad2(date.getHours());
    const minute = pad2(date.getMinutes());
    return `${year}-${month}-${day}T${hour}:${minute}`;
  }

  function localInputToIso(value) {
    if (!value) return "";
    const date = new Date(value);
    if (Number.isNaN(date.getTime())) return "";
    return date.toISOString();
  }

  function defaultScheduleForm() {
    return {
      name: "",
      branch: "main",
      server_ids: [],
      dry_run: false,
      validate_after_deploy: false,
      auto_rollback: false,
      validation_delay_ms: 8000,
      requires_approval: false,
      post_action_preset_ids: [],
      frequency: "daily",
      run_once_at: "",
      interval_minutes: 15,
      minute: 0,
      hour: 3,
      weekday: 1,
      day_of_month: 1,
      month: 1,
      custom_cron: "0 3 * * *",
      cron_expression: "0 3 * * *",
    };
  }

  function defaultScanScheduleForm() {
    return {
      name: "",
      server_ids: [],
      auto_init_git: false,
      gh_branch: "",
      frequency: "daily",
      run_once_at: "",
      interval_minutes: 15,
      minute: 0,
      hour: 3,
      weekday: 1,
      day_of_month: 1,
      month: 1,
      custom_cron: "0 3 * * *",
      cron_expression: "0 3 * * *",
    };
  }

  function buildCronExpression(form) {
    const minute = clamp(toNumber(form.minute, 0), 0, 59);
    const hour = clamp(toNumber(form.hour, 0), 0, 23);
    const weekday = clamp(toNumber(form.weekday, 1), 0, 6);
    const month = clamp(toNumber(form.month, 1), 1, 12);
    const dayLimit = daysInMonth(month);
    const dayOfMonth = clamp(toNumber(form.day_of_month, 1), 1, dayLimit);
    const intervalMinutes = clamp(toNumber(form.interval_minutes, 15), 1, 59);

    switch (form.frequency) {
      case "one_time":
        return "";
      case "interval":
        return `*/${intervalMinutes} * * * *`;
      case "hourly":
        return `${minute} * * * *`;
      case "daily":
        return `${minute} ${hour} * * *`;
      case "weekly":
        return `${minute} ${hour} * * ${weekday}`;
      case "monthly":
        return `${minute} ${hour} ${dayOfMonth} * *`;
      case "yearly":
        return `${minute} ${hour} ${dayOfMonth} ${month} *`;
      case "custom":
        return (form.custom_cron || "").trim();
      default:
        return `${minute} ${hour} * * *`;
    }
  }

  function describeSchedule(form) {
    const { time_zone: timeZone } = getDateTimeConfig();
    const timeZoneLabel = String(timeZone || "America/Lima").replace(/_/g, " ");
    const minute = pad2(clamp(toNumber(form.minute, 0), 0, 59));
    const hour = pad2(clamp(toNumber(form.hour, 0), 0, 23));
    const weekday = WEEKDAYS.find((item) => item.value === toNumber(form.weekday, 1));
    const month = MONTHS.find((item) => item.value === toNumber(form.month, 1));
    const intervalMinutes = clamp(toNumber(form.interval_minutes, 15), 1, 59);
    const dayOfMonth = clamp(toNumber(form.day_of_month, 1), 1, 31);
    const runOnceLabel = form.run_once_at
      ? formatDateTime(localInputToIso(form.run_once_at) || form.run_once_at, {
          dateStyle: "full",
          timeStyle: "short",
        })
      : "";

    switch (form.frequency) {
      case "one_time":
        return runOnceLabel ? `Se ejecutará una sola vez el ${runOnceLabel}.` : "Selecciona la fecha y hora exactas para esta ejecución.";
      case "interval":
        return `Se ejecutará cada ${intervalMinutes} minuto${intervalMinutes !== 1 ? "s" : ""} (${timeZoneLabel}).`;
      case "hourly":
        return `Se ejecutará cada hora al minuto ${minute} (${timeZoneLabel}).`;
      case "daily":
        return `Se ejecutará todos los días a las ${hour}:${minute} (${timeZoneLabel}).`;
      case "weekly":
        return `Se ejecutará cada ${weekday?.label || "semana"} a las ${hour}:${minute} (${timeZoneLabel}).`;
      case "monthly":
        return `Se ejecutará el día ${dayOfMonth} de cada mes a las ${hour}:${minute} (${timeZoneLabel}).`;
      case "yearly":
        return `Se ejecutará cada ${dayOfMonth} de ${month?.label || "ese mes"} a las ${hour}:${minute} (${timeZoneLabel}).`;
      case "custom":
        return form.custom_cron ? `Cron personalizado: ${form.custom_cron}` : "Define la expresión cron personalizada.";
      default:
        return "";
    }
  }

  function parseCronExpression(expression) {
    const fallback = defaultScheduleForm();
    if (typeof expression !== "string") return fallback;

    const parts = expression.trim().split(/\s+/);
    if (parts.length !== 5) {
      return { ...fallback, frequency: "custom", custom_cron: expression || "", cron_expression: expression || "" };
    }

    const [minute, hour, dom, month, dow] = parts;
    const parsedMinute = toNumber(minute, 0);
    const parsedHour = toNumber(hour, 3);
    const parsedDom = toNumber(dom, 1);
    const parsedMonth = toNumber(month, 1);
    const parsedDow = toNumber(dow, 1);

    if (/^\*\/\d+$/.test(minute) && hour === "*" && dom === "*" && month === "*" && dow === "*") {
      return {
        ...fallback,
        frequency: "interval",
        interval_minutes: clamp(toNumber(minute.slice(2), 15), 1, 59),
        custom_cron: expression,
        cron_expression: expression,
      };
    }

    if (/^\d+$/.test(minute) && hour === "*" && dom === "*" && month === "*" && dow === "*") {
      return {
        ...fallback,
        frequency: "hourly",
        minute: clamp(parsedMinute, 0, 59),
        custom_cron: expression,
        cron_expression: expression,
      };
    }

    if (/^\d+$/.test(minute) && /^\d+$/.test(hour) && dom === "*" && month === "*" && dow === "*") {
      return {
        ...fallback,
        frequency: "daily",
        minute: clamp(parsedMinute, 0, 59),
        hour: clamp(parsedHour, 0, 23),
        custom_cron: expression,
        cron_expression: expression,
      };
    }

    if (/^\d+$/.test(minute) && /^\d+$/.test(hour) && dom === "*" && month === "*" && /^\d+$/.test(dow)) {
      return {
        ...fallback,
        frequency: "weekly",
        minute: clamp(parsedMinute, 0, 59),
        hour: clamp(parsedHour, 0, 23),
        weekday: clamp(parsedDow, 0, 6),
        custom_cron: expression,
        cron_expression: expression,
      };
    }

    if (/^\d+$/.test(minute) && /^\d+$/.test(hour) && /^\d+$/.test(dom) && month === "*" && dow === "*") {
      return {
        ...fallback,
        frequency: "monthly",
        minute: clamp(parsedMinute, 0, 59),
        hour: clamp(parsedHour, 0, 23),
        day_of_month: clamp(parsedDom, 1, 31),
        custom_cron: expression,
        cron_expression: expression,
      };
    }

    if (/^\d+$/.test(minute) && /^\d+$/.test(hour) && /^\d+$/.test(dom) && /^\d+$/.test(month) && dow === "*") {
      return {
        ...fallback,
        frequency: "yearly",
        minute: clamp(parsedMinute, 0, 59),
        hour: clamp(parsedHour, 0, 23),
        month: clamp(parsedMonth, 1, 12),
        day_of_month: clamp(parsedDom, 1, 31),
        custom_cron: expression,
        cron_expression: expression,
      };
    }

    return {
      ...fallback,
      frequency: "custom",
      custom_cron: expression,
      cron_expression: expression,
    };
  }

  function parseScheduleForm(schedule) {
    const fallback = defaultScheduleForm();
    if (schedule?.run_once_at) {
      return {
        ...fallback,
        frequency: "one_time",
        run_once_at: formatLocalDateTimeInput(schedule.run_once_at),
        cron_expression: "",
        custom_cron: "",
      };
    }
    return parseCronExpression(schedule?.cron_expression || "0 3 * * *");
  }

  function RecentRunsBadges({ runs }) {
    if (!Array.isArray(runs) || runs.length === 0) return null;
    const ordered = [...runs].reverse();
    return (
      <div style={{ display: "inline-flex", gap: 4, alignItems: "center", marginLeft: 4 }} title="Últimas 3 corridas (más reciente a la derecha)">
        {ordered.map((run) => {
          const status = run?.status || "unknown";
          const tone =
            status === "ok" ? "#22d3a5"
              : status === "error" ? "#f43f5e"
                : status === "pending_approval" ? "#a78bfa"
                  : status === "started" ? "#00e5ff"
                    : "#64748b";
          const symbol =
            status === "ok" ? "✓"
              : status === "error" ? "✗"
                : status === "pending_approval" ? "⧗"
                  : status === "started" ? "•"
                    : "·";
          return (
            <span
              key={run.id}
              style={{
                display: "inline-flex",
                alignItems: "center",
                justifyContent: "center",
                width: 16,
                height: 16,
                borderRadius: 4,
                fontSize: 10,
                fontWeight: 700,
                color: tone,
                background: `${tone}18`,
                border: `1px solid ${tone}40`,
              }}
            >
              {symbol}
            </span>
          );
        })}
      </div>
    );
  }

  function SchedulesTabs({ activeTab, onChange, deployTotal, scanTotal, historyTotal }) {
    const tabs = [
      { key: "activos", icon: "📋", label: `Programados${deployTotal + scanTotal > 0 ? ` (${deployTotal + scanTotal})` : ""}` },
      { key: "historial", icon: "🕘", label: `Historial${historyTotal > 0 ? ` (${historyTotal})` : ""}` },
    ];
    return (
      <div className="card" style={{ marginBottom: 14 }}>
        <div className="card-body" style={{ padding: 12 }}>
          <div className="nav-tabs-wrap settings-tabs-wrap internal-tabs-wrap">
            <div className="nav-tabs settings-tabs internal-tabs">
              {tabs.map((tab) => (
                <button
                  key={tab.key}
                  type="button"
                  onClick={() => onChange(tab.key)}
                  className={`nav-tab${activeTab === tab.key ? " active" : ""}`}
                  title={tab.label}
                >
                  <span className="nav-tab-icon" aria-hidden="true">{tab.icon}</span>
                  <span className="nav-tab-text">{tab.label}</span>
                </button>
              ))}
            </div>
          </div>
        </div>
      </div>
    );
  }

  function SchedulesView({ toast, currentUser }) {
    const [schedules, setSchedules] = useState([]);
    const [schedulePage, setSchedulePage] = useState(1);
    const SCHEDULE_PAGE_SIZE = 5;
    const [scanSchedules, setScanSchedules] = useState([]);
    const [scheduleRuns, setScheduleRuns] = useState([]);
    const [scanRuns, setScanRuns] = useState([]);
    const [deployHistoryPage, setDeployHistoryPage] = useState(1);
    const [scanHistoryPage, setScanHistoryPage] = useState(1);
    const [deployHistoryTotal, setDeployHistoryTotal] = useState(0);
    const [scanHistoryTotal, setScanHistoryTotal] = useState(0);
    const [historyLoading, setHistoryLoading] = useState(false);
    const HISTORY_PAGE_SIZE = 5;
    const [activeTab, setActiveTab] = useState(() => {
      try {
        const saved = window.localStorage.getItem("dc:tabs:schedules");
        return saved === "historial" ? "historial" : "activos";
      } catch (_) {
        return "activos";
      }
    });
    useEffect(() => {
      try { window.localStorage.setItem("dc:tabs:schedules", activeTab); } catch (_) {}
    }, [activeTab]);
    const [status, setStatus] = useState(null);
    const [loading, setLoading] = useState(true);
    const [formOpen, setFormOpen] = useState(false);
    const [scanFormOpen, setScanFormOpen] = useState(false);
    const [editTarget, setEditTarget] = useState(null);
    const [scanEditTarget, setScanEditTarget] = useState(null);
    const [runningNow, setRunningNow] = useState(false);
    const [runningScanNow, setRunningScanNow] = useState(false);
    const [form, setForm] = useState(defaultScheduleForm());
    const [scanForm, setScanForm] = useState(defaultScanScheduleForm());
    const [servers, setServers] = useState([]);
    const [actionPresets, setActionPresets] = useState([]);
    const [saving, setSaving] = useState(false);
    const [savingScan, setSavingScan] = useState(false);

    const canWrite = currentUser?.role === "admin" || currentUser?.role === "superadmin";
    const isSuperAdmin = currentUser?.role === "superadmin";
    const allServerIds = servers.map((server) => server.id);
    const allServersSelected = allServerIds.length > 0 && allServerIds.every((id) => form.server_ids.includes(id));
    const allScanServersSelected = allServerIds.length > 0 && allServerIds.every((id) => scanForm.server_ids.includes(id));
    const selectedActionPresets = actionPresets.filter((preset) => form.post_action_preset_ids.includes(preset.id));
    const scheduleSummary = describeSchedule(form);
    const scanScheduleSummary = describeSchedule(scanForm);
    const computedCronExpression = buildCronExpression(form);
    const computedScanCronExpression = buildCronExpression(scanForm);
    const computedRunOnceAt = form.frequency === "one_time" ? localInputToIso(form.run_once_at) : "";
    const computedScanRunOnceAt = scanForm.frequency === "one_time" ? localInputToIso(scanForm.run_once_at) : "";
    const dayOptions = getDayOptions(form.month);
    const scanDayOptions = getDayOptions(scanForm.month);

    const loadHistory = async ({ deployPage, scanPage } = {}) => {
      const dPage = deployPage || deployHistoryPage;
      const sPage = scanPage || scanHistoryPage;
      const deployOffset = (dPage - 1) * HISTORY_PAGE_SIZE;
      const scanOffset = (sPage - 1) * HISTORY_PAGE_SIZE;
      setHistoryLoading(true);
      try {
        const [history, scanHistory] = await Promise.all([
          apiFetch(`/schedules/history?limit=${HISTORY_PAGE_SIZE}&offset=${deployOffset}`),
          apiFetch(`/schedules/scans/history?limit=${HISTORY_PAGE_SIZE}&offset=${scanOffset}`),
        ]);
        setScheduleRuns(Array.isArray(history?.runs) ? history.runs : []);
        setScanRuns(Array.isArray(scanHistory?.runs) ? scanHistory.runs : []);
        setDeployHistoryTotal(Number.isFinite(history?.total) ? history.total : 0);
        setScanHistoryTotal(Number.isFinite(scanHistory?.total) ? scanHistory.total : 0);
      } catch (_) {}
      setHistoryLoading(false);
    };

    const load = async () => {
      setLoading(true);
      try {
        const [list, scanList, stat] = await Promise.all([
          apiFetch("/schedules"),
          apiFetch("/schedules/scans"),
          apiFetch("/schedules/status"),
        ]);
        setSchedules(Array.isArray(list) ? list : []);
        setScanSchedules(Array.isArray(scanList) ? scanList : []);
        setStatus(stat?.error ? null : stat);
        await loadHistory({ deployPage: deployHistoryPage, scanPage: scanHistoryPage });
      } catch (_) {}
      setLoading(false);
    };

    useEffect(() => { load(); }, []);

    useEffect(() => {
      if (loading) return;
      loadHistory({ deployPage: deployHistoryPage, scanPage: scanHistoryPage });
      // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [deployHistoryPage, scanHistoryPage]);

    useEffect(() => {
      apiFetch("/servers").then((data) => setServers(Array.isArray(data) ? data : []));
      apiFetch("/http-actions/presets").then((data) => setActionPresets(Array.isArray(data) ? data : []));
    }, []);

    useEffect(() => {
      setForm((prev) => {
        const normalizedMonth = clamp(toNumber(prev.month, 1), 1, 12);
        const normalizedDay = clamp(toNumber(prev.day_of_month, 1), 1, daysInMonth(normalizedMonth));
        const cronExpression = buildCronExpression({ ...prev, month: normalizedMonth, day_of_month: normalizedDay });
        const normalizedRunOnceAt = prev.frequency === "one_time" ? localInputToIso(prev.run_once_at) : "";

        if (
          normalizedMonth === prev.month &&
          normalizedDay === prev.day_of_month &&
          cronExpression === prev.cron_expression &&
          normalizedRunOnceAt === (prev.run_once_at_iso || "")
        ) {
          return prev;
        }

        return {
          ...prev,
          month: normalizedMonth,
          day_of_month: normalizedDay,
          cron_expression: cronExpression,
          run_once_at_iso: normalizedRunOnceAt,
        };
      });
    }, [
      form.run_once_at,
      form.frequency,
      form.interval_minutes,
      form.minute,
      form.hour,
      form.weekday,
      form.day_of_month,
      form.month,
      form.custom_cron,
    ]);

    useEffect(() => {
      setScanForm((prev) => {
        const normalizedMonth = clamp(toNumber(prev.month, 1), 1, 12);
        const normalizedDay = clamp(toNumber(prev.day_of_month, 1), 1, daysInMonth(normalizedMonth));
        const cronExpression = buildCronExpression({ ...prev, month: normalizedMonth, day_of_month: normalizedDay });
        const normalizedRunOnceAt = prev.frequency === "one_time" ? localInputToIso(prev.run_once_at) : "";

        if (
          normalizedMonth === prev.month &&
          normalizedDay === prev.day_of_month &&
          cronExpression === prev.cron_expression &&
          normalizedRunOnceAt === (prev.run_once_at_iso || "")
        ) {
          return prev;
        }

        return {
          ...prev,
          month: normalizedMonth,
          day_of_month: normalizedDay,
          cron_expression: cronExpression,
          run_once_at_iso: normalizedRunOnceAt,
        };
      });
    }, [
      scanForm.run_once_at,
      scanForm.frequency,
      scanForm.interval_minutes,
      scanForm.minute,
      scanForm.hour,
      scanForm.weekday,
      scanForm.day_of_month,
      scanForm.month,
      scanForm.custom_cron,
    ]);

    const openForm = (schedule = null) => {
      if (schedule) {
        let serverIds = Array.isArray(schedule.server_ids) ? schedule.server_ids : [];
        if (!serverIds.length) {
          try { serverIds = JSON.parse(schedule.server_ids_json || "[]"); } catch (_) {}
        }
        const parsedCron = parseScheduleForm(schedule);
        setForm({
          ...defaultScheduleForm(),
          ...parsedCron,
          name: schedule.name || "",
          branch: schedule.branch || "main",
          server_ids: serverIds,
          dry_run: !!schedule.dry_run,
          validate_after_deploy: !!schedule.validate_after_deploy,
          auto_rollback: !!schedule.auto_rollback,
          validation_delay_ms: schedule.validation_delay_ms || 8000,
          requires_approval: !!schedule.requires_approval,
          post_action_preset_ids: Array.isArray(schedule.post_action_preset_ids)
            ? schedule.post_action_preset_ids.map(Number).filter(Boolean)
            : [],
          cron_expression: schedule.cron_expression || parsedCron.cron_expression,
          custom_cron: schedule.cron_expression || parsedCron.custom_cron,
          run_once_at: parsedCron.run_once_at || "",
          run_once_at_iso: schedule.run_once_at || "",
        });
        setEditTarget(schedule);
      } else {
        setForm(defaultScheduleForm());
        setEditTarget(null);
      }
      setFormOpen(true);
    };

    const openScanForm = (schedule = null) => {
      if (schedule) {
        let serverIds = Array.isArray(schedule.server_ids) ? schedule.server_ids : [];
        if (!serverIds.length) {
          try { serverIds = JSON.parse(schedule.server_ids_json || "[]"); } catch (_) {}
        }
        const parsedCron = parseScheduleForm(schedule);
        setScanForm({
          ...defaultScanScheduleForm(),
          ...parsedCron,
          name: schedule.name || "",
          server_ids: serverIds,
          auto_init_git: !!schedule.auto_init_git,
          gh_branch: schedule.gh_branch || "",
          cron_expression: schedule.cron_expression || parsedCron.cron_expression,
          custom_cron: schedule.cron_expression || parsedCron.custom_cron,
          run_once_at: parsedCron.run_once_at || "",
          run_once_at_iso: schedule.run_once_at || "",
        });
        setScanEditTarget(schedule);
      } else {
        setScanForm(defaultScanScheduleForm());
        setScanEditTarget(null);
      }
      setScanFormOpen(true);
    };

    const saveForm = async () => {
      const cronExpression = buildCronExpression(form);
      const runOnceAt = form.frequency === "one_time" ? localInputToIso(form.run_once_at) : "";

      if (!form.name.trim()) { toast("El nombre es obligatorio", "err"); return; }
      if (form.frequency === "one_time" && !runOnceAt) { toast("La fecha y hora únicas son obligatorias", "err"); return; }
      if (form.frequency !== "one_time" && !cronExpression.trim()) { toast("La programación es obligatoria", "err"); return; }
      if (!form.server_ids.length) { toast("Selecciona al menos un servidor", "err"); return; }

      setSaving(true);
      try {
        const payload = {
          name: form.name.trim(),
          cron_expression: form.frequency === "one_time" ? "" : cronExpression.trim(),
          run_once_at: runOnceAt || null,
          branch: form.branch,
          server_ids: form.server_ids,
          dry_run: form.dry_run,
          validate_after_deploy: form.validate_after_deploy,
          auto_rollback: form.auto_rollback,
          validation_delay_ms: Number(form.validation_delay_ms) || 8000,
          requires_approval: form.requires_approval,
          post_action_preset_ids: form.post_action_preset_ids,
        };

        let res;
        if (editTarget) {
          res = await apiFetch(`/schedules/${editTarget.id}`, { method: "PUT", body: payload });
        } else {
          res = await apiFetch("/schedules", { method: "POST", body: payload });
        }

        if (res?.error) { toast(res.error, "err"); return; }

        toast(editTarget ? "Programación actualizada" : "Programación creada", "ok");
        setFormOpen(false);
        await load();
      } catch (error) {
        toast(error.message || "Error al guardar", "err");
      } finally {
        setSaving(false);
      }
    };

    const saveScanForm = async () => {
      const cronExpression = buildCronExpression(scanForm);
      const runOnceAt = scanForm.frequency === "one_time" ? localInputToIso(scanForm.run_once_at) : "";

      if (!scanForm.name.trim()) { toast("El nombre es obligatorio", "err"); return; }
      if (scanForm.frequency === "one_time" && !runOnceAt) { toast("La fecha y hora únicas son obligatorias", "err"); return; }
      if (scanForm.frequency !== "one_time" && !cronExpression.trim()) { toast("La programación es obligatoria", "err"); return; }
      if (!scanForm.server_ids.length) { toast("Selecciona al menos un servidor", "err"); return; }

      setSavingScan(true);
      try {
        const payload = {
          name: scanForm.name.trim(),
          cron_expression: scanForm.frequency === "one_time" ? "" : cronExpression.trim(),
          run_once_at: runOnceAt || null,
          server_ids: scanForm.server_ids,
          auto_init_git: scanForm.auto_init_git,
          gh_branch: scanForm.gh_branch.trim(),
        };

        let res;
        if (scanEditTarget) {
          res = await apiFetch(`/schedules/scans/${scanEditTarget.id}`, { method: "PUT", body: payload });
        } else {
          res = await apiFetch("/schedules/scans", { method: "POST", body: payload });
        }

        if (res?.error) { toast(res.error, "err"); return; }

        toast(scanEditTarget ? "Programación de scan actualizada" : "Programación de scan creada", "ok");
        setScanFormOpen(false);
        await load();
      } catch (error) {
        toast(error.message || "Error al guardar", "err");
      } finally {
        setSavingScan(false);
      }
    };

    const toggleEnabled = async (schedule) => {
      const res = await apiFetch(`/schedules/${schedule.id}/toggle`, { method: "POST" });
      if (res?.error) { toast(res.error, "err"); return; }
      toast(schedule.enabled ? "Programación pausada" : "Programación activada", "ok");
      await load();
    };

    const deleteSchedule = async (schedule) => {
      if (!window.confirm(`¿Eliminar programación "${schedule.name}"?`)) return;
      const res = await apiFetch(`/schedules/${schedule.id}`, { method: "DELETE" });
      if (res?.error) { toast(res.error, "err"); return; }
      toast("Programación eliminada", "ok");
      await load();
    };

    const toggleScanEnabled = async (schedule) => {
      const res = await apiFetch(`/schedules/scans/${schedule.id}/toggle`, { method: "POST" });
      if (res?.error) { toast(res.error, "err"); return; }
      toast(schedule.enabled ? "Programación de scan pausada" : "Programación de scan activada", "ok");
      await load();
    };

    const deleteScanSchedule = async (schedule) => {
      if (!window.confirm(`¿Eliminar programación de scan "${schedule.name}"?`)) return;
      const res = await apiFetch(`/schedules/scans/${schedule.id}`, { method: "DELETE" });
      if (res?.error) { toast(res.error, "err"); return; }
      toast("Programación de scan eliminada", "ok");
      await load();
    };

    const runNow = async (schedule) => {
      setRunningNow(true);
      const res = await apiFetch("/schedules/run-now", { method: "POST", body: { id: schedule.id } });
      setRunningNow(false);
      if (res?.error) { toast(res.error, "err"); return; }
      toast(`Programación "${schedule.name}" ejecutada ahora`, "ok");
      await load();
    };

    const runScanNow = async (schedule) => {
      setRunningScanNow(true);
      const res = await apiFetch("/schedules/scans/run-now", { method: "POST", body: { id: schedule.id } });
      setRunningScanNow(false);
      if (res?.error) { toast(res.error, "err"); return; }
      toast(`Scan programado "${schedule.name}" ejecutado ahora`, "ok");
      await load();
    };

    const toggleServer = (serverId) => {
      setForm((prev) => {
        const ids = prev.server_ids.includes(serverId)
          ? prev.server_ids.filter((id) => id !== serverId)
          : [...prev.server_ids, serverId];
        return { ...prev, server_ids: ids };
      });
    };

    const toggleAllServers = () => {
      setForm((prev) => ({
        ...prev,
        server_ids: allServersSelected ? [] : [...allServerIds],
      }));
    };

    const toggleScanServer = (serverId) => {
      setScanForm((prev) => {
        const ids = prev.server_ids.includes(serverId)
          ? prev.server_ids.filter((id) => id !== serverId)
          : [...prev.server_ids, serverId];
        return { ...prev, server_ids: ids };
      });
    };

    const toggleAllScanServers = () => {
      setScanForm((prev) => ({
        ...prev,
        server_ids: allScanServersSelected ? [] : [...allServerIds],
      }));
    };

    const toggleActionPreset = (presetId) => {
      setForm((prev) => {
        const nextIds = prev.post_action_preset_ids.includes(presetId)
          ? prev.post_action_preset_ids.filter((id) => id !== presetId)
          : [...prev.post_action_preset_ids, presetId];
        return { ...prev, post_action_preset_ids: nextIds };
      });
    };

    const STATUS_COLOR = {
      running: "#00e5ff",
      idle: "#22d3a5",
      stopped: "#64748b",
    };

    return (
      <div style={{ animation: "fadeIn .3s ease" }}>
        {formOpen && (
          <div className="modal-overlay">
            <div className="modal modal-lg">
              <div className="modal-head">
                <span>{editTarget ? "✏ Editar programación" : "➕ Nueva programación"}</span>
                <button onClick={() => setFormOpen(false)} className="btn btn-ghost btn-sm">✕</button>
              </div>
              <div className="modal-body">
                <div className="grid-2" style={{ marginBottom: 12 }}>
                  <div className="field">
                    <label>Nombre</label>
                    <input
                      value={form.name}
                      onChange={(e) => setForm((prev) => ({ ...prev, name: e.target.value }))}
                      className="input"
                      placeholder="Deploy nocturno"
                    />
                  </div>
                  <div className="field">
                    <label>Branch</label>
                    <select
                      value={form.branch}
                      onChange={(e) => setForm((prev) => ({ ...prev, branch: e.target.value }))}
                      className="input select"
                    >
                      <option value="main">main</option>
                      <option value="production">production</option>
                      <option value="release">release</option>
                    </select>
                  </div>
                </div>

                <div
                  style={{
                    border: "1px solid #1e2a3a",
                    borderRadius: 12,
                    background: "#10161d",
                    padding: 14,
                    marginBottom: 14,
                  }}
                >
                  <div className="grid-2" style={{ marginBottom: 12 }}>
                    <div className="field" style={{ marginBottom: 0 }}>
                      <label>Frecuencia</label>
                      <select
                        value={form.frequency}
                        onChange={(e) => setForm((prev) => ({ ...prev, frequency: e.target.value }))}
                        className="input select"
                      >
                        {FREQUENCY_OPTIONS.map((option) => (
                          <option key={option.value} value={option.value}>{option.label}</option>
                        ))}
                      </select>
                    </div>

                    {form.frequency === "one_time" && (
                      <div className="field" style={{ marginBottom: 0 }}>
                        <label>Fecha y hora</label>
                        <input
                          type="datetime-local"
                          value={form.run_once_at}
                          onChange={(e) => setForm((prev) => ({ ...prev, run_once_at: e.target.value }))}
                          className="input"
                        />
                      </div>
                    )}

                    {form.frequency === "interval" && (
                      <div className="field" style={{ marginBottom: 0 }}>
                        <label>Intervalo</label>
                        <select
                          value={form.interval_minutes}
                          onChange={(e) => setForm((prev) => ({ ...prev, interval_minutes: Number(e.target.value) }))}
                          className="input select"
                        >
                          {[5, 10, 15, 20, 30, 45, 59].map((value) => (
                            <option key={value} value={value}>Cada {value} minutos</option>
                          ))}
                        </select>
                      </div>
                    )}

                    {form.frequency === "hourly" && (
                      <div className="field" style={{ marginBottom: 0 }}>
                        <label>Minuto de ejecución</label>
                        <select
                          value={form.minute}
                          onChange={(e) => setForm((prev) => ({ ...prev, minute: Number(e.target.value) }))}
                          className="input select"
                        >
                          {Array.from({ length: 60 }, (_, value) => (
                            <option key={value} value={value}>Minuto {pad2(value)}</option>
                          ))}
                        </select>
                      </div>
                    )}

                    {["daily", "weekly", "monthly", "yearly"].includes(form.frequency) && (
                      <div className="field" style={{ marginBottom: 0 }}>
                        <label>Hora</label>
                        <select
                          value={form.hour}
                          onChange={(e) => setForm((prev) => ({ ...prev, hour: Number(e.target.value) }))}
                          className="input select"
                        >
                          {Array.from({ length: 24 }, (_, value) => (
                            <option key={value} value={value}>{pad2(value)}:00</option>
                          ))}
                        </select>
                      </div>
                    )}

                    {["daily", "weekly", "monthly", "yearly"].includes(form.frequency) && (
                      <div className="field" style={{ marginBottom: 0 }}>
                        <label>Minuto</label>
                        <select
                          value={form.minute}
                          onChange={(e) => setForm((prev) => ({ ...prev, minute: Number(e.target.value) }))}
                          className="input select"
                        >
                          {Array.from({ length: 60 }, (_, value) => (
                            <option key={value} value={value}>{pad2(value)}</option>
                          ))}
                        </select>
                      </div>
                    )}

                    {form.frequency === "weekly" && (
                      <div className="field" style={{ marginBottom: 0 }}>
                        <label>Día de la semana</label>
                        <select
                          value={form.weekday}
                          onChange={(e) => setForm((prev) => ({ ...prev, weekday: Number(e.target.value) }))}
                          className="input select"
                        >
                          {WEEKDAYS.map((option) => (
                            <option key={option.value} value={option.value}>{option.label}</option>
                          ))}
                        </select>
                      </div>
                    )}

                    {["monthly", "yearly"].includes(form.frequency) && (
                      <div className="field" style={{ marginBottom: 0 }}>
                        <label>Día del mes</label>
                        <select
                          value={form.day_of_month}
                          onChange={(e) => setForm((prev) => ({ ...prev, day_of_month: Number(e.target.value) }))}
                          className="input select"
                        >
                          {dayOptions.map((value) => (
                            <option key={value} value={value}>Día {value}</option>
                          ))}
                        </select>
                      </div>
                    )}

                    {form.frequency === "yearly" && (
                      <div className="field" style={{ marginBottom: 0 }}>
                        <label>Mes</label>
                        <select
                          value={form.month}
                          onChange={(e) => setForm((prev) => ({ ...prev, month: Number(e.target.value) }))}
                          className="input select"
                        >
                          {MONTHS.map((option) => (
                            <option key={option.value} value={option.value}>{option.label}</option>
                          ))}
                        </select>
                      </div>
                    )}

                    {form.frequency === "custom" && (
                      <div className="field" style={{ gridColumn: "1 / -1", marginBottom: 0 }}>
                        <label>Cron personalizado</label>
                        <input
                          value={form.custom_cron}
                          onChange={(e) => setForm((prev) => ({ ...prev, custom_cron: e.target.value }))}
                          className="input"
                          placeholder="0 3 * * *"
                          style={{ fontFamily: "monospace" }}
                        />
                        <div style={{ fontSize: 11, color: "#64748b", marginTop: 6 }}>
                          Usa esto solo si necesitas una regla avanzada. Formato: minuto hora día mes día-semana.
                        </div>
                      </div>
                    )}
                  </div>

                  <div
                    style={{
                      border: "1px solid #00e5ff22",
                      background: "#00e5ff0d",
                      borderRadius: 10,
                      padding: 12,
                    }}
                  >
                    <div style={{ fontSize: 11, color: "#9fb4c8", marginBottom: 4 }}>Resumen de ejecución</div>
                    <div style={{ fontSize: 12, color: "#e2e8f0", marginBottom: 6 }}>{scheduleSummary}</div>
                    <div style={{ fontSize: 11, color: "#64748b" }}>
                      {form.frequency === "one_time"
                        ? <>Fecha única: <code style={{ color: "#94a3b8" }}>{computedRunOnceAt || "—"}</code></>
                        : <>Cron generado: <code style={{ color: "#94a3b8" }}>{computedCronExpression || "—"}</code></>}
                    </div>
                  </div>
                </div>

                <div
                  style={{
                    border: "1px solid #1e2a3a",
                    borderRadius: 12,
                    background: "#10161d",
                    padding: 14,
                    marginBottom: 14,
                  }}
                >
                  <div style={{ display: "flex", justifyContent: "space-between", gap: 12, alignItems: "center", flexWrap: "wrap", marginBottom: 10 }}>
                    <div>
                      <label style={{ marginBottom: 2 }}>Acciones post-deploy</label>
                      <div style={{ fontSize: 11, color: "#64748b" }}>
                        Selecciona presets para ejecutarlos automáticamente cuando termine el deploy.
                      </div>
                    </div>
                    <div style={{ fontSize: 11, color: "#94a3b8" }}>
                      {form.post_action_preset_ids.length} seleccionada{form.post_action_preset_ids.length !== 1 ? "s" : ""}
                    </div>
                  </div>

                  {actionPresets.length === 0 ? (
                    <div className="alert alert-info">
                      No hay presets de acciones guardados. Créelos primero en la pestaña `Acciones`.
                    </div>
                  ) : (
                    <div style={{ display: "flex", flexDirection: "column", gap: 8, maxHeight: 220, overflowY: "auto" }}>
                      {actionPresets.map((preset) => {
                        const selected = form.post_action_preset_ids.includes(preset.id);
                        return (
                          <label
                            key={preset.id}
                            style={{
                              display: "flex",
                              alignItems: "flex-start",
                              gap: 10,
                              padding: "10px 12px",
                              borderRadius: 8,
                              border: `1px solid ${selected ? "#22d3a544" : "#1e2a3a"}`,
                              background: selected ? "#22d3a508" : "#131820",
                              cursor: "pointer",
                            }}
                          >
                            <input
                              type="checkbox"
                              checked={selected}
                              onChange={() => toggleActionPreset(preset.id)}
                            />
                            <div>
                              <div style={{ fontSize: 12, fontWeight: 700 }}>{preset.name}</div>
                              <div style={{ fontSize: 11, color: "#9fb4c8", marginTop: 2 }}>
                                {(preset.method || "GET")} {preset.route_path} · timeout {Math.max(1, Math.round((preset.timeout_ms || 15000) / 1000))}s
                              </div>
                              {preset.action_label && (
                                <div style={{ fontSize: 10, color: "#64748b", marginTop: 2 }}>
                                  Etiqueta: {preset.action_label}
                                </div>
                              )}
                            </div>
                          </label>
                        );
                      })}
                    </div>
                  )}

                  {selectedActionPresets.length > 0 && (
                    <div style={{ fontSize: 11, color: "#9fb4c8", marginTop: 10 }}>
                      Cola automática: {selectedActionPresets.map((preset) => preset.name).join(" → ")}
                    </div>
                  )}
                </div>

                <div className="field">
                  <div style={{ display: "flex", justifyContent: "space-between", gap: 10, alignItems: "center", marginBottom: 6, flexWrap: "wrap" }}>
                    <label style={{ marginBottom: 0 }}>Servidores</label>
                    <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
                      <span style={{ fontSize: 11, color: "#64748b" }}>
                        {form.server_ids.length} seleccionados de {servers.length}
                      </span>
                      <button onClick={toggleAllServers} type="button" className="btn btn-ghost btn-sm">
                        {allServersSelected ? "Quitar todos" : "Seleccionar todos"}
                      </button>
                    </div>
                  </div>
                  <div style={{ display: "flex", flexDirection: "column", gap: 6, maxHeight: 220, overflowY: "auto" }}>
                    {servers.map((server) => {
                      const selected = form.server_ids.includes(server.id);
                      return (
                        <label
                          key={server.id}
                          style={{
                            display: "flex",
                            alignItems: "center",
                            gap: 10,
                            padding: "8px 12px",
                            borderRadius: 8,
                            border: `1px solid ${selected ? "#00e5ff44" : "#1e2a3a"}`,
                            background: selected ? "#00e5ff08" : "#131820",
                            cursor: "pointer",
                          }}
                        >
                          <input
                            type="checkbox"
                            checked={selected}
                            onChange={() => toggleServer(server.id)}
                          />
                          <div>
                            <div style={{ fontSize: 12, fontWeight: 700 }}>{server.label}</div>
                            <div style={{ fontSize: 10, color: "#64748b" }}>
                              {server.username}@{server.host}:{server.port}
                            </div>
                          </div>
                        </label>
                      );
                    })}
                  </div>
                </div>

                <div className="grid-2" style={{ marginBottom: 12 }}>
                  <label style={{ display: "flex", alignItems: "center", gap: 8, cursor: "pointer" }}>
                    <input
                      type="checkbox"
                      checked={form.dry_run}
                      onChange={(e) => setForm((prev) => ({ ...prev, dry_run: e.target.checked }))}
                    />
                    <span style={{ fontSize: 12 }}>Dry Run (solo simulación)</span>
                  </label>
                  <label style={{ display: "flex", alignItems: "center", gap: 8, cursor: "pointer" }}>
                    <input
                      type="checkbox"
                      checked={form.requires_approval}
                      onChange={(e) => setForm((prev) => ({ ...prev, requires_approval: e.target.checked }))}
                    />
                    <span style={{ fontSize: 12 }}>Requiere aprobación</span>
                  </label>
                  <label style={{ display: "flex", alignItems: "center", gap: 8, cursor: "pointer" }}>
                    <input
                      type="checkbox"
                      checked={form.validate_after_deploy}
                      onChange={(e) => setForm((prev) => ({
                        ...prev,
                        validate_after_deploy: e.target.checked,
                        auto_rollback: e.target.checked ? prev.auto_rollback : false,
                      }))}
                    />
                    <span style={{ fontSize: 12 }}>Validación post-deploy</span>
                  </label>
                  <label style={{ display: "flex", alignItems: "center", gap: 8, cursor: "pointer", opacity: form.validate_after_deploy ? 1 : 0.5 }}>
                    <input
                      type="checkbox"
                      checked={form.auto_rollback}
                      disabled={!form.validate_after_deploy}
                      onChange={(e) => setForm((prev) => ({ ...prev, auto_rollback: e.target.checked }))}
                    />
                    <span style={{ fontSize: 12 }}>Rollback automático</span>
                  </label>
                </div>

                {form.validate_after_deploy && (
                  <div className="field">
                    <label>Espera antes de validar (ms)</label>
                    <input
                      type="number"
                      min="2000"
                      max="60000"
                      step="1000"
                      value={form.validation_delay_ms}
                      onChange={(e) => setForm((prev) => ({ ...prev, validation_delay_ms: Number(e.target.value) }))}
                      className="input"
                    />
                  </div>
                )}
              </div>
              <div className="modal-foot btn-row">
                <button onClick={() => setFormOpen(false)} className="btn btn-secondary">Cancelar</button>
                <button onClick={saveForm} disabled={saving} className="btn btn-primary" style={{ opacity: saving ? 0.5 : 1 }}>
                  {saving ? "Guardando..." : editTarget ? "Actualizar" : "Crear"}
                </button>
              </div>
            </div>
          </div>
        )}

        {scanFormOpen && (
          <div className="modal-overlay">
            <div className="modal modal-lg">
              <div className="modal-head">
                <span>{scanEditTarget ? "✏ Editar scan programado" : "➕ Nuevo scan programado"}</span>
                <button onClick={() => setScanFormOpen(false)} className="btn btn-ghost btn-sm">✕</button>
              </div>
              <div className="modal-body">
                <div className="grid-2" style={{ marginBottom: 12 }}>
                  <div className="field">
                    <label>Nombre</label>
                    <input
                      value={scanForm.name}
                      onChange={(e) => setScanForm((prev) => ({ ...prev, name: e.target.value }))}
                      className="input"
                      placeholder="Scan nocturno de servidores"
                    />
                  </div>
                  <div className="field">
                    <label>Branch para auto clonar</label>
                    <input
                      value={scanForm.gh_branch}
                      onChange={(e) => setScanForm((prev) => ({ ...prev, gh_branch: e.target.value }))}
                      className="input"
                      placeholder="Vacío = usar branch guardada"
                    />
                  </div>
                </div>

                <div
                  style={{
                    border: "1px solid #1e2a3a",
                    borderRadius: 12,
                    background: "#10161d",
                    padding: 14,
                    marginBottom: 14,
                  }}
                >
                  <div className="grid-2" style={{ marginBottom: 12 }}>
                    <div className="field" style={{ marginBottom: 0 }}>
                      <label>Frecuencia</label>
                      <select
                        value={scanForm.frequency}
                        onChange={(e) => setScanForm((prev) => ({ ...prev, frequency: e.target.value }))}
                        className="input select"
                      >
                        {FREQUENCY_OPTIONS.map((option) => (
                          <option key={option.value} value={option.value}>{option.label}</option>
                        ))}
                      </select>
                    </div>

                    {scanForm.frequency === "one_time" && (
                      <div className="field" style={{ marginBottom: 0 }}>
                        <label>Fecha y hora</label>
                        <input
                          type="datetime-local"
                          value={scanForm.run_once_at}
                          onChange={(e) => setScanForm((prev) => ({ ...prev, run_once_at: e.target.value }))}
                          className="input"
                        />
                      </div>
                    )}

                    {scanForm.frequency === "interval" && (
                      <div className="field" style={{ marginBottom: 0 }}>
                        <label>Intervalo</label>
                        <select
                          value={scanForm.interval_minutes}
                          onChange={(e) => setScanForm((prev) => ({ ...prev, interval_minutes: Number(e.target.value) }))}
                          className="input select"
                        >
                          {[5, 10, 15, 20, 30, 45, 59].map((value) => (
                            <option key={value} value={value}>Cada {value} minutos</option>
                          ))}
                        </select>
                      </div>
                    )}

                    {scanForm.frequency === "hourly" && (
                      <div className="field" style={{ marginBottom: 0 }}>
                        <label>Minuto de ejecución</label>
                        <select
                          value={scanForm.minute}
                          onChange={(e) => setScanForm((prev) => ({ ...prev, minute: Number(e.target.value) }))}
                          className="input select"
                        >
                          {Array.from({ length: 60 }, (_, value) => (
                            <option key={value} value={value}>Minuto {pad2(value)}</option>
                          ))}
                        </select>
                      </div>
                    )}

                    {["daily", "weekly", "monthly", "yearly"].includes(scanForm.frequency) && (
                      <div className="field" style={{ marginBottom: 0 }}>
                        <label>Hora</label>
                        <select
                          value={scanForm.hour}
                          onChange={(e) => setScanForm((prev) => ({ ...prev, hour: Number(e.target.value) }))}
                          className="input select"
                        >
                          {Array.from({ length: 24 }, (_, value) => (
                            <option key={value} value={value}>{pad2(value)}:00</option>
                          ))}
                        </select>
                      </div>
                    )}

                    {["daily", "weekly", "monthly", "yearly"].includes(scanForm.frequency) && (
                      <div className="field" style={{ marginBottom: 0 }}>
                        <label>Minuto</label>
                        <select
                          value={scanForm.minute}
                          onChange={(e) => setScanForm((prev) => ({ ...prev, minute: Number(e.target.value) }))}
                          className="input select"
                        >
                          {Array.from({ length: 60 }, (_, value) => (
                            <option key={value} value={value}>{pad2(value)}</option>
                          ))}
                        </select>
                      </div>
                    )}

                    {scanForm.frequency === "weekly" && (
                      <div className="field" style={{ marginBottom: 0 }}>
                        <label>Día de la semana</label>
                        <select
                          value={scanForm.weekday}
                          onChange={(e) => setScanForm((prev) => ({ ...prev, weekday: Number(e.target.value) }))}
                          className="input select"
                        >
                          {WEEKDAYS.map((option) => (
                            <option key={option.value} value={option.value}>{option.label}</option>
                          ))}
                        </select>
                      </div>
                    )}

                    {["monthly", "yearly"].includes(scanForm.frequency) && (
                      <div className="field" style={{ marginBottom: 0 }}>
                        <label>Día del mes</label>
                        <select
                          value={scanForm.day_of_month}
                          onChange={(e) => setScanForm((prev) => ({ ...prev, day_of_month: Number(e.target.value) }))}
                          className="input select"
                        >
                          {scanDayOptions.map((value) => (
                            <option key={value} value={value}>Día {value}</option>
                          ))}
                        </select>
                      </div>
                    )}

                    {scanForm.frequency === "yearly" && (
                      <div className="field" style={{ marginBottom: 0 }}>
                        <label>Mes</label>
                        <select
                          value={scanForm.month}
                          onChange={(e) => setScanForm((prev) => ({ ...prev, month: Number(e.target.value) }))}
                          className="input select"
                        >
                          {MONTHS.map((option) => (
                            <option key={option.value} value={option.value}>{option.label}</option>
                          ))}
                        </select>
                      </div>
                    )}

                    {scanForm.frequency === "custom" && (
                      <div className="field" style={{ gridColumn: "1 / -1", marginBottom: 0 }}>
                        <label>Cron personalizado</label>
                        <input
                          value={scanForm.custom_cron}
                          onChange={(e) => setScanForm((prev) => ({ ...prev, custom_cron: e.target.value }))}
                          className="input"
                          placeholder="0 3 * * *"
                          style={{ fontFamily: "monospace" }}
                        />
                        <div style={{ fontSize: 11, color: "#64748b", marginTop: 6 }}>
                          Usa esto solo si necesitas una regla avanzada. Formato: minuto hora día mes día-semana.
                        </div>
                      </div>
                    )}
                  </div>

                  <div
                    style={{
                      border: "1px solid #00e5ff22",
                      background: "#00e5ff0d",
                      borderRadius: 10,
                      padding: 12,
                    }}
                  >
                    <div style={{ fontSize: 11, color: "#9fb4c8", marginBottom: 4 }}>Resumen de ejecución</div>
                    <div style={{ fontSize: 12, color: "#e2e8f0", marginBottom: 6 }}>{scanScheduleSummary}</div>
                    <div style={{ fontSize: 11, color: "#64748b" }}>
                      {scanForm.frequency === "one_time"
                        ? <>Fecha única: <code style={{ color: "#94a3b8" }}>{computedScanRunOnceAt || "—"}</code></>
                        : <>Cron generado: <code style={{ color: "#94a3b8" }}>{computedScanCronExpression || "—"}</code></>}
                    </div>
                  </div>
                </div>

                <div className="field">
                  <div style={{ display: "flex", justifyContent: "space-between", gap: 10, alignItems: "center", marginBottom: 6, flexWrap: "wrap" }}>
                    <label style={{ marginBottom: 0 }}>Servidores</label>
                    <div style={{ display: "flex", gap: 8, alignItems: "center", flexWrap: "wrap" }}>
                      <span style={{ fontSize: 11, color: "#64748b" }}>
                        {scanForm.server_ids.length} seleccionados de {servers.length}
                      </span>
                      <button onClick={toggleAllScanServers} type="button" className="btn btn-ghost btn-sm">
                        {allScanServersSelected ? "Quitar todos" : "Seleccionar todos"}
                      </button>
                    </div>
                  </div>
                  <div style={{ display: "flex", flexDirection: "column", gap: 6, maxHeight: 220, overflowY: "auto" }}>
                    {servers.map((server) => {
                      const selected = scanForm.server_ids.includes(server.id);
                      return (
                        <label
                          key={server.id}
                          style={{
                            display: "flex",
                            alignItems: "center",
                            gap: 10,
                            padding: "8px 12px",
                            borderRadius: 8,
                            border: `1px solid ${selected ? "#00e5ff44" : "#1e2a3a"}`,
                            background: selected ? "#00e5ff08" : "#131820",
                            cursor: "pointer",
                          }}
                        >
                          <input
                            type="checkbox"
                            checked={selected}
                            onChange={() => toggleScanServer(server.id)}
                          />
                          <div>
                            <div style={{ fontSize: 12, fontWeight: 700 }}>{server.label}</div>
                            <div style={{ fontSize: 10, color: "#64748b" }}>
                              {server.username}@{server.host}:{server.port}
                            </div>
                          </div>
                        </label>
                      );
                    })}
                  </div>
                </div>

                <div className="grid-2" style={{ marginBottom: 12 }}>
                  <label style={{ display: "flex", alignItems: "center", gap: 8, cursor: "pointer" }}>
                    <input
                      type="checkbox"
                      checked={scanForm.auto_init_git}
                      onChange={(e) => setScanForm((prev) => ({ ...prev, auto_init_git: e.target.checked }))}
                    />
                    <span style={{ fontSize: 12 }}>Auto clonar repo si falta</span>
                  </label>
                </div>

                <div className="alert alert-info" style={{ marginBottom: 0 }}>
                  Este scan usará la contraseña SSH guardada y, si dejas la branch vacía, tomará la branch GitHub configurada en Ajustes.
                </div>
              </div>
              <div className="modal-foot btn-row">
                <button onClick={() => setScanFormOpen(false)} className="btn btn-secondary">Cancelar</button>
                <button onClick={saveScanForm} disabled={savingScan} className="btn btn-primary" style={{ opacity: savingScan ? 0.5 : 1 }}>
                  {savingScan ? "Guardando..." : scanEditTarget ? "Actualizar" : "Crear"}
                </button>
              </div>
            </div>
          </div>
        )}

        <SchedulesTabs
          activeTab={activeTab}
          onChange={setActiveTab}
          deployTotal={schedules.length}
          scanTotal={scanSchedules.length}
          historyTotal={deployHistoryTotal + scanHistoryTotal}
        />

        {activeTab === "activos" && status && (
          <div className="card" style={{ marginBottom: 16 }}>
            <div className="card-head">⏱ Scheduler</div>
            <div className="card-body" style={{ display: "flex", gap: 12, flexWrap: "wrap", alignItems: "center" }}>
              <span
                className="tag"
                style={{
                  borderColor: `${STATUS_COLOR[status.status] || "#64748b"}44`,
                  color: STATUS_COLOR[status.status] || "#64748b",
                  background: `${STATUS_COLOR[status.status] || "#64748b"}15`,
                }}
              >
                {status.status || "—"}
              </span>
              {status.started_at && (
                <span style={{ fontSize: 11, color: "#64748b" }}>
                  Iniciado: {fmtDate(status.started_at)}
                </span>
              )}
              {status.last_run_at && (
                <span style={{ fontSize: 11, color: "#64748b" }}>
                  Último tick: {fmtDate(status.last_run_at)}
                </span>
              )}
              {status.last_triggered && (
                <span style={{ fontSize: 11, color: "#64748b" }}>
                  Ejecuciones lanzadas: <strong style={{ color: "#e2e8f0" }}>{status.last_triggered}</strong>
                </span>
              )}
              <span style={{ fontSize: 11, color: "#64748b" }}>
                Deploys activos: <strong style={{ color: "#e2e8f0" }}>{status.active_deploy_schedules || 0}</strong>
              </span>
              <span style={{ fontSize: 11, color: "#64748b" }}>
                Scans activos: <strong style={{ color: "#e2e8f0" }}>{status.active_scan_schedules || 0}</strong>
              </span>
              {status.last_error && (
                <span style={{ fontSize: 11, color: "#f43f5e" }}>
                  Error: {status.last_error}
                </span>
              )}
            </div>
          </div>
        )}

        {activeTab === "activos" && (
        <div className="card">
          <div className="card-head">
            <span>🗓 Deploys Programados</span>
            {canWrite && (
              <button onClick={() => openForm()} className="btn btn-primary btn-sm">
                + Nueva
              </button>
            )}
          </div>
          <div className="card-body">
            {loading && <div style={{ fontSize: 12, color: "#64748b" }}>Cargando...</div>}
            {!loading && schedules.length === 0 && (
              <div className="alert alert-info">
                No hay programaciones configuradas.
                {canWrite && " Usa el botón «+ Nueva» para crear una."}
              </div>
            )}
            {!loading && schedules.length > 0 && (() => {
              const sortedSchedules = [...schedules].sort((a, b) => {
                const ta = new Date(a.created_at || 0).getTime() || 0;
                const tb = new Date(b.created_at || 0).getTime() || 0;
                if (tb !== ta) return tb - ta;
                return (b.id || 0) - (a.id || 0);
              });
              const totalPages = Math.max(1, Math.ceil(sortedSchedules.length / SCHEDULE_PAGE_SIZE));
              const safePage = Math.min(schedulePage, totalPages);
              const pagedSchedules = sortedSchedules.slice((safePage - 1) * SCHEDULE_PAGE_SIZE, safePage * SCHEDULE_PAGE_SIZE);
              return (
              <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
                {pagedSchedules.map((schedule) => {
                  let serverIds = Array.isArray(schedule.server_ids) ? schedule.server_ids : [];
                  if (!serverIds.length) {
                    try { serverIds = JSON.parse(schedule.server_ids_json || "[]"); } catch (_) {}
                  }
                  const targetsAllServers = servers.length > 0 && serverIds.length === servers.length;
                  const isOneTime = schedule.schedule_type === "one_time" || !!schedule.run_once_at;
                  const postActionCount = Array.isArray(schedule.post_action_preset_ids) ? schedule.post_action_preset_ids.length : 0;

                  return (
                    <div
                      key={schedule.id}
                      style={{
                        background: "#131820",
                        border: `1px solid ${schedule.enabled ? "#1e2a3a" : "#1e2a3a55"}`,
                        borderRadius: 10,
                        padding: "12px 14px",
                        opacity: schedule.enabled ? 1 : 0.65,
                      }}
                    >
                      <div style={{ display: "flex", justifyContent: "space-between", gap: 10, flexWrap: "wrap", alignItems: "flex-start" }}>
                        <div style={{ flex: 1, minWidth: 180 }}>
                          <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 4, flexWrap: "wrap" }}>
                            <span style={{ fontWeight: 700, fontSize: 13 }}>{schedule.name}</span>
                            <span
                              className="tag"
                              style={{
                                borderColor: schedule.enabled ? "#22d3a544" : "#64748b44",
                                color: schedule.enabled ? "#22d3a5" : "#64748b",
                                background: schedule.enabled ? "#22d3a515" : "#64748b15",
                                fontSize: 9,
                              }}
                            >
                              {schedule.enabled ? "activo" : "pausado"}
                            </span>
                            {schedule.dry_run && (
                              <span className="tag" style={{ borderColor: "#fbbf2444", color: "#fbbf24", background: "#fbbf2415", fontSize: 9 }}>
                                dry run
                              </span>
                            )}
                            {schedule.requires_approval && (
                              <span className="tag" style={{ borderColor: "#a78bfa44", color: "#a78bfa", background: "#a78bfa15", fontSize: 9 }}>
                                aprobación
                              </span>
                            )}
                            {isOneTime && (
                              <span className="tag" style={{ borderColor: "#f9731644", color: "#f97316", background: "#f9731615", fontSize: 9 }}>
                                una sola vez
                              </span>
                            )}
                            {targetsAllServers && (
                              <span className="tag" style={{ borderColor: "#00e5ff44", color: "#00e5ff", background: "#00e5ff15", fontSize: 9 }}>
                                todos los servidores
                              </span>
                            )}
                            {postActionCount > 0 && (
                              <span className="tag" style={{ borderColor: "#22d3a544", color: "#22d3a5", background: "#22d3a515", fontSize: 9 }}>
                                {postActionCount} acción{postActionCount !== 1 ? "es" : ""} post-deploy
                              </span>
                            )}
                          </div>
                          <div style={{ display: "flex", gap: 8, flexWrap: "wrap", fontSize: 11, color: "#64748b" }}>
                            {isOneTime ? (
                              <code style={{ color: "#94a3b8", background: "#0f141b", padding: "2px 6px", borderRadius: 4 }}>
                                {schedule.run_once_at || "fecha única"}
                              </code>
                            ) : (
                              <code style={{ color: "#94a3b8", background: "#0f141b", padding: "2px 6px", borderRadius: 4 }}>
                                {schedule.cron_expression}
                              </code>
                            )}
                            <span>branch: <strong style={{ color: "#e2e8f0" }}>{schedule.branch}</strong></span>
                            <span>{targetsAllServers ? "Todos los servidores" : `${serverIds.length} servidor${serverIds.length !== 1 ? "es" : ""}`}</span>
                          </div>
                          {isOneTime && schedule.run_once_at && (
                            <div style={{ fontSize: 11, color: "#9fb4c8", marginTop: 6 }}>
                              Se ejecuta una sola vez el {fmtFullDate(schedule.run_once_at)}
                            </div>
                          )}
                          {schedule.cron_human && (
                            <div style={{ fontSize: 11, color: "#9fb4c8", marginTop: 6 }}>
                              {schedule.cron_human}
                            </div>
                          )}
                          {postActionCount > 0 && (
                            <div style={{ fontSize: 11, color: "#9fb4c8", marginTop: 6 }}>
                              Ejecutará automáticamente la cola de acciones HTTP al terminar el deploy.
                            </div>
                          )}
                          <div style={{ display: "flex", gap: 10, flexWrap: "wrap", fontSize: 11, color: "#64748b", marginTop: 6, alignItems: "center" }}>
                            {schedule.next_run_at && (
                              <span>Próxima: <strong style={{ color: "#00e5ff" }}>{fmtFullDate(schedule.next_run_at)}</strong></span>
                            )}
                            {schedule.last_run_at && (
                              <span>Última: {fmtDate(schedule.last_run_at)}</span>
                            )}
                            {schedule.last_deploy_id && (
                              <span>Deploy: <strong style={{ color: "#94a3b8" }}>#{schedule.last_deploy_id}</strong></span>
                            )}
                            {Array.isArray(schedule.recent_runs) && schedule.recent_runs.length > 0 && (
                              <span style={{ display: "inline-flex", alignItems: "center", gap: 4 }}>
                                Últ. corridas: <RecentRunsBadges runs={schedule.recent_runs} />
                              </span>
                            )}
                          </div>
                        </div>

                        {canWrite && (
                          <div className="btn-row" style={{ flex: "0 0 auto" }}>
                            <button
                              onClick={() => runNow(schedule)}
                              disabled={runningNow}
                              className="btn btn-ghost btn-sm"
                              title="Ejecutar ahora"
                              style={{ color: "#00e5ff" }}
                            >
                              ▶ Ahora
                            </button>
                            <button
                              onClick={() => toggleEnabled(schedule)}
                              className="btn btn-ghost btn-sm"
                              title={schedule.enabled ? "Pausar" : "Activar"}
                            >
                              {schedule.enabled ? "⏸ Pausar" : "▶ Activar"}
                            </button>
                            <button
                              onClick={() => openForm(schedule)}
                              className="btn btn-ghost btn-sm"
                            >
                              ✏
                            </button>
                            {isSuperAdmin && (
                              <button
                                onClick={() => deleteSchedule(schedule)}
                                className="btn btn-ghost btn-sm"
                                style={{ color: "#f43f5e" }}
                              >
                                🗑
                              </button>
                            )}
                          </div>
                        )}
                      </div>
                    </div>
                  );
                })}
                {totalPages > 1 && (
                  <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 8, paddingTop: 4 }}>
                    <span style={{ fontSize: 11, color: "#64748b" }}>
                      {(safePage - 1) * SCHEDULE_PAGE_SIZE + 1}–{Math.min(safePage * SCHEDULE_PAGE_SIZE, schedules.length)} de {schedules.length}
                    </span>
                    <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
                      <button
                        onClick={() => setSchedulePage(Math.max(1, safePage - 1))}
                        disabled={safePage <= 1}
                        className="btn btn-secondary"
                        style={{ padding: "4px 10px", fontSize: 11, opacity: safePage <= 1 ? 0.5 : 1 }}
                      >
                        ←
                      </button>
                      <span style={{ fontSize: 11, color: "#94a3b8" }}>
                        {safePage} / {totalPages}
                      </span>
                      <button
                        onClick={() => setSchedulePage(Math.min(totalPages, safePage + 1))}
                        disabled={safePage >= totalPages}
                        className="btn btn-secondary"
                        style={{ padding: "4px 10px", fontSize: 11, opacity: safePage >= totalPages ? 0.5 : 1 }}
                      >
                        →
                      </button>
                    </div>
                  </div>
                )}
              </div>
              );
            })()}
          </div>
        </div>
        )}

        {activeTab === "activos" && (
        <div className="card" style={{ marginTop: 16 }}>
          <div className="card-head">
            <span>🔄 Scans Programados</span>
            {canWrite && (
              <button onClick={() => openScanForm()} className="btn btn-primary btn-sm">
                + Nuevo scan
              </button>
            )}
          </div>
          <div className="card-body">
            {loading && <div style={{ fontSize: 12, color: "#64748b" }}>Cargando...</div>}
            {!loading && scanSchedules.length === 0 && (
              <div className="alert alert-info">
                No hay scans programados configurados.
                {canWrite && " Usa el botón «+ Nuevo scan» para crear uno."}
              </div>
            )}
            {!loading && scanSchedules.length > 0 && (
              <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
                {scanSchedules.map((schedule) => {
                  let serverIds = Array.isArray(schedule.server_ids) ? schedule.server_ids : [];
                  if (!serverIds.length) {
                    try { serverIds = JSON.parse(schedule.server_ids_json || "[]"); } catch (_) {}
                  }
                  const targetsAllServers = servers.length > 0 && serverIds.length === servers.length;
                  const isOneTime = schedule.schedule_type === "one_time" || !!schedule.run_once_at;
                  return (
                    <div
                      key={schedule.id}
                      style={{
                        background: "#131820",
                        border: `1px solid ${schedule.enabled ? "#1e2a3a" : "#1e2a3a55"}`,
                        borderRadius: 10,
                        padding: "12px 14px",
                        opacity: schedule.enabled ? 1 : 0.65,
                      }}
                    >
                      <div style={{ display: "flex", justifyContent: "space-between", gap: 10, flexWrap: "wrap", alignItems: "flex-start" }}>
                        <div style={{ flex: 1, minWidth: 180 }}>
                          <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 4, flexWrap: "wrap" }}>
                            <span style={{ fontWeight: 700, fontSize: 13 }}>{schedule.name}</span>
                            <span
                              className="tag"
                              style={{
                                borderColor: schedule.enabled ? "#22d3a544" : "#64748b44",
                                color: schedule.enabled ? "#22d3a5" : "#64748b",
                                background: schedule.enabled ? "#22d3a515" : "#64748b15",
                                fontSize: 9,
                              }}
                            >
                              {schedule.enabled ? "activo" : "pausado"}
                            </span>
                            {isOneTime && (
                              <span className="tag" style={{ borderColor: "#f9731644", color: "#f97316", background: "#f9731615", fontSize: 9 }}>
                                una sola vez
                              </span>
                            )}
                            {targetsAllServers && (
                              <span className="tag" style={{ borderColor: "#00e5ff44", color: "#00e5ff", background: "#00e5ff15", fontSize: 9 }}>
                                todos los servidores
                              </span>
                            )}
                            {schedule.auto_init_git ? (
                              <span className="tag" style={{ borderColor: "#22d3a544", color: "#22d3a5", background: "#22d3a515", fontSize: 9 }}>
                                auto clonar repo
                              </span>
                            ) : null}
                          </div>
                          <div style={{ display: "flex", gap: 8, flexWrap: "wrap", fontSize: 11, color: "#64748b" }}>
                            {isOneTime ? (
                              <code style={{ color: "#94a3b8", background: "#0f141b", padding: "2px 6px", borderRadius: 4 }}>
                                {schedule.run_once_at || "fecha única"}
                              </code>
                            ) : (
                              <code style={{ color: "#94a3b8", background: "#0f141b", padding: "2px 6px", borderRadius: 4 }}>
                                {schedule.cron_expression}
                              </code>
                            )}
                            <span>{targetsAllServers ? "Todos los servidores" : `${serverIds.length} servidor${serverIds.length !== 1 ? "es" : ""}`}</span>
                            <span>branch: <strong style={{ color: "#e2e8f0" }}>{schedule.gh_branch || "guardada en Ajustes"}</strong></span>
                          </div>
                          {isOneTime && schedule.run_once_at && (
                            <div style={{ fontSize: 11, color: "#9fb4c8", marginTop: 6 }}>
                              Se ejecuta una sola vez el {fmtFullDate(schedule.run_once_at)}
                            </div>
                          )}
                          {schedule.cron_human && (
                            <div style={{ fontSize: 11, color: "#9fb4c8", marginTop: 6 }}>
                              {schedule.cron_human}
                            </div>
                          )}
                          <div style={{ display: "flex", gap: 10, flexWrap: "wrap", fontSize: 11, color: "#64748b", marginTop: 6, alignItems: "center" }}>
                            {schedule.next_run_at && (
                              <span>Próxima: <strong style={{ color: "#00e5ff" }}>{fmtFullDate(schedule.next_run_at)}</strong></span>
                            )}
                            {schedule.last_run_at && (
                              <span>Última: {fmtDate(schedule.last_run_at)}</span>
                            )}
                            {Array.isArray(schedule.recent_runs) && schedule.recent_runs.length > 0 && (
                              <span style={{ display: "inline-flex", alignItems: "center", gap: 4 }}>
                                Últ. corridas: <RecentRunsBadges runs={schedule.recent_runs} />
                              </span>
                            )}
                          </div>
                        </div>

                        {canWrite && (
                          <div className="btn-row" style={{ flex: "0 0 auto" }}>
                            <button
                              onClick={() => runScanNow(schedule)}
                              disabled={runningScanNow}
                              className="btn btn-ghost btn-sm"
                              title="Ejecutar ahora"
                              style={{ color: "#00e5ff" }}
                            >
                              ▶ Ahora
                            </button>
                            <button
                              onClick={() => toggleScanEnabled(schedule)}
                              className="btn btn-ghost btn-sm"
                              title={schedule.enabled ? "Pausar" : "Activar"}
                            >
                              {schedule.enabled ? "⏸ Pausar" : "▶ Activar"}
                            </button>
                            <button
                              onClick={() => openScanForm(schedule)}
                              className="btn btn-ghost btn-sm"
                            >
                              ✏
                            </button>
                            {isSuperAdmin && (
                              <button
                                onClick={() => deleteScanSchedule(schedule)}
                                className="btn btn-ghost btn-sm"
                                style={{ color: "#f43f5e" }}
                              >
                                🗑
                              </button>
                            )}
                          </div>
                        )}
                      </div>
                    </div>
                  );
                })}
              </div>
            )}
          </div>
        </div>
        )}

        {activeTab === "historial" && (
        <div className="card" style={{ marginTop: 16 }}>
          <div className="card-head">
            <span>🕘 Historial de Programaciones</span>
            {historyLoading && (
              <span style={{ fontSize: 11, color: "#64748b", marginLeft: 10 }}>cargando...</span>
            )}
          </div>
          <div className="card-body">
            {deployHistoryTotal === 0 && scanHistoryTotal === 0 && (
              <div className="alert alert-info">
                Aún no hay ejecuciones registradas de programaciones.
              </div>
            )}
            {deployHistoryTotal > 0 && (
              <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
                <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 8 }}>
                  <div style={{ fontSize: 12, fontWeight: 700, color: "#e2e8f0" }}>
                    Deploys <span style={{ color: "#64748b", fontWeight: 400 }}>({deployHistoryTotal})</span>
                  </div>
                  {(() => {
                    const totalPages = Math.max(1, Math.ceil(deployHistoryTotal / HISTORY_PAGE_SIZE));
                    if (totalPages <= 1) return null;
                    return (
                      <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
                        <button
                          onClick={() => setDeployHistoryPage((page) => Math.max(1, page - 1))}
                          disabled={deployHistoryPage <= 1 || historyLoading}
                          className="btn btn-secondary"
                          style={{ padding: "4px 10px", fontSize: 11, opacity: deployHistoryPage <= 1 ? 0.5 : 1 }}
                        >
                          ←
                        </button>
                        <span style={{ fontSize: 11, color: "#94a3b8" }}>
                          {deployHistoryPage} / {totalPages}
                        </span>
                        <button
                          onClick={() => setDeployHistoryPage((page) => Math.min(totalPages, page + 1))}
                          disabled={deployHistoryPage >= totalPages || historyLoading}
                          className="btn btn-secondary"
                          style={{ padding: "4px 10px", fontSize: 11, opacity: deployHistoryPage >= totalPages ? 0.5 : 1 }}
                        >
                          →
                        </button>
                      </div>
                    );
                  })()}
                </div>
                {scheduleRuns.map((run) => {
                  const tone =
                    run.status === "error" ? "#f43f5e"
                      : run.status === "pending_approval" ? "#a78bfa"
                        : run.deploy_id ? "#22d3a5"
                          : "#64748b";
                  return (
                    <div
                      key={run.id}
                      style={{
                        background: "#131820",
                        border: "1px solid #1e2a3a",
                        borderRadius: 10,
                        padding: "12px 14px",
                      }}
                    >
                      <div style={{ display: "flex", justifyContent: "space-between", gap: 12, flexWrap: "wrap", alignItems: "flex-start" }}>
                        <div style={{ flex: 1, minWidth: 220 }}>
                          <div style={{ display: "flex", gap: 8, flexWrap: "wrap", alignItems: "center", marginBottom: 6 }}>
                            <span style={{ fontWeight: 700, fontSize: 13 }}>{run.schedule_name}</span>
                            <span className="tag" style={{ borderColor: `${tone}44`, color: tone, background: `${tone}15`, fontSize: 9 }}>
                              {run.status}
                            </span>
                            <span className="tag" style={{ borderColor: "#64748b44", color: "#94a3b8", background: "#64748b15", fontSize: 9 }}>
                              {run.trigger === "manual" ? "manual" : "scheduler"}
                            </span>
                            {run.deploy_id && (
                              <span className="tag" style={{ borderColor: "#00e5ff44", color: "#00e5ff", background: "#00e5ff15", fontSize: 9 }}>
                                deploy #{run.deploy_id}
                              </span>
                            )}
                          </div>
                          <div style={{ fontSize: 11, color: "#64748b" }}>
                            Inicio: {fmtFullDate(run.started_at)}
                            {run.finished_at ? ` · Fin: ${fmtDate(run.finished_at)}` : ""}
                          </div>
                          {run.deploy_status && (
                            <div style={{ fontSize: 11, color: "#9fb4c8", marginTop: 6 }}>
                              Estado del deploy asociado: {run.deploy_status}
                              {run.deploy_branch ? ` · branch ${run.deploy_branch}` : ""}
                            </div>
                          )}
                          {run.error_message && (
                            <div style={{ fontSize: 11, color: "#fda4af", marginTop: 6 }}>
                              Error: {run.error_message}
                            </div>
                          )}
                        </div>
                      </div>
                    </div>
                  );
                })}
              </div>
            )}
            {scanHistoryTotal > 0 && (
              <div style={{ display: "flex", flexDirection: "column", gap: 10, marginTop: deployHistoryTotal > 0 ? 16 : 0 }}>
                <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 8 }}>
                  <div style={{ fontSize: 12, fontWeight: 700, color: "#e2e8f0" }}>
                    Scans <span style={{ color: "#64748b", fontWeight: 400 }}>({scanHistoryTotal})</span>
                  </div>
                  {(() => {
                    const totalPages = Math.max(1, Math.ceil(scanHistoryTotal / HISTORY_PAGE_SIZE));
                    if (totalPages <= 1) return null;
                    return (
                      <div style={{ display: "flex", alignItems: "center", gap: 6 }}>
                        <button
                          onClick={() => setScanHistoryPage((page) => Math.max(1, page - 1))}
                          disabled={scanHistoryPage <= 1 || historyLoading}
                          className="btn btn-secondary"
                          style={{ padding: "4px 10px", fontSize: 11, opacity: scanHistoryPage <= 1 ? 0.5 : 1 }}
                        >
                          ←
                        </button>
                        <span style={{ fontSize: 11, color: "#94a3b8" }}>
                          {scanHistoryPage} / {totalPages}
                        </span>
                        <button
                          onClick={() => setScanHistoryPage((page) => Math.min(totalPages, page + 1))}
                          disabled={scanHistoryPage >= totalPages || historyLoading}
                          className="btn btn-secondary"
                          style={{ padding: "4px 10px", fontSize: 11, opacity: scanHistoryPage >= totalPages ? 0.5 : 1 }}
                        >
                          →
                        </button>
                      </div>
                    );
                  })()}
                </div>
                {scanRuns.map((run) => {
                  const tone = run.status === "error" ? "#f43f5e" : run.status === "ok" ? "#22d3a5" : "#64748b";
                  const summary = run.summary || {};
                  return (
                    <div
                      key={`scan-${run.id}`}
                      style={{
                        background: "#131820",
                        border: "1px solid #1e2a3a",
                        borderRadius: 10,
                        padding: "12px 14px",
                      }}
                    >
                      <div style={{ display: "flex", justifyContent: "space-between", gap: 12, flexWrap: "wrap", alignItems: "flex-start" }}>
                        <div style={{ flex: 1, minWidth: 220 }}>
                          <div style={{ display: "flex", gap: 8, flexWrap: "wrap", alignItems: "center", marginBottom: 6 }}>
                            <span style={{ fontWeight: 700, fontSize: 13 }}>{run.schedule_name}</span>
                            <span className="tag" style={{ borderColor: `${tone}44`, color: tone, background: `${tone}15`, fontSize: 9 }}>
                              {run.status}
                            </span>
                            <span className="tag" style={{ borderColor: "#64748b44", color: "#94a3b8", background: "#64748b15", fontSize: 9 }}>
                              {run.trigger === "manual" ? "manual" : "scheduler"}
                            </span>
                            {summary.total_servers ? (
                              <span className="tag" style={{ borderColor: "#00e5ff44", color: "#00e5ff", background: "#00e5ff15", fontSize: 9 }}>
                                {summary.total_servers} servidor{summary.total_servers !== 1 ? "es" : ""}
                              </span>
                            ) : null}
                          </div>
                          <div style={{ fontSize: 11, color: "#64748b" }}>
                            Inicio: {fmtFullDate(run.started_at)}
                            {run.finished_at ? ` · Fin: ${fmtDate(run.finished_at)}` : ""}
                          </div>
                          {run.status === "ok" && (
                            <div style={{ fontSize: 11, color: "#9fb4c8", marginTop: 6 }}>
                              Usuarios detectados: {summary.total_users || 0}
                              {` · nuevos ${summary.total_added || 0} · retirados ${summary.total_removed || 0}`}
                            </div>
                          )}
                          {run.error_message && (
                            <div style={{ fontSize: 11, color: "#fda4af", marginTop: 6 }}>
                              Error: {run.error_message}
                            </div>
                          )}
                        </div>
                      </div>
                    </div>
                  );
                })}
              </div>
            )}
          </div>
        </div>
        )}
      </div>
    );
  }

  window.DCViews = window.DCViews || {};
  window.DCViews.SchedulesView = SchedulesView;
})();
