"use client";

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

import { apiFetch } from "@/lib/api";
import {
  ResultPanel,
  ScraperPage,
  type ScraperPageConfig,
} from "@/app/scrapers/_components/ScraperPage";

// ── Cian-specific types + hooks ────────────────────────────────────────────

interface ScrapeAroundResp {
  total_fetched: number;
  total_inserted: number;
  total_updated: number;
  by_source: Array<{
    source: string;
    fetched: number;
    inserted: number;
    updated: number;
  }>;
}

interface CianDetailTriggerResp {
  ok: boolean;
  offer_url: string;
  cian_id: number | null;
  price_changes_count: number;
  saved: boolean;
  listing_id: number | null;
}

interface CianNewbuildingTriggerResp {
  ok: boolean;
  zhk_url: string;
  cian_internal_house_id: number | null;
  name: string | null;
  price_dynamics_count: number;
  has_management_company: boolean;
  saved: boolean;
  house_id: number | null;
}

interface TestAuthResp {
  authenticated: boolean;
  userId: number | null;
  reason: string | null;
}

interface UploadCookiesResp {
  ok?: boolean;
  userId?: number;
  cookieCount?: number;
  detail?: string;
}

interface CookieEditorEntry {
  name: string;
  value: string | number | boolean;
  [key: string]: unknown;
}

function convertCookiesFormat(input: string): Record<string, string> {
  const parsed: unknown = JSON.parse(input.trim());
  if (Array.isArray(parsed)) {
    return (parsed as CookieEditorEntry[]).reduce<Record<string, string>>(
      (acc, c) => {
        if (
          typeof c === "object" &&
          c !== null &&
          "name" in c &&
          "value" in c
        ) {
          acc[String(c.name)] = String(c.value);
        }
        return acc;
      },
      {},
    );
  }
  if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) {
    return Object.fromEntries(
      Object.entries(parsed as Record<string, unknown>).map(([k, v]) => [
        k,
        String(v),
      ]),
    );
  }
  throw new Error("Expected JSON array or plain object");
}

function parsePreview(raw: string): {
  cookies: Record<string, string> | null;
  error: string | null;
} {
  if (!raw.trim()) return { cookies: null, error: null };
  try {
    const cookies = convertCookiesFormat(raw);
    return { cookies, error: null };
  } catch (e) {
    return { cookies: null, error: e instanceof Error ? e.message : String(e) };
  }
}

function useScrapeAround() {
  return useMutation<
    ScrapeAroundResp,
    Error,
    { lat: number; lon: number; radius_m: number; multi_room_cian: boolean }
  >({
    mutationFn: (input) =>
      apiFetch<ScrapeAroundResp>("/api/v1/admin/scrape", {
        method: "POST",
        body: JSON.stringify({ ...input, sources: ["cian"] }),
      }),
  });
}

function useScrapeCianDetail() {
  return useMutation<
    CianDetailTriggerResp,
    Error,
    { offer_url: string; listing_id: number | undefined }
  >({
    mutationFn: ({ offer_url, listing_id }) => {
      const qs = new URLSearchParams({ offer_url });
      if (listing_id !== undefined) qs.set("listing_id", String(listing_id));
      return apiFetch<CianDetailTriggerResp>(
        `/api/v1/admin/scrape/cian-detail?${qs.toString()}`,
        { method: "POST" },
      );
    },
  });
}

function useScrapeCianNewbuilding() {
  return useMutation<
    CianNewbuildingTriggerResp,
    Error,
    { zhk_url: string; house_id: number | undefined }
  >({
    mutationFn: ({ zhk_url, house_id }) => {
      const qs = new URLSearchParams({ zhk_url });
      if (house_id !== undefined) qs.set("house_id", String(house_id));
      return apiFetch<CianNewbuildingTriggerResp>(
        `/api/v1/admin/scrape/cian-newbuilding?${qs.toString()}`,
        { method: "POST" },
      );
    },
  });
}

function useTestAuth() {
  return useQuery<TestAuthResp>({
    queryKey: ["cian-test-auth"],
    queryFn: () => apiFetch<TestAuthResp>("/api/v1/admin/scrape/cian/test-auth"),
    staleTime: 30_000,
    refetchInterval: 60_000,
  });
}

// ── Cian auth banner ───────────────────────────────────────────────────────

