"use client";

import { useState } from "react";
import { useQuery } from "@tanstack/react-query";

import { apiFetch } from "@/lib/api";
import { cardStyle, labelStyle, td, th } from "@/lib/adminStyles";
import { BulkGeoPanel } from "@/components/admin/BulkGeoPanel";
import { JobSettingsPanel } from "@/components/admin/JobSettingsPanel";

interface UnifiedRunRow {
  scraper_type: "kn" | "nspd" | "objective" | "nspd_geo";
  run_id: number;
  started_at: string | null;
  finished_at: string | null;
  heartbeat_at: string | null;
  status: string;
  error: string | null;
  triggered_by: string | null;
  scope: string | null;
  requests_count: number | null;
  items_ok: number | null;
  items_failed: number | null;
  sub_items_ok: number | null;
  progress_idx: number | null;
  progress_total: number | null;
  extra: Record<string, unknown> | null;
}

interface UnifiedLogRow {
  scraper_type: "kn" | "nspd" | "nspd_geo";
  log_id: number;
  run_id: number | null;
  ts: string | null;
  level: "debug" | "info" | "warn" | "error";
  stage: string | null;
  entity_id: string | null;
  message: string;
}

const SCRAPER_BADGE: Record<string, { bg: string; fg: string; label: string }> =
  {
    kn: { bg: "#dbeafe", fg: "#1d4ed8", label: "DOM.РФ kn" },
    nspd: { bg: "#fef3c7", fg: "#a16207", label: "NSPD" },
    objective: { bg: "#d1fae5", fg: "#059669", label: "Objective" },
    nspd_geo: { bg: "#ede9fe", fg: "#6d28d9", label: "🗺 NSPD geo" },
  };

const STATUS_COLOR: Record<string, string> = {
  done: "#16a34a",
  running: "#1d4ed8",
  failed: "#b3261e",
  zombie: "#9333ea",
};

function formatIso(s: string | null): string {
  if (!s) return "—";
  return new Date(s).toLocaleString("ru-RU");
}

function diffSecs(a: string | null, b: string | null): string {
  if (!a || !b) return "—";
  const dt = (new Date(b).getTime() - new Date(a).getTime()) / 1000;
  if (dt < 60) return `${dt.toFixed(0)}с`;
  if (dt < 3600) return `${(dt / 60).toFixed(1)} мин`;
  return `${(dt / 3600).toFixed(1)} ч`;
}

