"use client";

import { useEffect, useState } from "react";
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";

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

interface ObjectiveRunRow {
  run_id: number;
  started_at: string | null;
  finished_at: string | null;
  heartbeat_at: string | null;
  group_name: string;
  reports_ok: number;
  reports_failed: number;
  rows_corpus_room: number;
  rows_lots: number;
  rows_mappings: number;
  requests_count: number;
  status: string;
  error: string | null;
  triggered_by: string;
}

interface ObjectiveCoverage {
  pg: {
    lots: number;
    corp_room_month: number;
    mappings: number;
    last_lot_snapshot: string | null;
    last_run_at: string | null;
  };
  sqlite: {
    path: string;
    exists: boolean;
    size_bytes?: number;
    modified_at?: number;
    lots?: number;
    corp_room_month?: number;
    mappings?: number;
    error?: string;
  };
}

interface ObjectiveTriggerResp {
  task_id: string;
  sqlite_path: string;
}

interface ObjectiveSyncOurResp {
  task_id: string;
  groups_planned: string[];
  estimate_minutes: number;
}

interface ObjectiveConfig {
  cron_schedule: string;
  groups_csv: string;
  groups: string[];
  use_ddu: boolean;
  use_dkp: boolean;
  period_months_back: number;
  inter_group_delay_s: number;
  rate_ms: number;
  retries: number;
  updated_at: string | null;
  updated_by: string | null;
  _note?: string;
}

function formatBytes(b: number | undefined): string {
  if (!b) return "—";
  const mb = b / 1024 / 1024;
  return mb >= 100 ? `${mb.toFixed(0)} MB` : `${mb.toFixed(1)} MB`;
}