function CianAuthBanner() {
  const authQ = useTestAuth();
  return (
    <div
      className="scraper-section"
      style={{
        background: "var(--surface-2, #f0f4ff)",
        borderLeft: "4px solid var(--accent, #3b6cff)",
        padding: "12px 16px",
      }}
    >
      <p style={{ margin: 0, fontSize: "0.9rem" }}>
        <strong>Stage 7: Cian Valuation</strong> требует загруженных cookies.{" "}
        Управление cookies — в разделе «Cookies» ниже.
      </p>
      <p
        style={{
          margin: "6px 0 0",
          fontSize: "0.85rem",
          color: "var(--muted, #5b6066)",
        }}
      >
        Статус сессии:{" "}
        {authQ.isPending && "проверяем…"}
        {authQ.error && (
          <span style={{ color: "var(--error, #c0392b)" }}>
            ошибка проверки
          </span>
        )}
        {authQ.data && (
          <span
            style={{
              color: authQ.data.authenticated
                ? "var(--success, #27ae60)"
                : "var(--error, #c0392b)",
              fontWeight: 600,
            }}
          >
            {authQ.data.authenticated
              ? `авторизован (userId: ${authQ.data.userId ?? "—"})`
              : `не авторизован — ${authQ.data.reason ?? "неизвестная причина"}`}
          </span>
        )}
      </p>
    </div>
  );
}

// ── Cian cookies section (formerly standalone page) ────────────────────────

