(() => {
  const { useState, useEffect, apiFetch, fmtDate, SC, getPlaybookCatalog, getPlaybookCategories, runKey } = window.DC;
  const { IncidentPlaybook } = window.DCComponents;

  const STATUS_TONE = {
    ok: SC.ok,
    warning: "#fbbf24",
    critical: SC.error,
    error: SC.error,
    running: SC.running,
    idle: "#64748b",
  };

  const SERVICE_LABELS = {
    apache: "Apache",
    php_fpm: "PHP-FPM",
    mariadb: "MariaDB",
  };

  const fmtAgo = (value) => {
    if (!value) return "—";
    const diffMs = Math.max(0, Date.now() - new Date(value).getTime());
    const diffMinutes = Math.floor(diffMs / 60000);

    if (diffMinutes < 1) return "hace <1 min";
    if (diffMinutes < 60) return `hace ${diffMinutes} min`;

    const diffHours = Math.floor(diffMinutes / 60);
    if (diffHours < 24) return `hace ${diffHours} h`;

    return `hace ${Math.floor(diffHours / 24)} d`;
  };

  const shortenProcessName = (raw) => {
    if (!raw) return "";
    const trimmed = String(raw).trim();
    if (!trimmed) return "";
    const lastSlash = trimmed.lastIndexOf("/");
    const tail = lastSlash >= 0 ? trimmed.slice(lastSlash + 1) : trimmed;
    return tail.length > 32 ? `${tail.slice(0, 30)}…` : tail;
  };

  const parseTopProcs = (raw) => {
    if (!raw) return [];
    return String(raw)
      .split("\n")
      .map((line) => line.trim())
      .filter(Boolean)
      .map((line) => {
        const parts = line.split(/\s+/);
        if (parts.length < 3) return null;
        const memPct = Number.parseFloat(parts[parts.length - 1]);
        const cpuPct = Number.parseFloat(parts[parts.length - 2]);
        const command = parts.slice(0, parts.length - 2).join(" ");
        if (!command || !Number.isFinite(memPct) || !Number.isFinite(cpuPct)) return null;
        return { command, short: shortenProcessName(command), cpu_pct: cpuPct, mem_pct: memPct };
      })
      .filter(Boolean);
  };

  const getResourcePressure = (server, thresholds) => {
    const t = thresholds || {};
    const cpuWarn = Number.isFinite(t.cpu_warn) ? t.cpu_warn : 90;
    const cpuCrit = Number.isFinite(t.cpu_crit) ? t.cpu_crit : 95;
    const ramWarn = Number.isFinite(t.ram_warn) ? t.ram_warn : 80;
    const ramCrit = Number.isFinite(t.ram_crit) ? t.ram_crit : 90;
    const diskWarn = Number.isFinite(t.disk_warn) ? t.disk_warn : 80;
    const diskCrit = Number.isFinite(t.disk_crit) ? t.disk_crit : 95;
    const cpu = Number(server.cpu_pct) || 0;
    const ram = Number(server.ram_pct) || 0;
    const disk = Number(server.disk_pct) || 0;

    const cpuLevel = cpu >= cpuCrit ? "critical" : cpu >= cpuWarn ? "warning" : null;
    const ramLevel = ram >= ramCrit ? "critical" : ram >= ramWarn ? "warning" : null;
    const diskLevel = disk >= diskCrit ? "critical" : disk >= diskWarn ? "warning" : null;
    return {
      cpu: cpuLevel,
      ram: ramLevel,
      disk: diskLevel,
      anyPressure: Boolean(cpuLevel || ramLevel || diskLevel),
      thresholds: { cpuWarn, cpuCrit, ramWarn, ramCrit, diskWarn, diskCrit },
    };
  };

  const parseLoadValue = (value) => {
    if (!value) return 0;
    const first = String(value).trim().split(/\s+/)[0];
    const parsed = Number.parseFloat(first);
    return Number.isFinite(parsed) ? parsed : 0;
  };

  const getServerTone = (server) => {
    if ((server.critical_incidents || 0) > 0) return SC.error;
    if ((server.down_sites || 0) > 0) return "#fb7185";
    if ((server.degraded_sites || 0) > 0 || server.monitor_status === "warning") return "#fbbf24";
    if (server.monitor_status === "critical" || server.monitor_status === "error") return SC.error;
    return STATUS_TONE[server.monitor_status] || SC.ok;
  };

  const getServiceTone = (status) => {
    const normalized = String(status || "unknown").toLowerCase();
    if (normalized === "active") return SC.ok;
    if (["activating", "reloading", "running"].includes(normalized)) return SC.running;
    if (["inactive", "failed", "dead", "error"].includes(normalized)) return SC.error;
    return "#64748b";
  };

  const buildServerHighlight = (server) => {
    if ((server.critical_incidents || 0) > 0) {
      return `${server.critical_incidents} incidente(s) crítico(s) activos`;
    }
    if ((server.down_sites || 0) > 0) {
      return `${server.down_sites} sitio(s) caído(s) o sin respuesta`;
    }
    if ((server.degraded_sites || 0) > 0) {
      return `${server.degraded_sites} sitio(s) degradado(s) detectado(s)`;
    }
    if ((server.active_incidents || 0) > 0) {
      return `${server.active_incidents} incidente(s) abierto(s)`;
    }
    return "Operación estable en el último barrido";
  };

  const getServerPriority = (server) => {
    const loadWeight = Math.round(parseLoadValue(server.load_avg) * 10);
    return ((server.critical_incidents || 0) * 1000) +
      ((server.down_sites || 0) * 120) +
      ((server.degraded_sites || 0) * 40) +
      ((server.active_incidents || 0) * 30) +
      (server.monitor_status === "critical" ? 25 : server.monitor_status === "warning" ? 10 : 0) +
      loadWeight;
  };

  function Pill({ children, tone = "#64748b" }) {
    return (
      <span
        className="tag"
        style={{
          borderColor: `${tone}55`,
          color: tone,
          background: `${tone}12`,
        }}
      >
        {children}
      </span>
    );
  }

  function ServiceChip({ name, status }) {
    const tone = getServiceTone(status);
    return (
      <div className="noc-service-pill">
        <span className="noc-service-dot" style={{ background: tone, boxShadow: `0 0 0 4px ${tone}18` }} />
        <span className="noc-service-name">{name}</span>
        <strong style={{ color: tone }}>{status || "unknown"}</strong>
      </div>
    );
  }

  function MetricCard({ label, value, tone = "#e2e8f0", meta }) {
    return (
      <div style={{ background: "#131820", border: "1px solid #1e2a3a", borderRadius: 14, padding: 16 }}>
        <div style={{ color: "#64748b", fontSize: 11, letterSpacing: "1px", textTransform: "uppercase", fontWeight: 700, marginBottom: 8 }}>
          {label}
        </div>
        <div style={{ fontFamily: "Syne,sans-serif", fontWeight: 800, fontSize: 26, color: tone, marginBottom: 6 }}>
          {value}
        </div>
        {meta && <div style={{ color: "#94a3b8", fontSize: 11, lineHeight: 1.5 }}>{meta}</div>}
      </div>
    );
  }

  function NocTabs({ tabs, activeTab, onChange }) {
    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>
    );
  }

  const NOC_TABS = [
    { key: "overview", label: "Resumen", icon: "🛡" },
    { key: "incidents", label: "Incidentes", icon: "🚨" },
    { key: "servers", label: "Servidores", icon: "🖥" },
    { key: "activity", label: "Actividad", icon: "📣" },
    { key: "guide", label: "Guía", icon: "📖" },
  ];
  const NOC_TAB_KEYS = new Set(NOC_TABS.map((tab) => tab.key));

  function PlaybookGuide() {
    const catalog = getPlaybookCatalog ? getPlaybookCatalog() : [];
    const categories = getPlaybookCategories ? getPlaybookCategories() : {};
    const [search, setSearch] = useState("");
    const [activeCategory, setActiveCategory] = useState("all");

    const normalizedSearch = search.trim().toLowerCase();
    const filtered = catalog.filter((entry) => {
      if (activeCategory !== "all" && entry.category !== activeCategory) return false;
      if (!normalizedSearch) return true;
      const haystack = [
        entry.title,
        entry.diagnosis,
        ...(entry.causes || []),
        ...(entry.manualSteps || []),
        entry.verifyAfter,
      ].filter(Boolean).join(" ").toLowerCase();
      return haystack.includes(normalizedSearch);
    });

    const counts = catalog.reduce((acc, entry) => {
      acc[entry.category] = (acc[entry.category] || 0) + 1;
      return acc;
    }, {});

    const categoryButtons = [
      { key: "all", label: "Todas", icon: "📂", tone: "#7dd3fc", count: catalog.length },
      ...Object.entries(categories).map(([key, info]) => ({
        key,
        label: info.label,
        icon: info.icon,
        tone: info.tone,
        count: counts[key] || 0,
      })),
    ];

    return (
      <div className="card">
        <div className="card-head">
          <span>📖 Guía rápida de incidentes</span>
          <span style={{ color: "#94a3b8", fontSize: 11 }}>{catalog.length} playbooks</span>
        </div>
        <div className="card-body">
          <div className="alert alert-info" style={{ marginBottom: 14 }}>
            Referencia consolidada de los incidentes que el monitor detecta. Cada tarjeta muestra el diagnóstico,
            causas frecuentes, acciones recomendadas y verificación posterior.
          </div>

          <div className="noc-guide-toolbar">
            <input
              className="input noc-guide-search"
              type="search"
              placeholder="Buscar por síntoma, causa o acción..."
              value={search}
              onChange={(event) => setSearch(event.target.value)}
            />
            <div className="noc-guide-filters">
              {categoryButtons.map((cat) => {
                const active = activeCategory === cat.key;
                return (
                  <button
                    key={cat.key}
                    type="button"
                    onClick={() => setActiveCategory(cat.key)}
                    className="noc-guide-filter"
                    style={{
                      borderColor: active ? cat.tone : "#1e2a3a",
                      background: active ? `${cat.tone}18` : "transparent",
                      color: active ? cat.tone : "#cbd5e1",
                    }}
                  >
                    <span aria-hidden="true">{cat.icon}</span>
                    <span>{cat.label}</span>
                    <span className="noc-guide-filter-count">{cat.count}</span>
                  </button>
                );
              })}
            </div>
          </div>

          {filtered.length === 0 ? (
            <div className="alert alert-warn">Sin playbooks que coincidan con el filtro actual.</div>
          ) : (
            <div className="noc-guide-grid">
              {filtered.map((entry) => {
                const cat = categories[entry.category] || { label: entry.category, tone: "#64748b", icon: "•" };
                return (
                  <div key={entry.key} className="noc-guide-card" style={{ "--guide-tone": cat.tone }}>
                    <div className="noc-guide-card-head">
                      <div className="noc-guide-card-title">
                        <span aria-hidden="true">{cat.icon}</span>
                        <span>{entry.title}</span>
                      </div>
                      <Pill tone={cat.tone}>{cat.label}</Pill>
                    </div>

                    <div className="noc-guide-section">
                      <div className="noc-guide-label">🩺 Diagnóstico</div>
                      <div className="noc-guide-body">{entry.diagnosis}</div>
                    </div>

                    {entry.causes && entry.causes.length > 0 && (
                      <div className="noc-guide-section">
                        <div className="noc-guide-label">Causas frecuentes</div>
                        <ul className="noc-guide-list">
                          {entry.causes.map((cause, index) => (
                            <li key={`cause-${index}`}>{cause}</li>
                          ))}
                        </ul>
                      </div>
                    )}

                    {entry.recommendedActions && entry.recommendedActions.length > 0 && (
                      <div className="noc-guide-section">
                        <div className="noc-guide-label">🛠 Acciones recomendadas</div>
                        <div className="noc-guide-actions">
                          {entry.recommendedActions.map((action) => (
                            <span
                              key={action.key}
                              className={`noc-guide-action${action.primary ? " noc-guide-action-primary" : ""}`}
                            >
                              {action.primary ? "★ " : ""}{action.label}
                            </span>
                          ))}
                        </div>
                      </div>
                    )}

                    {entry.manualSteps && entry.manualSteps.length > 0 && (
                      <div className="noc-guide-section">
                        <div className="noc-guide-label">Pasos manuales</div>
                        <ul className="noc-guide-list">
                          {entry.manualSteps.map((step, index) => (
                            <li key={`step-${index}`}>{step}</li>
                          ))}
                        </ul>
                      </div>
                    )}

                    {entry.verifyAfter && (
                      <div className="noc-guide-verify">
                        <span aria-hidden="true">✅</span>
                        <span>{entry.verifyAfter}</span>
                      </div>
                    )}
                  </div>
                );
              })}
            </div>
          )}
        </div>
      </div>
    );
  }

  function NocView({ toast }) {
    const [overview, setOverview] = useState(null);
    const [loading, setLoading] = useState(false);
    const [runningAction, setRunningAction] = useState("");
    const [activeTab, setActiveTab] = useState(() => {
      try {
        return window.localStorage.getItem("dc:tabs:noc") || "overview";
      } catch (_error) {
        return "overview";
      }
    });

    const loadOverview = async (silent = false) => {
      if (!silent) setLoading(true);
      try {
        const response = await apiFetch("/ops/noc");
        if (response?.error) {
          toast(response.error || "No se pudo cargar el tablero NOC", "err");
        } else if (response) {
          setOverview(response);
        }
      } catch (error) {
        toast(`No se pudo cargar el tablero NOC: ${error?.message || error}`, "err");
      } finally {
        if (!silent) setLoading(false);
      }
    };

    useEffect(() => {
      loadOverview();
      const intervalId = setInterval(() => loadOverview(true), 30000);
      return () => clearInterval(intervalId);
    }, []);

    const runIncidentAction = async (incidentId, actionKey, successMessage) => {
      setRunningAction(runKey.incident(actionKey, incidentId));
      try {
        const response = await apiFetch(`/incidents/${incidentId}/${actionKey}`, {
          method: "POST",
          body: actionKey === "resolve" ? { reason: "resolved_from_noc" } : {},
        });
        if (response?.error) toast(response.error, "err");
        else toast(successMessage);
        await loadOverview(true);
      } catch (error) {
        toast(`Error al ejecutar ${actionKey}: ${error?.message || error}`, "err");
      } finally {
        setRunningAction("");
      }
    };

    const runRunbook = async (serverId, actionKey, incidentId = null) => {
      setRunningAction(runKey.runbook(actionKey, serverId));
      try {
        const response = await apiFetch(`/ops/runbooks/${actionKey}`, {
          method: "POST",
          body: {
            server_id: serverId,
            ...(incidentId ? { incident_id: incidentId } : {}),
          },
        });
        if (response?.error) {
          toast(response.error, "err");
        } else {
          toast(`Runbook ejecutado: ${actionKey}`);
        }
        await loadOverview(true);
      } catch (error) {
        toast(`Error al ejecutar runbook ${actionKey}: ${error?.message || error}`, "err");
      } finally {
        setRunningAction("");
      }
    };

    const summary = overview?.summary || {};
    const health = overview?.health || {};
    const servers = overview?.servers || [];
    const thresholds = overview?.thresholds || {};
    const criticalIncidents = overview?.critical_incidents || [];
    const degradedSites = overview?.degraded_sites || [];
    const recentJobs = overview?.recent_jobs || [];
    const remediations = overview?.remediation_runs || [];
    const recentAlerts = overview?.recent_alerts || [];
    const backups = overview?.backups || {};
    const backupVerification = backups.verification?.latest_run || null;
    const channels = overview?.alert_channels?.configured_channels || [];
    const rankedServers = [...servers]
      .sort((a, b) => {
        const scoreDiff = getServerPriority(b) - getServerPriority(a);
        if (scoreDiff !== 0) return scoreDiff;
        return String(a.label || "").localeCompare(String(b.label || ""), "es");
      })
      .map((server) => {
        const pressure = getResourcePressure(server, thresholds);
        return {
          server,
          pressure,
          // Solo parseamos top_procs cuando hay presión — evita trabajo en cada
          // tick del polling para servidores sanos.
          topProcs: pressure.anyPressure ? parseTopProcs(server.top_procs).slice(0, 5) : [],
        };
      });
    const incidentsByServer = criticalIncidents.reduce((acc, incident) => {
      const key = incident.server_label || "global";
      if (!acc[key]) {
        acc[key] = {
          server_label: key,
          total: 0,
          critical: 0,
          acknowledged: 0,
          open: 0,
          latest_seen_at: incident.last_seen_at || null,
        };
      }
      acc[key].total += 1;
      if (incident.severity === "critical") acc[key].critical += 1;
      if (incident.status === "acknowledged") acc[key].acknowledged += 1;
      if (incident.status === "open") acc[key].open += 1;
      if (!acc[key].latest_seen_at || new Date(incident.last_seen_at).getTime() > new Date(acc[key].latest_seen_at).getTime()) {
        acc[key].latest_seen_at = incident.last_seen_at || acc[key].latest_seen_at;
      }
      return acc;
    }, {});
    const incidentServerSummary = Object.values(incidentsByServer)
      .sort((a, b) => {
        if (b.critical !== a.critical) return b.critical - a.critical;
        if (b.total !== a.total) return b.total - a.total;
        return String(a.server_label).localeCompare(String(b.server_label), "es");
      });
    const busy = Boolean(runningAction);

    useEffect(() => {
      if (!NOC_TAB_KEYS.has(activeTab)) {
        setActiveTab("overview");
      }
    }, [activeTab]);

    useEffect(() => {
      try {
        window.localStorage.setItem("dc:tabs:noc", activeTab);
      } catch (_error) {}
    }, [activeTab]);

    return (
      <div className="noc-layout">
        <div className="card">
          <div className="card-head">
            <span>🛡 Panel NOC</span>
            <div className="noc-toolbar">
              <Pill tone={STATUS_TONE[health.status] || "#64748b"}>App: {health.status || "—"}</Pill>
              <Pill tone={channels.length ? SC.ok : "#fbbf24"}>Alertas: {channels.length ? channels.join(", ") : "sin canales"}</Pill>
              <button onClick={() => loadOverview()} disabled={loading || busy} className="btn btn-ghost btn-sm">
                {loading ? "Actualizando..." : "Actualizar"}
              </button>
            </div>
          </div>
          <div className="card-body">
            <div className="alert alert-info" style={{ marginBottom: 14 }}>
              Vista unificada de incidentes, servidores, dominios degradados y acciones de remediación. Se refresca sola cada 30 segundos.
            </div>

            <div className="noc-metrics-grid">
              <MetricCard label="Servidores" value={summary.servers_total || 0} tone="#e2e8f0" meta={`${summary.degraded_servers || 0} degradados`} />
              <MetricCard label="Incidentes" value={summary.active_incidents || 0} tone={summary.critical_incidents ? SC.error : summary.active_incidents ? "#fbbf24" : SC.ok} meta={`${summary.critical_incidents || 0} críticos`} />
              <MetricCard label="Sitios degradados" value={summary.degraded_sites || 0} tone={summary.degraded_sites ? "#fbbf24" : SC.ok} meta={`${servers.reduce((acc, item) => acc + (item.down_sites || 0), 0)} caídos o timeout`} />
              <MetricCard label="Jobs activos" value={summary.running_jobs || 0} tone={summary.running_jobs ? SC.running : SC.ok} meta={`${summary.active_remediations || 0} remediaciones en curso`} />
              <MetricCard label="Backups" value={backups.total_count || 0} tone={backups.total_count ? SC.ok : "#fbbf24"} meta={backupVerification ? `Verificado ${fmtAgo(backupVerification.finished_at || backupVerification.started_at)}` : "Sin restore test"} />
            </div>
          </div>
        </div>

        <NocTabs tabs={NOC_TABS} activeTab={activeTab} onChange={setActiveTab} />

        {activeTab === "overview" && (
          <div className="noc-split-grid">
            <div className="card">
              <div className="card-head">
                <span>🚨 Incidentes críticos</span>
                <span style={{ color: "#94a3b8", fontSize: 11 }}>{criticalIncidents.length} abiertos</span>
              </div>
              <div className="card-body">
                {criticalIncidents.length === 0 ? (
                  <div className="alert alert-success">No hay incidentes críticos abiertos.</div>
                ) : (
                  <div className="noc-list">
                    {criticalIncidents.slice(0, 8).map((incident) => (
                      <div key={incident.id} className="noc-panel">
                        <div className="noc-row-head" style={{ marginBottom: 6 }}>
                          <div style={{ fontWeight: 700, color: "#e2e8f0" }}>{incident.title}</div>
                          <div className="noc-tag-row">
                            <Pill tone={incident.severity === "critical" ? SC.error : "#fbbf24"}>{incident.severity}</Pill>
                            <Pill tone={incident.status === "acknowledged" ? "#fbbf24" : "#64748b"}>{incident.status}</Pill>
                          </div>
                        </div>
                        <div style={{ color: "#cbd5e1", fontSize: 12, marginBottom: 8 }}>{incident.summary || "Sin resumen"}</div>
                        <div style={{ display: "flex", gap: 10, flexWrap: "wrap", color: "#94a3b8", fontSize: 11 }}>
                          <span>Servidor: <strong style={{ color: "#e2e8f0" }}>{incident.server_label || "global"}</strong></span>
                          <span>Última vez: <strong style={{ color: "#e2e8f0" }}>{fmtAgo(incident.last_seen_at)}</strong></span>
                        </div>
                        <IncidentPlaybook
                          incident={incident}
                          runningAction={runningAction}
                          busy={busy}
                          onRunIncidentAction={runIncidentAction}
                          onRunRunbook={runRunbook}
                        />
                      </div>
                    ))}
                  </div>
                )}
              </div>
            </div>

            <div className="card">
              <div className="card-head">
                <span>🌐 Sitios degradados</span>
                <span style={{ color: "#94a3b8", fontSize: 11 }}>{degradedSites.length} detectados</span>
              </div>
              <div className="card-body">
                {degradedSites.length === 0 ? (
                  <div className="alert alert-success">No hay dominios degradados en el último barrido.</div>
                ) : (
                  <div className="noc-list">
                    {degradedSites.slice(0, 12).map((site) => (
                      <div key={`${site.server_id}-${site.domain}`} className="noc-panel">
                        <div className="noc-row-head" style={{ alignItems: "center", marginBottom: 4 }}>
                          <div style={{ fontWeight: 700, color: "#22d3ee", wordBreak: "break-word" }}>{site.domain}</div>
                          <Pill tone={["down", "timeout", "error", "slow_critical"].includes(site.status) || site.dns_ok === 0 ? SC.error : "#fbbf24"}>
                            {site.status}
                          </Pill>
                        </div>
                        <div style={{ color: "#94a3b8", fontSize: 11, display: "flex", gap: 10, flexWrap: "wrap" }}>
                          <span>{site.server_label || "Servidor"} · {site.virt_user}</span>
                          <span>{site.response_ms || 0} ms</span>
                          <span>DNS: {site.dns_ok ? "ok" : "fail"}</span>
                          <span>SSL: {Number.isFinite(site.ssl_valid_days) ? `${site.ssl_valid_days} d` : "n/d"}</span>
                        </div>
                      </div>
                    ))}
                  </div>
                )}
              </div>
            </div>
          </div>
        )}

        {activeTab === "incidents" && (
          <div className="noc-split-grid">
            <div className="card">
              <div className="card-head">
                <span>🚨 Gestión de incidentes</span>
                <span style={{ color: "#94a3b8", fontSize: 11 }}>{criticalIncidents.length} abiertos</span>
              </div>
              <div className="card-body">
                {criticalIncidents.length === 0 ? (
                  <div className="alert alert-success">No hay incidentes críticos abiertos.</div>
                ) : (
                  <div className="noc-list">
                    {criticalIncidents.map((incident) => (
                      <div key={incident.id} className="noc-panel">
                        <div className="noc-row-head" style={{ marginBottom: 6 }}>
                          <div style={{ fontWeight: 700, color: "#e2e8f0" }}>{incident.title}</div>
                          <div className="noc-tag-row">
                            <Pill tone={incident.severity === "critical" ? SC.error : "#fbbf24"}>{incident.severity}</Pill>
                            <Pill tone={incident.status === "acknowledged" ? "#fbbf24" : "#64748b"}>{incident.status}</Pill>
                          </div>
                        </div>
                        <div style={{ color: "#cbd5e1", fontSize: 12, marginBottom: 8 }}>{incident.summary || "Sin resumen"}</div>
                        <div style={{ display: "flex", gap: 10, flexWrap: "wrap", color: "#94a3b8", fontSize: 11 }}>
                          <span>Servidor: <strong style={{ color: "#e2e8f0" }}>{incident.server_label || "global"}</strong></span>
                          <span>Última vez: <strong style={{ color: "#e2e8f0" }}>{fmtAgo(incident.last_seen_at)}</strong></span>
                        </div>
                        <IncidentPlaybook
                          incident={incident}
                          runningAction={runningAction}
                          busy={busy}
                          onRunIncidentAction={runIncidentAction}
                          onRunRunbook={runRunbook}
                        />
                      </div>
                    ))}
                  </div>
                )}
              </div>
            </div>

            <div className="noc-stack">
              <div className="card">
                <div className="card-head">
                  <span>📌 Estado operativo</span>
                  <span style={{ color: "#94a3b8", fontSize: 11 }}>lectura rápida</span>
                </div>
                <div className="card-body">
                  <div className="noc-list">
                    <div className="noc-panel">
                      <div className="noc-row-head" style={{ marginBottom: 6 }}>
                        <div style={{ color: "#e2e8f0", fontSize: 12, fontWeight: 700 }}>Incidentes abiertos</div>
                        <Pill tone={criticalIncidents.length ? SC.error : SC.ok}>{criticalIncidents.length}</Pill>
                      </div>
                      <div style={{ color: "#94a3b8", fontSize: 11 }}>
                        {criticalIncidents.filter((incident) => incident.status === "open").length} sin reconocer ·{" "}
                        {criticalIncidents.filter((incident) => incident.status === "acknowledged").length} reconocidos
                      </div>
                    </div>

                    <div className="noc-panel">
                      <div className="noc-row-head" style={{ marginBottom: 6 }}>
                        <div style={{ color: "#e2e8f0", fontSize: 12, fontWeight: 700 }}>Severidad crítica</div>
                        <Pill tone={summary.critical_incidents ? SC.error : "#64748b"}>{summary.critical_incidents || 0}</Pill>
                      </div>
                      <div style={{ color: "#94a3b8", fontSize: 11 }}>
                        {incidentServerSummary.length} servidor(es) con incidentes visibles en este momento.
                      </div>
                    </div>

                    <div className="noc-panel">
                      <div className="noc-row-head" style={{ marginBottom: 6 }}>
                        <div style={{ color: "#e2e8f0", fontSize: 12, fontWeight: 700 }}>Última alerta</div>
                        <Pill tone={recentAlerts.length ? "#fbbf24" : "#64748b"}>{recentAlerts.length ? fmtAgo(recentAlerts[0].created_at) : "sin alertas"}</Pill>
                      </div>
                      <div style={{ color: "#94a3b8", fontSize: 11 }}>
                        {recentAlerts.length ? (recentAlerts[0].type || "alerta") : "No hay actividad reciente de alertas."}
                      </div>
                    </div>
                  </div>
                </div>
              </div>

              <div className="card">
                <div className="card-head">
                  <span>🖥 Servidores impactados</span>
                  <span style={{ color: "#94a3b8", fontSize: 11 }}>{incidentServerSummary.length} con incidentes</span>
                </div>
                <div className="card-body">
                  {incidentServerSummary.length === 0 ? (
                    <div className="alert alert-success">No hay servidores con incidentes activos.</div>
                  ) : (
                    <div className="noc-list">
                      {incidentServerSummary.slice(0, 8).map((server) => (
                        <div key={server.server_label} className="noc-panel">
                          <div className="noc-row-head" style={{ marginBottom: 6 }}>
                            <div style={{ color: "#e2e8f0", fontSize: 12, fontWeight: 700 }}>{server.server_label}</div>
                            <div className="noc-tag-row">
                              <Pill tone={server.critical ? SC.error : "#fbbf24"}>{server.critical} críticos</Pill>
                              <Pill tone={server.open ? "#fbbf24" : "#64748b"}>{server.open} abiertos</Pill>
                            </div>
                          </div>
                          <div style={{ color: "#94a3b8", fontSize: 11, display: "flex", gap: 10, flexWrap: "wrap" }}>
                            <span>Total: {server.total}</span>
                            <span>Reconocidos: {server.acknowledged}</span>
                            <span>Última vez: {fmtAgo(server.latest_seen_at)}</span>
                          </div>
                        </div>
                      ))}
                    </div>
                  )}
                </div>
              </div>

              <div className="card">
                <div className="card-head">
                  <span>📣 Actividad relacionada</span>
                  <span style={{ color: "#94a3b8", fontSize: 11 }}>alertas y remediación</span>
                </div>
                <div className="card-body">
                  <div className="noc-list">
                    {recentAlerts.slice(0, 4).map((alert) => (
                      <div key={`alert-${alert.id}`} className="noc-panel">
                        <div className="noc-row-head" style={{ marginBottom: 4 }}>
                          <div style={{ color: "#e2e8f0", fontSize: 12, fontWeight: 700 }}>{alert.server_label || `Srv ${alert.server_id}`}</div>
                          <Pill tone={alert.type?.includes("site") ? "#fbbf24" : SC.error}>{alert.type}</Pill>
                        </div>
                        <div style={{ color: "#94a3b8", fontSize: 11 }}>{fmtAgo(alert.created_at)}</div>
                      </div>
                    ))}
                    {remediations.slice(0, 3).map((run) => (
                      <div key={`rem-${run.id}`} className="noc-panel">
                        <div className="noc-row-head" style={{ marginBottom: 4 }}>
                          <div style={{ color: "#e2e8f0", fontSize: 12, fontWeight: 700 }}>{run.action_key}</div>
                          <Pill tone={run.status === "error" ? SC.error : run.status === "running" ? SC.running : SC.ok}>{run.status}</Pill>
                        </div>
                        <div style={{ color: "#94a3b8", fontSize: 11 }}>
                          {run.server_label || run.target_label || "—"} · {fmtAgo(run.started_at)}
                        </div>
                      </div>
                    ))}
                    {recentAlerts.length === 0 && remediations.length === 0 && (
                      <div className="alert alert-success">Sin alertas ni remediaciones recientes asociadas a incidentes.</div>
                    )}
                  </div>
                </div>
              </div>
            </div>
          </div>
        )}

        {activeTab === "servers" && (
          <div className="card">
            <div className="card-head">
              <span>🖥 Estado por servidor</span>
              <span style={{ color: "#94a3b8", fontSize: 11 }}>priorizados por impacto e incidentes</span>
            </div>
            <div className="card-body">
              <div className="alert alert-info" style={{ marginBottom: 14 }}>
                Cada tarjeta resalta primero los servidores con incidentes críticos, sitios caídos o degradación visible. Las acciones rápidas siguen disponibles sin salir del panel.
              </div>
              <div className="noc-servers-grid">
                {rankedServers.map(({ server, pressure, topProcs }) => {
                  const tone = getServerTone(server);
                  const totalSites = server.total_sites || 0;
                  const degradedSitesCount = server.degraded_sites || 0;
                  const healthySites = Math.max(0, totalSites - degradedSitesCount);
                  const healthyRatio = totalSites > 0 ? Math.round((healthySites / totalSites) * 100) : 100;
                  const sitesTone = degradedSitesCount > 0 ? (server.down_sites ? SC.error : "#fbbf24") : SC.ok;
                  const cpuTone = pressure.cpu === "critical" ? SC.error : pressure.cpu === "warning" ? "#fbbf24" : "#22d3ee";
                  const ramTone = pressure.ram === "critical" ? SC.error : pressure.ram === "warning" ? "#fbbf24" : "#c084fc";
                  const pressureBadges = [];
                  if (pressure.cpu) pressureBadges.push({ label: `CPU ${Math.round(server.cpu_pct || 0)}%`, level: pressure.cpu });
                  if (pressure.ram) pressureBadges.push({ label: `RAM ${Math.round(server.ram_pct || 0)}%`, level: pressure.ram });
                  if (pressure.disk) pressureBadges.push({ label: `Disco ${Math.round(server.disk_pct || 0)}%`, level: pressure.disk });

                  return (
                    <div key={server.id} className="noc-server-card" style={{ "--server-tone": tone }}>
                      <div className="noc-server-top">
                        <div className="noc-server-identity">
                          <div className="noc-server-name">{server.label}</div>
                          <div className="noc-server-host">{server.host}</div>
                          <div className="noc-server-highlight">{buildServerHighlight(server)}</div>
                        </div>
                        <div className="noc-server-badges">
                          <Pill tone={STATUS_TONE[server.monitor_status] || "#64748b"}>{server.monitor_status || "sin snapshot"}</Pill>
                          <Pill tone={server.critical_incidents ? SC.error : server.active_incidents ? "#fbbf24" : "#64748b"}>
                            {server.active_incidents || 0} incidente(s)
                          </Pill>
                        </div>
                      </div>

                      <div className="noc-server-metric-grid">
                        <div className="noc-stat-box">
                          <div className="noc-stat-label">CPU</div>
                          <div className="noc-stat-value" style={{ color: cpuTone }}>{server.cpu_pct ?? "—"}%</div>
                          <div className="noc-stat-meta">{pressure.cpu === "critical" ? `crítico ≥ ${pressure.thresholds.cpuCrit}%` : pressure.cpu === "warning" ? `alto ≥ ${pressure.thresholds.cpuWarn}%` : "uso actual"}</div>
                        </div>
                        <div className="noc-stat-box">
                          <div className="noc-stat-label">RAM</div>
                          <div className="noc-stat-value" style={{ color: ramTone }}>{server.ram_pct ?? "—"}%</div>
                          <div className="noc-stat-meta">{pressure.ram === "critical" ? `crítico ≥ ${pressure.thresholds.ramCrit}%` : pressure.ram === "warning" ? `alto ≥ ${pressure.thresholds.ramWarn}%` : "memoria"}</div>
                        </div>
                        <div className="noc-stat-box noc-stat-box-sites">
                          <div className="noc-stat-label">Sitios sanos</div>
                          <div className="noc-stat-value" style={{ color: sitesTone }}>{healthySites}/{totalSites || 0}</div>
                          <div className="noc-stat-meta">{degradedSitesCount || 0} degradado(s)</div>
                        </div>
                      </div>

                      {pressure.anyPressure && (
                        <div className="noc-pressure-block">
                          <div className="noc-pressure-head">
                            <span aria-hidden="true">🔥</span>
                            <span className="noc-pressure-title">Presión de recursos</span>
                            <div className="noc-pressure-tags">
                              {pressureBadges.map((badge) => (
                                <Pill key={badge.label} tone={badge.level === "critical" ? SC.error : "#fbbf24"}>{badge.label}</Pill>
                              ))}
                            </div>
                          </div>
                          {topProcs.length === 0 ? (
                            <div className="noc-pressure-empty">Sin procesos en el snapshot. Ejecuta "Métricas" para refrescar.</div>
                          ) : (
                            <div className="noc-procs-table">
                              <div className="noc-procs-row noc-procs-row-head">
                                <span>Proceso</span>
                                <span>CPU</span>
                                <span>MEM</span>
                              </div>
                              {topProcs.map((proc, index) => (
                                <div key={`${proc.command}-${index}`} className="noc-procs-row" title={proc.command}>
                                  <span className="noc-procs-name">{proc.short}</span>
                                  <span className="noc-procs-num" style={{ color: proc.cpu_pct >= 50 ? SC.error : proc.cpu_pct >= 20 ? "#fbbf24" : "#cbd5e1" }}>{proc.cpu_pct.toFixed(1)}%</span>
                                  <span className="noc-procs-num" style={{ color: proc.mem_pct >= 50 ? SC.error : proc.mem_pct >= 20 ? "#fbbf24" : "#cbd5e1" }}>{proc.mem_pct.toFixed(1)}%</span>
                                </div>
                              ))}
                            </div>
                          )}
                        </div>
                      )}

                      <div className="noc-server-health">
                        <div className="noc-server-health-head">
                          <span>Salud web</span>
                          <strong style={{ color: sitesTone }}>{healthyRatio}%</strong>
                        </div>
                        <div className="noc-server-health-track">
                          <div className="noc-server-health-fill" style={{ width: `${Math.min(100, Math.max(0, healthyRatio))}%` }} />
                        </div>
                      </div>

                      <div className="noc-service-row">
                        <ServiceChip name={SERVICE_LABELS.apache} status={server.apache || "—"} />
                        <ServiceChip name={SERVICE_LABELS.php_fpm} status={server.php_fpm || "—"} />
                        <ServiceChip name={SERVICE_LABELS.mariadb} status={server.mariadb || "—"} />
                      </div>

                      <div className="noc-server-signal-row">
                        <Pill tone={server.down_sites ? SC.error : "#64748b"}>caídos: {server.down_sites || 0}</Pill>
                        <Pill tone={server.slow_sites ? "#fbbf24" : "#64748b"}>lentos: {server.slow_sites || 0}</Pill>
                        <Pill tone={server.ssl_expiring_sites ? "#fbbf24" : "#64748b"}>ssl: {server.ssl_expiring_sites || 0}</Pill>
                      </div>

                      <div className="noc-action-row noc-server-actions">
                        <button onClick={() => runRunbook(server.id, "collect_metrics")} disabled={busy} className="btn btn-ghost btn-xs">
                          {runningAction === runKey.runbook("collect_metrics", server.id) ? "..." : "Métricas"}
                        </button>
                        <button onClick={() => runRunbook(server.id, "verify_sites")} disabled={busy} className="btn btn-ghost btn-xs">
                          {runningAction === runKey.runbook("verify_sites", server.id) ? "..." : "Sitios"}
                        </button>
                        <button onClick={() => runRunbook(server.id, "reload_web_stack")} disabled={busy} className="btn btn-ghost btn-xs">
                          {runningAction === runKey.runbook("reload_web_stack", server.id) ? "..." : "Recargar web"}
                        </button>
                      </div>

                      <div className="noc-server-footer">
                        <span>Snapshot: {server.last_snapshot_at ? `${fmtDate(server.last_snapshot_at)} • ${fmtAgo(server.last_snapshot_at)}` : "sin datos"}</span>
                        <span>Load: <strong style={{ color: "#e2e8f0" }}>{server.load_avg || "—"}</strong></span>
                      </div>
                    </div>
                  );
                })}
              </div>
            </div>
          </div>
        )}

        {activeTab === "activity" && (
          <div className="noc-triptych">
            <div className="card">
              <div className="card-head">📣 Alertas recientes</div>
              <div className="card-body">
                <div className="noc-list">
                  {recentAlerts.length === 0 && <div style={{ color: "#94a3b8", fontSize: 12 }}>Sin alertas recientes.</div>}
                  {recentAlerts.slice(0, 8).map((alert) => (
                    <div key={alert.id} style={{ paddingBottom: 8, borderBottom: "1px solid #1e2a3a55" }}>
                      <div className="noc-row-head" style={{ marginBottom: 4 }}>
                        <div style={{ color: "#e2e8f0", fontSize: 12, fontWeight: 700 }}>{alert.server_label || `Srv ${alert.server_id}`}</div>
                        <Pill tone={alert.type?.includes("site") ? "#fbbf24" : SC.error}>{alert.type}</Pill>
                      </div>
                      <div style={{ color: "#94a3b8", fontSize: 11 }}>{fmtAgo(alert.created_at)}</div>
                    </div>
                  ))}
                </div>
              </div>
            </div>

            <div className="card">
              <div className="card-head">⚙ Jobs recientes</div>
              <div className="card-body">
                <div className="noc-list">
                  {recentJobs.length === 0 && <div style={{ color: "#94a3b8", fontSize: 12 }}>Sin jobs recientes.</div>}
                  {recentJobs.slice(0, 8).map((job) => (
                    <div key={job.id} style={{ paddingBottom: 8, borderBottom: "1px solid #1e2a3a55" }}>
                      <div className="noc-row-head">
                        <div style={{ color: "#e2e8f0", fontSize: 12, fontWeight: 700 }}>{job.job_type}</div>
                        <Pill tone={job.status === "error" ? SC.error : job.status === "running" ? SC.running : SC.ok}>{job.status}</Pill>
                      </div>
                      <div style={{ color: "#94a3b8", fontSize: 11 }}>{job.trigger} • {fmtAgo(job.started_at)}</div>
                    </div>
                  ))}
                </div>
              </div>
            </div>

            <div className="card">
              <div className="card-head">🧰 Remediaciones</div>
              <div className="card-body">
                <div className="noc-list">
                  {remediations.length === 0 && <div style={{ color: "#94a3b8", fontSize: 12 }}>Sin remediaciones registradas.</div>}
                  {remediations.slice(0, 8).map((run) => (
                    <div key={run.id} style={{ paddingBottom: 8, borderBottom: "1px solid #1e2a3a55" }}>
                      <div className="noc-row-head">
                        <div style={{ color: "#e2e8f0", fontSize: 12, fontWeight: 700 }}>{run.action_key}</div>
                        <Pill tone={run.status === "error" ? SC.error : run.status === "running" ? SC.running : SC.ok}>{run.status}</Pill>
                      </div>
                      <div style={{ color: "#94a3b8", fontSize: 11 }}>{run.server_label || run.target_label || "—"} • {fmtAgo(run.started_at)}</div>
                    </div>
                  ))}
                </div>
              </div>
            </div>
          </div>
        )}

        {activeTab === "guide" && <PlaybookGuide />}
      </div>
    );
  }

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