function formatEpoch(sec: number | undefined): string {
  if (!sec) return "—";
  return new Date(sec * 1000).toLocaleString("ru-RU");
}

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(1)}с`;
  return `${(dt / 60).toFixed(1)} мин`;
}

export default function ObjectiveEtlAdminPage() {
  const [overrideSqlitePath, setOverrideSqlitePath] = useState("");
  const [lastTaskId, setLastTaskId] = useState<string | null>(null);
  const [syncGroupsCsv, setSyncGroupsCsv] = useState("");
  const [lastSyncTaskId, setLastSyncTaskId] = useState<string | null>(null);
  const [cfgDraft, setCfgDraft] = useState<Partial<ObjectiveConfig>>({});
  const [cfgSaved, setCfgSaved] = useState(false);
  const [showEtl, setShowEtl] = useState(false);
  const queryClient = useQueryClient();

  const coverage = useQuery({
    queryKey: ["objective-coverage"],
    queryFn: () =>
      apiFetch<ObjectiveCoverage>("/api/v1/admin/scrape/objective/coverage"),
    refetchInterval: 10000,
  });

  const runs = useQuery({
    queryKey: ["objective-runs"],
    queryFn: () =>
      apiFetch<ObjectiveRunRow[]>(
        "/api/v1/admin/scrape/objective/runs?limit=20",
      ),
    refetchInterval: 5000,
  });

  const cfg = useQuery({
    queryKey: ["objective-config"],
    queryFn: () =>
      apiFetch<ObjectiveConfig>("/api/v1/admin/scrape/objective/config"),
    refetchOnWindowFocus: false,
  });

  // Подтягиваем cfg в draft при первом появлении (не во время render!)
  useEffect(() => {
    if (cfg.data && Object.keys(cfgDraft).length === 0) {
      setCfgDraft(cfg.data);
    }
  }, [cfg.data, cfgDraft]);

  const trigger = useMutation({
    mutationFn: () =>
      apiFetch<ObjectiveTriggerResp>("/api/v1/admin/scrape/objective", {
        method: "POST",
        body: JSON.stringify({ sqlite_path: overrideSqlitePath || null }),
      }),
    onSuccess: (data) => {
      setLastTaskId(data.task_id);
      void queryClient.invalidateQueries({ queryKey: ["objective-runs"] });
      void queryClient.invalidateQueries({ queryKey: ["objective-coverage"] });
    },
  });

  const syncOur = useMutation({
    mutationFn: () => {
      const groups = syncGroupsCsv
        .split(",")
        .map((g) => g.trim())
        .filter(Boolean);
      return apiFetch<ObjectiveSyncOurResp>(
        "/api/v1/admin/scrape/objective/sync-our",
        {
          method: "POST",
          body: JSON.stringify({ groups: groups.length ? groups : null }),
        },
      );
    },
    onSuccess: (data) => {
      setLastSyncTaskId(data.task_id);
      void queryClient.invalidateQueries({ queryKey: ["objective-runs"] });
    },
  });

  const saveCfg = useMutation({
    mutationFn: (patch: Partial<ObjectiveConfig>) =>
      apiFetch<ObjectiveConfig>("/api/v1/admin/scrape/objective/config", {
        method: "PUT",
        body: JSON.stringify(patch),
      }),
    onSuccess: (data) => {
      setCfgDraft(data);
      setCfgSaved(true);
      window.setTimeout(() => setCfgSaved(false), 3000);
      void queryClient.invalidateQueries({ queryKey: ["objective-config"] });
    },
  });

  return (
    <>
      <h2 style={{ marginTop: 0, fontSize: 18 }}>
        Objective: sync с api.objctv.ru → наша PG
      </h2>
      <p style={{ color: "#5b6066", fontSize: 13, marginTop: 4 }}>
        <strong>Source of truth = эта PG.</strong> Наш Celery каждый вторник
        06:00 МСК тянет 4 группы (Свердл.обл + Челябинск + Тюмень + Пермь) с
        api.objctv.ru напрямую в objective_lots / corpus_room_month /
        lots_history. SQLite Антона (/sf/) — отдельный микросервис, остался
        только как bootstrap-fallback (свёрнут ниже).
      </p>

      {/* Coverage */}
      <section style={cardStyle}>
        <h3 style={sectionTitle}>📦 Состояние PG (источник истины)</h3>
        {coverage.isLoading && (
          <p style={{ color: "#5b6066", fontSize: 13 }}>Загрузка…</p>
        )}
        {coverage.data && (
          <div
            style={{
              display: "grid",
              gridTemplateColumns: "1fr 1fr 1fr",
              gap: 24,
            }}
          >
            <Stat label="objective_lots" value={coverage.data.pg.lots} />
            <Stat
              label="corp×room×month"
              value={coverage.data.pg.corp_room_month}
            />
            <Stat label="complex_mapping" value={coverage.data.pg.mappings} />
            <div
              style={{
                gridColumn: "1 / -1",
                borderTop: "1px solid #e6e8ec",
                paddingTop: 12,
                fontSize: 12,
                color: "#5b6066",
                display: "grid",
                gridTemplateColumns: "1fr 1fr",
                gap: 16,
              }}
            >
              <div>
                last lot snapshot:{" "}
                <code>{coverage.data.pg.last_lot_snapshot ?? "—"}</code>
              </div>
              <div>
                last successful sync:{" "}
                <code>{formatIso(coverage.data.pg.last_run_at)}</code>
              </div>
            </div>
            <div
              style={{
                gridColumn: "1 / -1",
                fontSize: 11,
                color: "#9ca3af",
                display: "grid",
                gridTemplateColumns: "1fr 1fr 1fr",
                gap: 16,
              }}
            >
              <div>
                SQLite Антона (legacy):{" "}
                {coverage.data.sqlite.exists ? (
                  <span style={{ color: "#16a34a" }}>
                    ✓ {formatBytes(coverage.data.sqlite.size_bytes)}
                  </span>
                ) : (
                  <span style={{ color: "#9ca3af" }}>✗ не примонтирован</span>
                )}
              </div>
              <div>
                файл обновлён:{" "}
                <code>{formatEpoch(coverage.data.sqlite.modified_at)}</code>
              </div>
              <div>
                lots в SQLite:{" "}
                <code>
                  {coverage.data.sqlite.lots?.toLocaleString("ru-RU") ?? "—"}
                </code>
              </div>
            </div>
          </div>
        )}
      </section>

      {/* ⚙️ Settings — основной блок (всегда раскрыт) */}
      <section style={{ ...cardStyle, marginTop: 16, borderColor: "#3b82f6" }}>
        <h3 style={{ ...sectionTitle, color: "#1d4ed8" }}>
          ⚙️ Настройки sync — расписание + параметры запроса
        </h3>
        <p
          style={{
            color: "#5b6066",
            fontSize: 12,
            marginTop: -4,
            marginBottom: 16,
          }}
        >
          Хранятся в БД <code>objective_sync_config</code>. Все поля кроме cron
          подхватываются на следующем sync-вызове автоматически.
          <strong> Cron требует</strong>{" "}
          <code>docker compose restart beat</code> после изменения.
        </p>
        {!cfg.data && <p style={{ color: "#5b6066" }}>Загрузка настроек…</p>}
        {cfg.data && (
          <div style={{ display: "grid", gap: 16 }}>
            <label>
              <span style={labelStyle}>
                📅 Cron-расписание (минута час дом мес дн)
              </span>
              <input
                type="text"
                value={cfgDraft.cron_schedule ?? ""}
                onChange={(e) =>
                  setCfgDraft({ ...cfgDraft, cron_schedule: e.target.value })
                }
                placeholder="0 6 * * tue"
                style={{ ...inputStyle, fontFamily: "monospace" }}
              />
              <span style={{ fontSize: 11, color: "#5b6066" }}>
                Сейчас активно: <code>{cfg.data.cron_schedule}</code>
              </span>
            </label>

            <label>
              <span style={labelStyle}>🌍 Группы (csv)</span>
              <input
                type="text"
                value={cfgDraft.groups_csv ?? ""}
                onChange={(e) =>
                  setCfgDraft({ ...cfgDraft, groups_csv: e.target.value })
                }
                placeholder="Свердловская область,Челябинск,Тюмень,Пермь"
                style={{ ...inputStyle, fontFamily: "monospace" }}
              />
              <span style={{ fontSize: 11, color: "#5b6066" }}>
                Доступные на тарифе: Екатеринбург, Свердловская область,
                Свердловск, Верхняя Пышма, Берёзовский, Сысерть, Нижний Тагил,
                Каменск-Уральский, Первоуральск, Новоуральск, Челябинск, Тюмень,
                Пермь.
              </span>
            </label>

            <div
              style={{
                display: "grid",
                gridTemplateColumns: "repeat(3, 1fr)",
                gap: 16,
              }}
            >
              <label>
                <span style={labelStyle}>📊 use_ddu</span>
                <select
                  value={String(cfgDraft.use_ddu ?? true)}
                  onChange={(e) =>
                    setCfgDraft({
                      ...cfgDraft,
                      use_ddu: e.target.value === "true",
                    })
                  }
                  style={inputStyle}
                >
                  <option value="true">true (включить)</option>
                  <option value="false">false (выключить)</option>
                </select>
              </label>

              <label>
                <span style={labelStyle}>📊 use_dkp</span>
                <select
                  value={String(cfgDraft.use_dkp ?? true)}
                  onChange={(e) =>
                    setCfgDraft({
                      ...cfgDraft,
                      use_dkp: e.target.value === "true",
                    })
                  }
                  style={inputStyle}
                >
                  <option value="true">true</option>
                  <option value="false">false</option>
                </select>
              </label>

              <label>
                <span style={labelStyle}>
                  🗓 period_months_back (для corp_sum)
                </span>
                <input
                  type="number"
                  min={1}
                  max={24}
                  value={cfgDraft.period_months_back ?? 1}
                  onChange={(e) =>
                    setCfgDraft({
                      ...cfgDraft,
                      period_months_back: Number(e.target.value),
                    })
                  }
                  style={inputStyle}
                />
                <span style={{ fontSize: 11, color: "#5b6066" }}>
                  1 = последний полный месяц
                </span>
              </label>

              <label>
                <span style={labelStyle}>⏱ inter_group_delay_s</span>
                <input
                  type="number"
                  min={0}
                  max={300}
                  value={cfgDraft.inter_group_delay_s ?? 30}
                  onChange={(e) =>
                    setCfgDraft({
                      ...cfgDraft,
                      inter_group_delay_s: Number(e.target.value),
                    })
                  }
                  style={inputStyle}
                />
                <span style={{ fontSize: 11, color: "#5b6066" }}>
                  пауза между группами (сек)
                </span>
              </label>

              <label>
                <span style={labelStyle}>🚦 rate_ms (HTTP-пауза)</span>
                <input
                  type="number"
                  min={0}
                  max={10000}
                  step={100}
                  value={cfgDraft.rate_ms ?? 3000}
                  onChange={(e) =>
                    setCfgDraft({
                      ...cfgDraft,
                      rate_ms: Number(e.target.value),
                    })
                  }
                  style={inputStyle}
                />
                <span style={{ fontSize: 11, color: "#5b6066" }}>
                  между запросами в client (мс)
                </span>
              </label>

              <label>
                <span style={labelStyle}>🔄 retries (на 429/5xx)</span>
                <input
                  type="number"
                  min={0}
                  max={5}
                  value={cfgDraft.retries ?? 2}
                  onChange={(e) =>
                    setCfgDraft({
                      ...cfgDraft,
                      retries: Number(e.target.value),
                    })
                  }
                  style={inputStyle}
                />
              </label>
            </div>

            <div
              style={{
                display: "flex",
                gap: 12,
                alignItems: "center",
                flexWrap: "wrap",
              }}
            >
              <button
                type="button"
                onClick={() => saveCfg.mutate(cfgDraft)}
                disabled={saveCfg.isPending}
                style={primaryBtn}
              >
                {saveCfg.isPending ? "Сохранение…" : "💾 Сохранить настройки"}
              </button>
              <button
                type="button"
                onClick={() => cfg.data && setCfgDraft(cfg.data)}
                style={secondaryBtn}
              >
                Сбросить к актуальному
              </button>
              {cfgSaved && (
                <span style={{ color: "#16a34a", fontSize: 13 }}>
                  ✓ Сохранено. Параметры (кроме cron) применятся со следующим
                  sync.
                </span>
              )}
              {saveCfg.error && (
                <span style={{ color: "#b3261e", fontSize: 13 }}>
                  Ошибка: {(saveCfg.error as Error).message}
                </span>
              )}
            </div>

            {cfg.data.updated_at && (
              <p
                style={{
                  fontSize: 11,
                  color: "#5b6066",
                  borderTop: "1px solid #e6e8ec",
                  paddingTop: 8,
                  margin: 0,
                }}
              >
                Последнее изменение: {formatIso(cfg.data.updated_at)}
                {cfg.data.updated_by && ` (by ${cfg.data.updated_by})`}
              </p>
            )}
          </div>
        )}
      </section>

      {/* 🌐 Запустить наш sync */}
      <section style={{ ...cardStyle, marginTop: 16 }}>
        <h3 style={sectionTitle}>🌐 Запустить наш sync вручную</h3>
        <p style={{ color: "#5b6066", fontSize: 13, marginTop: 0 }}>
          Дёргает api.objctv.ru с параметрами из настроек выше. Ручной триггер
          для немедленного запуска (вне beat-расписания). ~3 минуты на группу ×
          N групп.
        </p>
        <label style={{ display: "block", marginBottom: 12 }}>
          <span style={labelStyle}>
            Override групп (csv, пусто = взять из настроек)
          </span>
          <input
            type="text"
            value={syncGroupsCsv}
            onChange={(e) => setSyncGroupsCsv(e.target.value)}
            placeholder={
              cfg.data?.groups_csv ?? "Свердл.обл,Челябинск,Тюмень,Пермь"
            }
            style={{ ...inputStyle, fontFamily: "monospace" }}
          />
        </label>
        <div
          style={{
            display: "flex",
            gap: 12,
            alignItems: "center",
            flexWrap: "wrap",
          }}
        >
          <button
            type="button"
            onClick={() => syncOur.mutate()}
            disabled={syncOur.isPending}
            style={primaryBtn}
          >
            {syncOur.isPending ? "Запуск…" : "🚀 Запустить наш sync"}
          </button>
          {lastSyncTaskId && (
            <span style={{ fontSize: 13, color: "#5b6066" }}>
              task <code>{lastSyncTaskId}</code> в очереди
            </span>
          )}
          {syncOur.error && (
            <span style={{ color: "#b3261e", fontSize: 13 }}>
              {(syncOur.error as Error).message}
            </span>
          )}
        </div>
      </section>

      {/* 🛠 Bootstrap ETL — collapsible */}
      <section style={{ ...cardStyle, marginTop: 16, background: "#fafafa" }}>
        <button
          type="button"
          onClick={() => setShowEtl(!showEtl)}
          style={{
            background: "none",
            border: "none",
            padding: 0,
            font: "inherit",
            cursor: "pointer",
            color: "#374151",
            fontSize: 14,
            fontWeight: 600,
            textAlign: "left" as const,
            width: "100%",
          }}
        >
          {showEtl ? "▼" : "▶"} 🛠 Bootstrap ETL: SQLite Антона → PG{" "}
          <span style={{ fontWeight: 400, fontSize: 12, color: "#9ca3af" }}>
            (legacy fallback, в обычной работе не нужен)
          </span>
        </button>
        {showEtl && (
          <div style={{ marginTop: 12, display: "grid", gap: 12 }}>
            <p style={{ fontSize: 13, color: "#5b6066", margin: 0 }}>
              Используется только для одноразового импорта Антоновского SQLite в
              нашу PG (например при cold-start). После того как наш sync
              отработал — этот ETL не нужен.
            </p>
            <label>
              <span style={labelStyle}>Override sqlite_path</span>
              <input
                type="text"
                value={overrideSqlitePath}
                onChange={(e) => setOverrideSqlitePath(e.target.value)}
                placeholder="/data/anton-sqlite/analysis.db"
                style={{ ...inputStyle, fontFamily: "monospace" }}
              />
            </label>
            <div style={{ display: "flex", gap: 12, alignItems: "center" }}>
              <button
                type="button"
                onClick={() => trigger.mutate()}
                disabled={trigger.isPending}
                style={secondaryBtn}
              >
                {trigger.isPending ? "Запуск…" : "Bootstrap ETL"}
              </button>
              {lastTaskId && (
                <span style={{ fontSize: 13, color: "#5b6066" }}>
                  task <code>{lastTaskId}</code>
                </span>
              )}
              {trigger.error && (
                <span style={{ color: "#b3261e", fontSize: 13 }}>
                  {(trigger.error as Error).message}
                </span>
              )}
            </div>
          </div>
        )}
      </section>

      {/* 📜 Runs */}
      <section style={{ ...cardStyle, marginTop: 16 }}>
        <h3 style={sectionTitle}>
          📜 Последние прогоны (top 20, обновляется каждые 5с)
        </h3>
        <div style={{ overflowX: "auto" }}>
          <table
            style={{ width: "100%", borderCollapse: "collapse", fontSize: 12 }}
          >
            <thead style={{ background: "#f9fafb" }}>
              <tr>
                <th style={th}>run_id</th>
                <th style={th}>started</th>
                <th style={th}>group</th>
                <th style={th}>duration</th>
                <th style={{ ...th, textAlign: "right" as const }}>lots</th>
                <th style={{ ...th, textAlign: "right" as const }}>
                  corp×room
                </th>
                <th style={{ ...th, textAlign: "right" as const }}>mappings</th>
                <th style={th}>status</th>
                <th style={th}>trigger</th>
                <th style={th}>error</th>
              </tr>
            </thead>
            <tbody>
              {runs.data?.map((r) => {
                const heartbeatLag = r.heartbeat_at
                  ? (Date.now() - new Date(r.heartbeat_at).getTime()) / 1000
                  : null;
                const isStale =
                  r.status === "running" &&
                  heartbeatLag !== null &&
                  heartbeatLag > 300;
                const statusColor =
                  r.status === "done"
                    ? "#16a34a"
                    : r.status === "running" && !isStale
                      ? "#1d4ed8"
                      : r.status === "failed" || isStale
                        ? "#b3261e"
                        : "#6b7280";
                return (
                  <tr key={r.run_id} style={{ borderTop: "1px solid #f3f4f6" }}>
                    <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.group_name}
                    </td>
                    <td style={td}>
                      {diffSecs(r.started_at, r.finished_at ?? r.heartbeat_at)}
                    </td>
                    <td
                      style={{
                        ...td,
                        textAlign: "right" as const,
                        fontFamily: "monospace",
                      }}
                    >
                      {r.rows_lots?.toLocaleString("ru-RU") ?? "—"}
                    </td>
                    <td
                      style={{
                        ...td,
                        textAlign: "right" as const,
                        fontFamily: "monospace",
                      }}
                    >
                      {r.rows_corpus_room?.toLocaleString("ru-RU") ?? "—"}
                    </td>
                    <td
                      style={{
                        ...td,
                        textAlign: "right" as const,
                        fontFamily: "monospace",
                      }}
                    >
                      {r.rows_mappings?.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,
                        color: "#b3261e",
                        maxWidth: 240,
                        overflow: "hidden",
                        textOverflow: "ellipsis",
                        whiteSpace: "nowrap",
                      }}
                      title={r.error ?? ""}
                    >
                      {r.error ?? ""}
                    </td>
                  </tr>
                );
              })}
              {runs.data?.length === 0 && (
                <tr>
                  <td
                    colSpan={10}
                    style={{
                      ...td,
                      textAlign: "center" as const,
                      color: "#6b7280",
                      padding: 24,
                    }}
                  >
                    Прогонов ещё не было
                  </td>
                </tr>
              )}
            </tbody>
          </table>
        </div>
      </section>

      <ScrapeLogsPanel
        scraperType="objective"
        title="Журнал Objective ETL"
        subtitle="фильтр scraper_type=objective · обновляется каждые 3 сек"
        containerStyle={{ marginTop: 16 }}
      />
    </>
  );
}

// ── Helper component ────────────────────────────────────────────────────────

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

// ── Styles ──────────────────────────────────────────────────────────────────

const sectionTitle = {
  margin: "0 0 12px",
  fontSize: 16,
  fontWeight: 600 as const,
};
const primaryBtn = {
  padding: "10px 18px",
  background: "#1d4ed8",
  color: "#fff",
  border: "none",
  borderRadius: 6,
  fontSize: 14,
  fontWeight: 600,
  cursor: "pointer",
};
const secondaryBtn = {
  padding: "8px 14px",
  background: "#f3f4f6",
  color: "#1f2937",
  border: "1px solid #d1d5db",
  borderRadius: 6,
  fontSize: 13,
  cursor: "pointer",
};