function CianCookiesSection() {
  const [rawInput, setRawInput] = useState("");
  const [showInstructions, setShowInstructions] = useState(false);

  const preview = parsePreview(rawInput);
  const previewKeys = preview.cookies ? Object.keys(preview.cookies) : [];

  const uploadMutation = useMutation({
    mutationFn: () => {
      if (!preview.cookies) {
        throw new Error("Invalid JSON — fix the input first");
      }
      return apiFetch<UploadCookiesResp>(
        "/api/v1/admin/scrape/cian/upload-cookies",
        {
          method: "POST",
          body: JSON.stringify(preview.cookies),
        },
      );
    },
    onSuccess: () => setRawInput(""),
  });

  const testAuthQuery = useQuery({
    queryKey: ["cian-test-auth", "manual"],
    queryFn: () =>
      apiFetch<TestAuthResp>("/api/v1/admin/scrape/cian/test-auth"),
    enabled: false,
    retry: false,
  });

  const canUpload =
    !!preview.cookies && previewKeys.length > 0 && !uploadMutation.isPending;

  return (
    <section className="scraper-section">
      <h2>Cookies (DMIR_AUTH)</h2>
      <p className="scraper-hint">
        Обновление сессии Cian для скрапера оценщика без перезапуска backend.
        Endpoints:{" "}
        <code>POST /api/v1/admin/scrape/cian/upload-cookies</code> ·{" "}
        <code>GET /api/v1/admin/scrape/cian/test-auth</code>
      </p>

      {/* Instructions collapsible */}
      <button
        onClick={() => setShowInstructions((v) => !v)}
        style={{
          background: "none",
          border: "none",
          padding: 0,
          cursor: "pointer",
          fontSize: 14,
          fontWeight: 600,
          color: "var(--accent, #1d4ed8)",
          display: "flex",
          alignItems: "center",
          gap: 6,
          marginBottom: showInstructions ? 0 : 12,
        }}
      >
        <span>{showInstructions ? "▼" : "▶"}</span>
        Как получить cookies (инструкция)
      </button>
      {showInstructions && (
        <ol
          style={{
            marginTop: 12,
            marginBottom: 12,
            paddingLeft: 20,
            fontSize: 13,
            color: "var(--fg, #374151)",
            lineHeight: 1.7,
          }}
        >
          <li>
            Открой{" "}
            <a
              href="https://www.cian.ru/"
              target="_blank"
              rel="noopener noreferrer"
            >
              cian.ru
            </a>{" "}
            и войди под нужным аккаунтом.
          </li>
          <li>
            Установи расширение <strong>Cookie-Editor</strong> (Chrome/Firefox){" "}
            <em>или</em> открой DevTools → Application → Cookies →
            www.cian.ru.
          </li>
          <li>
            <strong>Cookie-Editor:</strong> нажми Export → Export as JSON →
            скопируй массив{" "}
            <code>[{"{"}name, value, ...{"}"}]</code>.
          </li>
          <li>
            <strong>DevTools (ручной способ):</strong> собери объект{" "}
            <code>
              {"{"}
              {'"'}name1{'"'}: {'"'}val1{'"'}, ...{"}"}
            </code>
            .
          </li>
          <li>Вставь JSON в поле ниже — формат определится автоматически.</li>
          <li>
            Убедись что preview показывает нужные ключи, затем нажми Upload.
          </li>
        </ol>
      )}

      {/* Cookie input */}
      <div className="scraper-form">
        <label className="full">
          <textarea
            value={rawInput}
            onChange={(e) => {
              setRawInput(e.target.value);
              uploadMutation.reset();
            }}
            rows={10}
            placeholder={`Формат 1 — Cookie-Editor array:\n[\n  {"name": "session_key", "value": "abc123", "domain": ".cian.ru"},\n  {"name": "_CSRF", "value": "xyz", "domain": ".cian.ru"}\n]\n\nФормат 2 — dict (DevTools manual):\n{\n  "session_key": "abc123",\n  "_CSRF": "xyz"\n}`}
            style={{
              fontFamily: "var(--font-mono, ui-monospace, monospace)",
              fontSize: 12,
              resize: "vertical",
              minHeight: 200,
              padding: "8px 10px",
              border: "1px solid var(--border-strong, #d1d5db)",
              borderRadius: 6,
              background: "#fff",
              color: "var(--fg, #111)",
              width: "100%",
            }}
          />
        </label>

        {rawInput.trim() && (
          <div
            className={`scraper-result${preview.error ? " scraper-result--error" : ""}`}
            style={{ fontFamily: "inherit" }}
          >
            {preview.error ? (
              <span>JSON error: {preview.error}</span>
            ) : (
              <span style={{ color: "var(--success, #0a7a3a)" }}>
                Detected <strong>{previewKeys.length} cookies</strong>:{" "}
                {previewKeys.slice(0, 8).join(", ")}
                {previewKeys.length > 8
                  ? ` … +${previewKeys.length - 8} more`
                  : ""}
              </span>
            )}
          </div>
        )}

        <button
          onClick={(e) => {
            e.preventDefault();
            uploadMutation.mutate();
          }}
          disabled={!canUpload}
          title={!preview.cookies ? "Вставьте корректный JSON" : undefined}
        >
          {uploadMutation.isPending ? "Uploading…" : "Upload Cookies"}
        </button>

        <button
          onClick={(e) => {
            e.preventDefault();
            void testAuthQuery.refetch();
          }}
          disabled={testAuthQuery.isFetching}
          style={{
            background: "var(--surface-2, #f3f4f6)",
            color: "var(--fg, #1f2937)",
            border: "1px solid var(--border-strong, #d1d5db)",
            justifySelf: "start",
          }}
        >
          {testAuthQuery.isFetching ? "Checking…" : "Test Current Session"}
        </button>
      </div>

      {uploadMutation.isSuccess && (
        <div
          className="scraper-result"
          style={{
            fontFamily: "inherit",
            background: uploadMutation.data.ok
              ? "var(--success-soft, #f0fdf4)"
              : "var(--danger-soft, #fef2f2)",
            borderColor: uploadMutation.data.ok
              ? "var(--success, #0a7a3a)"
              : "var(--danger, #b3261e)",
            color: uploadMutation.data.ok
              ? "var(--success, #0a7a3a)"
              : "var(--danger, #b3261e)",
          }}
        >
          {uploadMutation.data.ok ? (
            <>
              <strong>Cookies uploaded.</strong> userId={" "}
              {uploadMutation.data.userId ?? "—"} · cookieCount={" "}
              {uploadMutation.data.cookieCount ?? previewKeys.length}
            </>
          ) : (
            <>
              <strong>Upload failed:</strong>{" "}
              {uploadMutation.data.detail ?? "Unknown error"}
            </>
          )}
        </div>
      )}

      {uploadMutation.isError && (
        <div className="scraper-result scraper-result--error">
          <strong>Error:</strong> {String(uploadMutation.error)}
        </div>
      )}

      {(testAuthQuery.isSuccess || testAuthQuery.isError) && (
        <div
          className="scraper-result"
          style={{
            fontFamily: "inherit",
            background:
              testAuthQuery.isSuccess && testAuthQuery.data.authenticated
                ? "var(--success-soft, #f0fdf4)"
                : "var(--danger-soft, #fef2f2)",
            borderColor:
              testAuthQuery.isSuccess && testAuthQuery.data.authenticated
                ? "var(--success, #0a7a3a)"
                : "var(--danger, #b3261e)",
            color:
              testAuthQuery.isSuccess && testAuthQuery.data.authenticated
                ? "var(--success, #0a7a3a)"
                : "var(--danger, #b3261e)",
          }}
        >
          {testAuthQuery.isError ? (
            <>
              <strong>Test auth error:</strong> {String(testAuthQuery.error)}
            </>
          ) : testAuthQuery.data.authenticated ? (
            <>
              <strong>Authenticated.</strong> userId={" "}
              {testAuthQuery.data.userId ?? "—"}
            </>
          ) : (
            <>
              <strong>Not authenticated.</strong>{" "}
              {testAuthQuery.data.reason
                ? `Reason: ${testAuthQuery.data.reason}`
                : "Session expired or cookies invalid."}
            </>
          )}
        </div>
      )}
    </section>
  );
}

