"use client";

import { 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 GeoJob {
  job_id: number;
  name: string | null;
  job_kind: string;
  source_kind: string;
  status: string;
  triggered_by: string;
  started_at: string | null;
  finished_at: string | null;
  heartbeat_at: string | null;
  targets_total: number;
  targets_done: number;
  targets_failed: number;
  targets_skipped: number;
  requests_count: number;
  waf_blocked_count: number;
  error: string | null;
  rate_ms: number;
  created_at: string | null;
  progress_pct: number;
}

interface EnqueueResp {
  job_id: number;
  targets_total: number;
  estimate_minutes: number;
}

const STATUS_COLOR: Record<string, string> = {
  queued: "#6b7280",
  running: "#1d4ed8",
  done: "#16a34a",
  failed: "#b3261e",
  paused: "#a16207",
  cancelled: "#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 GeoScrapeAdminPage() {
  const [name, setName] = useState("");
  const [sourceKind, setSourceKind] = useState<
    "manual_list" | "rosreestr_pending"
  >("rosreestr_pending");
  const [jobKind, setJobKind] = useState<"quarters" | "parcels" | "buildings">(
    "quarters",
  );
  const [thematicId, setThematicId] = useState(2);

  // Авто-синк thematic_id при смене job_kind. Пользователь может override через
  // селект ниже (например quarters + thematic_id=15 для комплексов).
  // Также переключаем source на manual_list когда job_kind != quarters, т.к.
  // rosreestr_pending содержит только кварталы (иначе backend вернёт 400).
  const handleJobKindChange = (kind: "quarters" | "parcels" | "buildings") => {
    setJobKind(kind);
    if (kind === "quarters") setThematicId(2);
    else if (kind === "parcels") setThematicId(1);
    else if (kind === "buildings") setThematicId(5);
    if (kind !== "quarters") setSourceKind("manual_list");
  };
  const [cadNumsText, setCadNumsText] = useState("");
  const [regionCodes, setRegionCodes] = useState("66,74,72,59");
  const [rateMs, setRateMs] = useState(600);
  const [lastResp, setLastResp] = useState<EnqueueResp | null>(null);
  const queryClient = useQueryClient();

  const jobs = useQuery({
    queryKey: ["geo-jobs"],
    queryFn: () => apiFetch<GeoJob[]>("/api/v1/admin/scrape/geo/jobs?limit=30"),
    refetchInterval: 4000,
  });

  const enqueue = useMutation({
    mutationFn: () => {
      const cad_nums =
        sourceKind === "manual_list"
          ? cadNumsText
              .split(/[,\n\s]+/)
              .map((s) => s.trim())
              .filter(Boolean)
          : [];
      const region_codes =
        sourceKind === "rosreestr_pending"
          ? regionCodes
              .split(",")
              .map((s) => parseInt(s.trim(), 10))
              .filter((n) => Number.isFinite(n))
          : null;
      return apiFetch<EnqueueResp>("/api/v1/admin/scrape/geo/jobs", {
        method: "POST",
        body: JSON.stringify({
          name: name || null,
          job_kind: jobKind,
          source_kind: sourceKind,
          cad_nums,
          thematic_id: thematicId,
          region_codes,
          rate_ms: rateMs,
        }),
      });
    },
    onSuccess: (data) => {
      setLastResp(data);
      void queryClient.invalidateQueries({ queryKey: ["geo-jobs"] });
    },
  });

  const cancelJob = useMutation({
    mutationFn: (id: number) =>
      apiFetch<{ cancelled: boolean }>(
        `/api/v1/admin/scrape/geo/jobs/${id}/cancel`,
        { method: "POST" },
      ),
    onSuccess: () =>
      void queryClient.invalidateQueries({ queryKey: ["geo-jobs"] }),
  });

  const resumeJob = useMutation({
    mutationFn: (id: number) =>
      apiFetch<{ resumed: boolean }>(
        `/api/v1/admin/scrape/geo/jobs/${id}/resume`,
        { method: "POST" },
      ),
    onSuccess: () =>
      void queryClient.invalidateQueries({ queryKey: ["geo-jobs"] }),
  });

  return (
    <>
      <h2 style={{ marginTop: 0, fontSize: 18 }}>
        NSPD Geo bulk-fetcher (resume-friendly)
      </h2>
      <p style={{ color: "#5b6066", fontSize: 13, marginTop: 4 }}>
        Bulk-загрузка кадастровых данных с nspd.gov.ru через urllib
        (lite-fetcher без Playwright). State хранится в БД (
        <code>nspd_geo_jobs</code> + <code>nspd_geo_targets</code>) — при
        redeploy worker_ready hook автоматически re-enqueue недоделанные jobs и
        продолжит с pending targets.
      </p>

      {/* Форма создания нового job */}
      <section style={cardStyle}>
        <h3 style={sectionTitle}>🚀 Новый bulk geo-job</h3>
        <div style={{ display: "grid", gap: 12 }}>
          <label>
            <span style={labelStyle}>Name (опционально)</span>
            <input
              type="text"
              value={name}
              onChange={(e) => setName(e.target.value)}
              placeholder="напр. УрФО quarters expansion 2026-Q2"
              style={inputStyle}
            />
          </label>

          <div
            style={{
              display: "grid",
              gridTemplateColumns: "repeat(3, 1fr)",
              gap: 12,
            }}
          >
            <label>
              <span style={labelStyle}>Source</span>
              <select
                value={sourceKind}
                onChange={(e) =>
                  setSourceKind(
                    e.target.value as "manual_list" | "rosreestr_pending",
                  )
                }
                style={inputStyle}
              >
                <option value="rosreestr_pending">
                  rosreestr_pending (auto из ДДУ)
                </option>
                <option value="manual_list">manual_list (ручной csv)</option>
              </select>
            </label>
            <label>
              <span style={labelStyle}>Job kind</span>
              <select
                value={jobKind}
                onChange={(e) =>
                  handleJobKindChange(
                    e.target.value as "quarters" | "parcels" | "buildings",
                  )
                }
                style={inputStyle}
              >
                <option value="quarters">кадастровые кварталы</option>
                <option value="parcels">участки ЗУ</option>
                <option value="buildings">здания ОКС</option>
              </select>
            </label>
            <label>
              <span style={labelStyle}>
                Thematic ID — NSPD thematicSearchId
              </span>
              <select
                value={thematicId}
                onChange={(e) => setThematicId(parseInt(e.target.value, 10))}
                style={inputStyle}
              >
                <option value={1}>1 — parcel (земельный участок)</option>
                <option value={2}>2 — quarter (кадастровый квартал)</option>
                <option value={4}>4 — admin (адм-территориальное)</option>
                <option value={5}>5 — building (ОКС)</option>
                <option value={7}>7 — zone (территориальная зона)</option>
                <option value={15}>15 — complex (комплекс объектов)</option>
              </select>
            </label>
          </div>

          {sourceKind === "rosreestr_pending" && (
            <label>
              <span style={labelStyle}>
                Region codes (csv) — фильтр rosreestr_deals.region_code
              </span>
              <input
                type="text"
                value={regionCodes}
                onChange={(e) => setRegionCodes(e.target.value)}
                placeholder="66,74,72,59"
                style={{ ...inputStyle, fontFamily: "monospace" }}
              />
              <span style={{ fontSize: 11, color: "#5b6066" }}>
                66=Свердл · 74=Челяб · 72=Тюмень · 59=Пермь
              </span>
            </label>
          )}

          {sourceKind === "manual_list" && (
            <label>
              <span style={labelStyle}>
                Cad numbers (через запятую, пробел или newline)
              </span>
              <textarea
                value={cadNumsText}
                onChange={(e) => setCadNumsText(e.target.value)}
                placeholder="66:41:0204016&#10;66:41:0204017&#10;..."
                rows={6}
                style={{ ...inputStyle, fontFamily: "monospace", fontSize: 12 }}
              />
            </label>
          )}

          <div
            style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}
          >
            <label>
              <span style={labelStyle}>Rate ms (между запросами)</span>
              <input
                type="number"
                min={100}
                max={10000}
                step={100}
                value={rateMs}
                onChange={(e) => setRateMs(parseInt(e.target.value, 10) || 600)}
                style={inputStyle}
              />
            </label>
          </div>

          {sourceKind === "rosreestr_pending" && jobKind !== "quarters" && (
            <div
              style={{
                background: "#fef3c7",
                border: "1px solid #f59e0b",
                borderRadius: 6,
                padding: "10px 14px",
                fontSize: 13,
                color: "#78350f",
              }}
            >
              rosreestr_pending пока работает только для кварталов. Для
              участков/зданий используй manual_list с cad-номерами через
              запятую.
            </div>
          )}

          <div style={{ display: "flex", gap: 12, alignItems: "center" }}>
            <button
              type="button"
              onClick={() => enqueue.mutate()}
              disabled={enqueue.isPending}
              style={primaryBtn}
            >
              {enqueue.isPending ? "Создание…" : "🚀 Запустить bulk-job"}
            </button>
            {lastResp && (
              <span style={{ fontSize: 13, color: "#5b6066" }}>
                Job <code>#{lastResp.job_id}</code> создан, targets:{" "}
                <strong>
                  {lastResp.targets_total.toLocaleString("ru-RU")}
                </strong>
                , примерно <strong>{lastResp.estimate_minutes}</strong> мин
              </span>
            )}
            {enqueue.error && (
              <span style={{ color: "#b3261e", fontSize: 13 }}>
                {(enqueue.error as Error).message}
              </span>
            )}
          </div>
        </div>
      </section>

      {/* Список jobs */}
      <section style={{ ...cardStyle, marginTop: 16 }}>
        <h3 style={sectionTitle}>📜 Geo jobs (top 30, refresh 4с)</h3>
        <div style={{ overflowX: "auto" }}>
          <table style={tableStyle}>
            <thead style={{ background: "#f9fafb" }}>
              <tr>
                <th style={th}>job_id</th>
                <th style={th}>name</th>
                <th style={th}>kind</th>
                <th style={th}>started</th>
                <th style={th}>duration</th>
                <th style={{ ...th, textAlign: "right" }}>progress</th>
                <th style={{ ...th, textAlign: "right" }}>done/total</th>
                <th style={{ ...th, textAlign: "right" }}>failed</th>
                <th style={{ ...th, textAlign: "right" }}>req/waf</th>
                <th style={th}>status</th>
                <th style={th}></th>
              </tr>
            </thead>
            <tbody>
              {jobs.data?.map((j) => {
                const heartbeatLag = j.heartbeat_at
                  ? (Date.now() - new Date(j.heartbeat_at).getTime()) / 1000
                  : null;
                const isStale =
                  j.status === "running" &&
                  heartbeatLag !== null &&
                  heartbeatLag > 300;
                const color = STATUS_COLOR[j.status] ?? "#6b7280";
                return (
                  <tr key={j.job_id} style={{ borderTop: "1px solid #f3f4f6" }}>
                    <td style={{ ...td, fontFamily: "monospace" }}>
                      {j.job_id}
                    </td>
                    <td
                      style={{
                        ...td,
                        maxWidth: 200,
                        overflow: "hidden",
                        textOverflow: "ellipsis",
                        whiteSpace: "nowrap",
                      }}
                    >
                      {j.name ?? <em style={{ color: "#9ca3af" }}>—</em>}
                    </td>
                    <td style={{ ...td, fontSize: 11 }}>{j.job_kind}</td>
                    <td style={{ ...td, fontSize: 11 }}>
                      {formatIso(j.started_at)}
                    </td>
                    <td style={td}>
                      {diffSecs(j.started_at, j.finished_at ?? j.heartbeat_at)}
                    </td>
                    <td style={{ ...td, textAlign: "right" }}>
                      <div
                        style={{
                          display: "flex",
                          flexDirection: "column",
                          gap: 2,
                          alignItems: "flex-end",
                        }}
                      >
                        <strong>{j.progress_pct.toFixed(1)}%</strong>
                        <div
                          style={{
                            width: 80,
                            height: 6,
                            background: "#e5e7eb",
                            borderRadius: 3,
                            overflow: "hidden",
                          }}
                        >
                          <div
                            style={{
                              width: `${Math.min(j.progress_pct, 100)}%`,
                              height: "100%",
                              background: color,
                            }}
                          />
                        </div>
                      </div>
                    </td>
                    <td
                      style={{
                        ...td,
                        textAlign: "right",
                        fontFamily: "monospace",
                      }}
                    >
                      {j.targets_done.toLocaleString("ru-RU")}/
                      {j.targets_total.toLocaleString("ru-RU")}
                    </td>
                    <td
                      style={{
                        ...td,
                        textAlign: "right",
                        fontFamily: "monospace",
                        color: j.targets_failed ? "#b3261e" : "inherit",
                      }}
                    >
                      {j.targets_failed}
                    </td>
                    <td
                      style={{
                        ...td,
                        textAlign: "right",
                        fontFamily: "monospace",
                        fontSize: 11,
                      }}
                    >
                      {j.requests_count}
                      {j.waf_blocked_count > 0 && (
                        <span style={{ color: "#a16207" }}>
                          {" / waf:"}
                          {j.waf_blocked_count}
                        </span>
                      )}
                    </td>
                    <td style={td}>
                      <span
                        style={{
                          display: "inline-block",
                          padding: "2px 8px",
                          borderRadius: 4,
                          background: color + "22",
                          color,
                          fontWeight: 500,
                        }}
                      >
                        {isStale ? "stale" : j.status}
                      </span>
                    </td>
                    <td style={td}>
                      {(j.status === "running" || j.status === "queued") && (
                        <button
                          type="button"
                          onClick={() => cancelJob.mutate(j.job_id)}
                          style={{
                            ...secondaryBtn,
                            padding: "4px 8px",
                            fontSize: 11,
                          }}
                        >
                          cancel
                        </button>
                      )}
                      {(j.status === "paused" ||
                        j.status === "failed" ||
                        j.status === "cancelled") && (
                        <button
                          type="button"
                          onClick={() => resumeJob.mutate(j.job_id)}
                          style={{
                            ...secondaryBtn,
                            padding: "4px 8px",
                            fontSize: 11,
                            background: "#dbeafe",
                            color: "#1d4ed8",
                          }}
                        >
                          resume
                        </button>
                      )}
                    </td>
                  </tr>
                );
              })}
              {jobs.data?.length === 0 && (
                <tr>
                  <td
                    colSpan={11}
                    style={{
                      ...td,
                      textAlign: "center",
                      color: "#6b7280",
                      padding: 24,
                    }}
                  >
                    Geo-jobs ещё не было
                  </td>
                </tr>
              )}
            </tbody>
          </table>
        </div>
      </section>

      <ScrapeLogsPanel
        scraperType="nspd_geo"
        title="Журнал NSPD geo (per-target)"
        subtitle="из nspd_geo_log · обновляется каждые 3 сек"
        containerStyle={{ marginTop: 16 }}
      />
    </>
  );
}

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",
};
const tableStyle = {
  width: "100%",
  borderCollapse: "collapse" as const,
  fontSize: 12,
};