export default function ScrapeAllAdminPage() {
  const [filter, setFilter] = useState<
    "all" | "kn" | "nspd" | "objective" | "nspd_geo"
  >("all");
  const [logsRunId, setLogsRunId] = useState<number | "">("");
  // scraper_type строки, по которой открыты логи: run_id не уникален между
  // скраперами (независимые BIGSERIAL PK), поэтому фильтр логов обязан быть
  // scoped на конкретный скрапер, иначе смешиваются прогоны с одинаковым run_id.
  const [logsScraperType, setLogsScraperType] = useState<
    "kn" | "nspd" | "nspd_geo" | ""
  >("");

  // Открыть логи конкретной строки: фиксируем и run_id, и scraper_type.
  function openLogsFor(row: UnifiedRunRow) {
    if (
      row.scraper_type === "kn" ||
      row.scraper_type === "nspd" ||
      row.scraper_type === "nspd_geo"
    ) {
      setLogsRunId(row.run_id);
      setLogsScraperType(row.scraper_type);
    }
  }

  // Сброс фильтра логов (кнопка «сбросить» и при смене таба).
  function resetLogsFilter() {
    setLogsRunId("");
    setLogsScraperType("");
  }

  // Смена таба не должна тянуть стейл-фильтр логов чужого прогона.
  function changeFilter(t: "all" | "kn" | "nspd" | "objective" | "nspd_geo") {
    setFilter(t);
    resetLogsFilter();
  }

  const runs = useQuery({
    queryKey: ["unified-runs", filter],
    queryFn: () => {
      const q =
        filter !== "all" ? `?scraper_type=${filter}&limit=30` : "?limit=30";
      return apiFetch<UnifiedRunRow[]>(`/api/v1/admin/scrape/all/runs${q}`);
    },
    refetchInterval: 5000,
  });

  const logs = useQuery({
    queryKey: ["unified-logs", filter, logsScraperType, logsRunId],
    queryFn: () => {
      const params = new URLSearchParams({ limit: "100" });
      // Когда открыт конкретный прогон — scope-им логи на его scraper_type
      // (run_id не уникален между скраперами). Иначе используем таб-фильтр.
      if (logsRunId !== "" && logsScraperType !== "") {
        params.set("scraper_type", logsScraperType);
        params.set("run_id", String(logsRunId));
      } else if (filter !== "all") {
        params.set("scraper_type", filter);
      }
      return apiFetch<UnifiedLogRow[]>(
        `/api/v1/admin/scrape/all/logs?${params}`,
      );
    },
    refetchInterval: 8000,
  });

  // Сводка по statusам в показанной выборке (top-30, НЕ глобальные тоталы).
  // v_scrape_runs_unified отдаёт и статусы вне 4 плиток (nspd skipped,
  // nspd_geo paused/queued) — собираем их в "other", чтобы сумма плиток
  // совпадала с числом строк, а не молча теряла прогоны.
  const summary = (() => {
    const s = { running: 0, done: 0, failed: 0, zombie: 0, other: 0 } as Record<
      string,
      number
    >;
    const known = new Set(["running", "done", "failed", "zombie"]);
    runs.data?.forEach((r) => {
      const key = known.has(r.status) ? r.status : "other";
      s[key] = (s[key] ?? 0) + 1;
    });
    return s;
  })();

  return (
    <>
      <h2 style={{ marginTop: 0, fontSize: 18 }}>
        Скраперы — сводный dashboard
      </h2>
      <p style={{ color: "#5b6066", fontSize: 13, marginTop: 4 }}>
        Все скраперы в одной таблице (через <code>v_scrape_runs_unified</code>).
        Для запуска / триггеров — отдельные страницы:{" "}
        <a href="/admin/scrape" style={{ color: "#1d4ed8" }}>
          DOM.РФ kn
        </a>{" "}
        ·{" "}
        <a href="/admin/scrape/geo" style={{ color: "#1d4ed8" }}>
          🗺 NSPD geo
        </a>{" "}
        ·{" "}
        <a href="/admin/scrape/objective" style={{ color: "#1d4ed8" }}>
          Objective
        </a>
        . Старый Playwright-based NSPD-скрапер снят с эксплуатации 2026-05-11 —
        история его прогонов осталась под фильтром <code>nspd</code> только для
        аудита.
      </p>

      {/* Сводка-плитки */}
      <section
        style={{
          ...cardStyle,
          marginTop: 16,
          display: "grid",
          gridTemplateColumns: "repeat(5, 1fr)",
          gap: 16,
        }}
      >
        <Stat
          label="🟦 в работе"
          value={summary.running ?? 0}
          color="#1d4ed8"
        />
        <Stat label="✅ успешно" value={summary.done ?? 0} color="#16a34a" />
        <Stat label="❌ failed" value={summary.failed ?? 0} color="#b3261e" />
        <Stat label="🧟 zombie" value={summary.zombie ?? 0} color="#9333ea" />
        <Stat label="• прочие" value={summary.other ?? 0} color="#6b7280" />
      </section>

      {/* Фильтр + табы */}
      <section style={{ ...cardStyle, marginTop: 16 }}>
        <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
          <span style={{ ...labelStyle, marginBottom: 0 }}>Filter:</span>
          {(["all", "kn", "nspd", "nspd_geo", "objective"] as const).map(
            (t) => (
              <button
                key={t}
                type="button"
                onClick={() => changeFilter(t)}
                style={{
                  ...filterTab,
                  background: filter === t ? "#1d4ed8" : "#f3f4f6",
                  color: filter === t ? "#fff" : "#1f2937",
                }}
              >
                {t === "all" ? "Все" : (SCRAPER_BADGE[t]?.label ?? t)}
              </button>
            ),
          )}
        </div>
      </section>

      {/* Таблица прогонов */}
      <section style={{ ...cardStyle, marginTop: 16 }}>
        <h3 style={sectionTitle}>
          📜 Последние прогоны (top 30, обновляется каждые 5с)
        </h3>
        <div style={{ overflowX: "auto" }}>
          <table style={tableStyle}>
            <thead style={{ background: "#f9fafb" }}>
              <tr>
                <th style={th}>scraper</th>
                <th style={th}>run_id</th>
                <th style={th}>started</th>
                <th style={th}>scope</th>
                <th style={th}>duration</th>
                <th style={{ ...th, textAlign: "right" }}>items_ok</th>
                <th style={{ ...th, textAlign: "right" }}>failed</th>
                <th style={{ ...th, textAlign: "right" }}>sub_ok</th>
                <th style={th}>status</th>
                <th style={th}>trigger</th>
                <th style={th}></th>
              </tr>
            </thead>
            <tbody>
              {runs.data?.map((r) => {
                const heartbeatLag = r.heartbeat_at
                  ? (Date.now() - new Date(r.heartbeat_at).getTime()) / 1000
                  : null;
                // Порог выровнен с backend zombie-детектором (10 мин без
                // heartbeat = точно мёртв, admin_scrape.py). Лаг 5-9 мин при
                // долгом батче — нормальная живая работа, не "stale".
                const isStale =
                  r.status === "running" &&
                  heartbeatLag !== null &&
                  heartbeatLag > 600;
                const badge = SCRAPER_BADGE[r.scraper_type];
                const statusColor = STATUS_COLOR[r.status] ?? "#6b7280";
                return (
                  <tr
                    key={`${r.scraper_type}-${r.run_id}`}
                    style={{ borderTop: "1px solid #f3f4f6" }}
                  >
                    <td style={td}>
                      {badge && (
                        <span
                          style={{
                            display: "inline-block",
                            padding: "2px 8px",
                            borderRadius: 4,
                            background: badge.bg,
                            color: badge.fg,
                            fontWeight: 500,
                            fontSize: 11,
                          }}
                        >
                          {badge.label}
                        </span>
                      )}
                    </td>
                    <td style={{ ...td, fontFamily: "monospace" }}>
                      {r.run_id}
                    </td>
                    <td style={td}>{formatIso(r.started_at)}</td>
                    <td
                      style={{ ...td, fontFamily: "monospace", fontSize: 11 }}
                    >
                      {r.scope ?? "—"}
                    </td>
                    <td style={td}>
                      {diffSecs(r.started_at, r.finished_at ?? r.heartbeat_at)}
                    </td>
                    <td
                      style={{
                        ...td,
                        textAlign: "right",
                        fontFamily: "monospace",
                      }}
                    >
                      {r.items_ok?.toLocaleString("ru-RU") ?? "—"}
                    </td>
                    <td
                      style={{
                        ...td,
                        textAlign: "right",
                        fontFamily: "monospace",
                        color: r.items_failed ? "#b3261e" : "inherit",
                      }}
                    >
                      {r.items_failed?.toLocaleString("ru-RU") ?? "—"}
                    </td>
                    <td
                      style={{
                        ...td,
                        textAlign: "right",
                        fontFamily: "monospace",
                      }}
                    >
                      {r.sub_items_ok?.toLocaleString("ru-RU") ?? "—"}
                    </td>
                    <td style={td}>
                      <span
                        style={{
                          display: "inline-block",
                          padding: "2px 8px",
                          borderRadius: 4,
                          background: statusColor + "22",
                          color: statusColor,
                          fontWeight: 500,
                        }}
                      >
                        {isStale ? "stale" : r.status}
                      </span>
                    </td>
                    <td style={{ ...td, color: "#6b7280" }}>
                      {r.triggered_by ?? "—"}
                    </td>
                    <td style={td}>
                      {(r.scraper_type === "kn" ||
                        r.scraper_type === "nspd" ||
                        r.scraper_type === "nspd_geo") && (
                        <button
                          type="button"
                          onClick={() => openLogsFor(r)}
                          style={{
                            ...secondaryBtn,
                            padding: "4px 10px",
                            fontSize: 11,
                          }}
                        >
                          логи
                        </button>
                      )}
                    </td>
                  </tr>
                );
              })}
              {runs.data?.length === 0 && (
                <tr>
                  <td
                    colSpan={11}
                    style={{
                      ...td,
                      textAlign: "center",
                      color: "#6b7280",
                      padding: 24,
                    }}
                  >
                    Прогонов с фильтром <code>{filter}</code> нет
                  </td>
                </tr>
              )}
            </tbody>
          </table>
        </div>
      </section>

      {/* Job Settings */}
      <div style={{ marginTop: 16 }}>
        <JobSettingsPanel />
      </div>

      {/* Bulk geo backfill */}
      <div style={{ marginTop: 16 }}>
        <BulkGeoPanel />
      </div>

      {/* Logs */}
      <section style={{ ...cardStyle, marginTop: 16 }}>
        <h3 style={sectionTitle}>
          📋 Логи (top 100){" "}
          {logsRunId !== "" && (
            <span style={{ fontSize: 13, fontWeight: 400, color: "#5b6066" }}>
              · фильтр {logsScraperType && `${logsScraperType} `}run_id=
              {logsRunId}{" "}
              <button
                type="button"
                onClick={resetLogsFilter}
                style={{
                  background: "none",
                  border: "none",
                  color: "#1d4ed8",
                  cursor: "pointer",
                  fontSize: 13,
                  padding: 0,
                }}
              >
                сбросить
              </button>
            </span>
          )}
        </h3>
        <div style={{ overflowX: "auto", maxHeight: 400, overflowY: "auto" }}>
          <table style={tableStyle}>
            <thead
              style={{ background: "#f9fafb", position: "sticky", top: 0 }}
            >
              <tr>
                <th style={th}>ts</th>
                <th style={th}>scraper</th>
                <th style={th}>run</th>
                <th style={th}>level</th>
                <th style={th}>stage</th>
                <th style={th}>entity</th>
                <th style={th}>message</th>
              </tr>
            </thead>
            <tbody>
              {logs.data?.map((l) => (
                <tr
                  key={`${l.scraper_type}-${l.log_id}`}
                  style={{ borderTop: "1px solid #f3f4f6" }}
                >
                  <td style={{ ...td, fontSize: 11, whiteSpace: "nowrap" }}>
                    {formatIso(l.ts)}
                  </td>
                  <td style={td}>
                    <span
                      style={{
                        fontSize: 10,
                        color: SCRAPER_BADGE[l.scraper_type]?.fg ?? "#6b7280",
                      }}
                    >
                      {l.scraper_type}
                    </span>
                  </td>
                  <td style={{ ...td, fontFamily: "monospace" }}>
                    {l.run_id ?? "—"}
                  </td>
                  <td style={td}>
                    <span
                      style={{
                        color:
                          l.level === "error"
                            ? "#b3261e"
                            : l.level === "warn"
                              ? "#a16207"
                              : "#5b6066",
                        fontSize: 11,
                        fontWeight: 500,
                      }}
                    >
                      {l.level}
                    </span>
                  </td>
                  <td style={{ ...td, fontSize: 11 }}>{l.stage ?? "—"}</td>
                  <td style={{ ...td, fontFamily: "monospace", fontSize: 11 }}>
                    {l.entity_id ?? "—"}
                  </td>
                  <td style={{ ...td, fontSize: 12 }}>{l.message}</td>
                </tr>
              ))}
              {logs.data?.length === 0 && (
                <tr>
                  <td
                    colSpan={7}
                    style={{
                      ...td,
                      textAlign: "center",
                      color: "#6b7280",
                      padding: 24,
                    }}
                  >
                    Логов нет (objective пока не пишет в log — только runs)
                  </td>
                </tr>
              )}
            </tbody>
          </table>
        </div>
      </section>
    </>
  );
}

function Stat({
  label,
  value,
  color,
}: {
  label: string;
  value: number;
  color: string;
}) {
  return (
    <div>
      <div style={{ fontSize: 12, color: "#5b6066", marginBottom: 4 }}>
        {label}
      </div>
      <div
        style={{
          fontFamily: "monospace",
          fontSize: 28,
          fontWeight: 600,
          color,
        }}
      >
        {value}
      </div>
    </div>
  );
}

const sectionTitle = {
  margin: "0 0 12px",
  fontSize: 16,
  fontWeight: 600 as const,
};
const filterTab = {
  padding: "6px 14px",
  border: "none",
  borderRadius: 6,
  fontSize: 13,
  fontWeight: 500 as const,
  cursor: "pointer",
};
const secondaryBtn = {
  padding: "8px 14px",
  background: "#f3f4f6",
  color: "#1f2937",
  border: "1px solid #d1d5db",
  borderRadius: 6,
  fontSize: 13,
  cursor: "pointer",
};
const tableStyle = {
  width: "100%",
  borderCollapse: "collapse" as const,
  fontSize: 12,
};