// ── Cian extra triggers ────────────────────────────────────────────────────

function CianExtraTriggers() {
  const authQ = useTestAuth();

  // 1. Search around
  const [lat, setLat] = useState("56.8400");
  const [lon, setLon] = useState("60.6050");
  const [radius, setRadius] = useState("1500");
  const [multiRoom, setMultiRoom] = useState(false);
  const aroundMut = useScrapeAround();

  // 2. Detail
  const [detailUrl, setDetailUrl] = useState("");
  const [detailListingId, setDetailListingId] = useState("");
  const detailMut = useScrapeCianDetail();

  // 3. Newbuilding
  const [nbZhkUrl, setNbZhkUrl] = useState("");
  const [nbHouseId, setNbHouseId] = useState("");
  const nbMut = useScrapeCianNewbuilding();

  return (
    <>
      {/* Auth banner */}
      <div
        className="scraper-section"
        style={{
          background: "var(--surface-2, #f0f4ff)",
          borderLeft: "4px solid var(--accent, #3b6cff)",
          padding: "12px 16px",
        }}
      >
        <p style={{ margin: 0, fontSize: "0.9rem" }}>
          <strong>Cian Valuation</strong> требует загруженных cookies.
          Управление cookies в разделе «Cookies» ниже.
        </p>
        <p
          style={{
            margin: "6px 0 0",
            fontSize: "0.85rem",
            color: "var(--muted, #5b6066)",
          }}
        >
          Статус сессии:{" "}
          {authQ.isPending && "проверяем…"}
          {authQ.error && (
            <span style={{ color: "var(--error, #c0392b)" }}>
              ошибка проверки
            </span>
          )}
          {authQ.data && (
            <span
              style={{
                color: authQ.data.authenticated
                  ? "var(--success, #27ae60)"
                  : "var(--error, #c0392b)",
                fontWeight: 600,
              }}
            >
              {authQ.data.authenticated
                ? `авторизован (userId: ${authQ.data.userId ?? "—"})`
                : `не авторизован — ${authQ.data.reason ?? "неизвестная причина"}`}
            </span>
          )}
        </p>
      </div>

      {/* 1. Search around */}
      <div className="scraper-section">
        <h3>1. Search around (общий /admin/scrape)</h3>
        <p className="scraper-hint">
          Запускает Cian SERP pipeline для одного якоря: search → save_listings.
          Поддерживает multi_room режим (~4× лотов, отдельный запрос на каждое
          кол-во комнат).
        </p>
        <form
          className="scraper-form"
          onSubmit={(e) => {
            e.preventDefault();
            aroundMut.mutate({
              lat: parseFloat(lat),
              lon: parseFloat(lon),
              radius_m: parseInt(radius, 10),
              multi_room_cian: multiRoom,
            });
          }}
        >
          <label>
            Lat
            <input
              value={lat}
              onChange={(e) => setLat(e.target.value)}
              required
            />
          </label>
          <label>
            Lon
            <input
              value={lon}
              onChange={(e) => setLon(e.target.value)}
              required
            />
          </label>
          <label>
            Radius (м)
            <input
              value={radius}
              onChange={(e) => setRadius(e.target.value)}
              required
            />
          </label>
          <label className="checkbox">
            <input
              type="checkbox"
              checked={multiRoom}
              onChange={(e) => setMultiRoom(e.target.checked)}
            />
            multi_room_cian (4 запроса по комнатам)
          </label>
          <button type="submit" disabled={aroundMut.isPending}>
            {aroundMut.isPending ? "Скрейпим…" : "Запустить"}
          </button>
        </form>
        <ResultPanel mut={aroundMut} />
      </div>

      {/* 2. Detail */}
      <div className="scraper-section">
        <h3>2. Detail page enrichment</h3>
        <p className="scraper-hint">
          Парсинг конкретного объявления Cian по URL: цена, история цен,
          сохранение в cian_price_dynamics. Если <code>listing_id</code> не
          указан — только debug-режим без записи в БД.
        </p>
        <form
          className="scraper-form"
          onSubmit={(e) => {
            e.preventDefault();
            detailMut.mutate({
              offer_url: detailUrl,
              listing_id:
                detailListingId !== ""
                  ? parseInt(detailListingId, 10)
                  : undefined,
            });
          }}
        >
          <label className="full">
            offer_url
            <input
              type="url"
              value={detailUrl}
              onChange={(e) => setDetailUrl(e.target.value)}
              required
              placeholder="https://ekb.cian.ru/sale/flat/XXXXX/"
            />
          </label>
          <label>
            listing_id (опционально)
            <input
              type="number"
              value={detailListingId}
              onChange={(e) => setDetailListingId(e.target.value)}
              placeholder="оставьте пустым для debug-режима"
            />
          </label>
          <button type="submit" disabled={detailMut.isPending}>
            {detailMut.isPending ? "Парсим…" : "Запустить"}
          </button>
        </form>
        <ResultPanel mut={detailMut} />
      </div>

      {/* 3. Newbuilding */}
      <div className="scraper-section">
        <h3>3. Новостройки (ЖК scrape)</h3>
        <p className="scraper-hint">
          Скрейп landing-страницы ЖК Cian: динамика цен, управляющая компания.
          Если <code>house_id</code> не указан — только debug-режим без записи
          в БД.
        </p>
        <form
          className="scraper-form"
          onSubmit={(e) => {
            e.preventDefault();
            nbMut.mutate({
              zhk_url: nbZhkUrl,
              house_id:
                nbHouseId !== "" ? parseInt(nbHouseId, 10) : undefined,
            });
          }}
        >
          <label className="full">
            zhk_url
            <input
              type="url"
              value={nbZhkUrl}
              onChange={(e) => setNbZhkUrl(e.target.value)}
              required
              placeholder="https://zhk-XXX-ekb-i.cian.ru/"
            />
          </label>
          <label>
            house_id (опционально)
            <input
              type="number"
              value={nbHouseId}
              onChange={(e) => setNbHouseId(e.target.value)}
              placeholder="оставьте пустым для debug-режима"
            />
          </label>
          <button type="submit" disabled={nbMut.isPending}>
            {nbMut.isPending ? "Парсим…" : "Запустить"}
          </button>
        </form>
        <ResultPanel mut={nbMut} />
      </div>

      {/* 4. Valuation placeholder */}
      <div className="scraper-section">
        <h3>4. Valuation Calculator (оценка квартиры)</h3>
        <p className="scraper-hint">
          Calculator вызывается автоматически из /estimate flow (Stage 9).
          Отдельный admin trigger будет добавлен позже.
        </p>
      </div>
    </>
  );
}

// ── Page config + export ───────────────────────────────────────────────────

const CIAN_CONFIG: ScraperPageConfig = {
  source: "cian",
  activeTab: "cian",
  displayName: "Cian скрапер",
  subtitle:
    "Ручной запуск sub-pipeline'ов CianScraper. Для debug и теста после изменений.",
  scheduleSource: "cian_city_sweep",
  paramConfig: {
    hasDetailParams: true,
    hasEnrichAddress: false,
    defaults: {
      pages_per_anchor: 3,
      detail_top_n: 20,
      request_delay_sec: 5,
      radius_m: 1500,
      enrich_houses: true,
    },
  },
  delayMin: 3,
  delayMax: 30,
};

export default function CianScraperPage() {
  return (
    <ScraperPage
      config={{
        ...CIAN_CONFIG,
        extraTriggers: <CianExtraTriggers />,
        extraSections: <CianCookiesSection />,
      }}
    />
  );
}
