import { ChangeEvent, ReactNode, useEffect, useMemo, useState } from "react";
import { useForm } from "react-hook-form";
import { useQueryClient } from "react-query";
import clsx from "clsx";

import {
  isUnauthorizedError,
  useAdminBlockchain,
  useAdminCacheCleanup,
  useAdminCacheSetLimits,
  useAdminLogin,
  useAdminLogout,
  useAdminNodeSetRole,
  useAdminNodes,
  useAdminOverview,
  useAdminStatus,
  useAdminStorage,
  useAdminSyncSetLimits,
  useAdminSystem,
  useAdminUploads,
} from "~/shared/services/admin";
import { clearAdminAuth, getAdminAuthSnapshot } from "~/shared/libs/admin-auth";

const numberFormatter = new Intl.NumberFormat("ru-RU");
const dateTimeFormatter = new Intl.DateTimeFormat("ru-RU", {
  dateStyle: "short",
  timeStyle: "medium",
});

const formatBytes = (input?: number | null) => {
  if (!input) {
    return "0 B";
  }
  const units = ["B", "KB", "MB", "GB", "TB", "PB"] as const;
  let value = input;
  let unitIndex = 0;
  while (value >= 1024 && unitIndex < units.length - 1) {
    value /= 1024;
    unitIndex += 1;
  }
  return `${value.toFixed(value >= 10 || value < 0.1 ? 0 : 1)} ${units[unitIndex]}`;
};

const formatDate = (iso?: string | null) => {
  if (!iso) {
    return "—";
  }
  const date = new Date(iso);
  if (Number.isNaN(date.getTime())) {
    return iso;
  }
  return dateTimeFormatter.format(date);
};

const Section = ({
  id,
  title,
  description,
  children,
  actions,
}: {
  id: string;
  title: string;
  description?: string;
  children: ReactNode;
  actions?: ReactNode;
}) => {
  return (
    <section id={id} className="rounded-2xl bg-slate-900/60 p-6 shadow-inner shadow-black/40 ring-1 ring-slate-800">
      <div className="mb-6 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
        <div>
          <h2 className="text-2xl font-semibold text-slate-100">{title}</h2>
          {description ? (
            <p className="mt-1 max-w-3xl text-sm text-slate-400">{description}</p>
          ) : null}
        </div>
        {actions ? <div className="flex h-full items-center gap-3">{actions}</div> : null}
      </div>
      <div className="space-y-6 text-sm text-slate-200">{children}</div>
    </section>
  );
};

type BadgeTone = "neutral" | "success" | "danger" | "warn";

const Badge = ({ children, tone = "neutral" }: { children: ReactNode; tone?: BadgeTone }) => {
  const toneMap: Record<BadgeTone, string> = {
    neutral: "bg-slate-800 text-slate-200",
    success: "bg-emerald-900/70 text-emerald-200",
    danger: "bg-rose-900/60 text-rose-100",
    warn: "bg-amber-900/60 text-amber-100",
  };
  return (
    <span className={clsx("inline-flex items-center gap-1 rounded-full px-3 py-1 text-xs font-semibold uppercase tracking-wide", toneMap[tone])}>
      {children}
    </span>
  );
};

type FlashMessage = {
  type: "success" | "error" | "info";
  message: string;
};

type AuthState = "checking" | "authorized" | "unauthorized";

const ADMIN_SECTIONS = [
  { id: "overview", label: "Обзор" },
  { id: "storage", label: "Хранилище" },
  { id: "uploads", label: "Загрузки" },
  { id: "system", label: "Система" },
  { id: "blockchain", label: "Блокчейн" },
  { id: "nodes", label: "Ноды" },
  { id: "status", label: "Статус & лимиты" },
] as const;

type CacheLimitsFormValues = {
  max_gb: number;
  ttl_days: number;
};

type CacheFitFormValues = {
  max_gb: number;
};

type SyncLimitsFormValues = {
  max_concurrent_pins: number;
  disk_low_watermark_pct: number;
};

const initialAuthState = getAdminAuthSnapshot().token ? "checking" : "unauthorized";

export const AdminPage = () => {
  const queryClient = useQueryClient();
  const [authState, setAuthState] = useState<AuthState>(initialAuthState);
  const [activeSection, setActiveSection] = useState<(typeof ADMIN_SECTIONS)[number]["id"]>("overview");
  const [token, setToken] = useState("");
  const [flash, setFlash] = useState<FlashMessage | null>(null);

  useEffect(() => {
    if (!flash) {
      return;
    }
    const timeout = setTimeout(() => setFlash(null), 6000);
    return () => clearTimeout(timeout);
  }, [flash]);

  useEffect(() => {
    if (authState === "unauthorized") {
      queryClient.removeQueries({
        predicate: (query) => Array.isArray(query.queryKey) && query.queryKey[0] === "admin",
      });
    }
  }, [authState, queryClient]);

  const overviewQuery = useAdminOverview({
    enabled: authState !== "unauthorized",
    retry: false,
    onSuccess: () => {
      setAuthState((prev) => (prev === "authorized" ? prev : "authorized"));
    },
    onError: (error) => {
      if (isUnauthorizedError(error)) {
        clearAdminAuth();
        setAuthState("unauthorized");
        setFlash({ type: "info", message: "Введите админ-токен, чтобы продолжить" });
        return;
      }
      setFlash({ type: "error", message: `Не удалось загрузить обзор: ${error.message}` });
    },
  });

  const isDataEnabled = authState === "authorized";

  const storageQuery = useAdminStorage({
    enabled: isDataEnabled,
    refetchInterval: 60_000,
  });
  const uploadsQuery = useAdminUploads({
    enabled: isDataEnabled,
    refetchInterval: 30_000,
  });
  const systemQuery = useAdminSystem({
    enabled: isDataEnabled,
    refetchInterval: 60_000,
  });
  const blockchainQuery = useAdminBlockchain({
    enabled: isDataEnabled,
    refetchInterval: 30_000,
  });
  const nodesQuery = useAdminNodes({
    enabled: isDataEnabled,
    refetchInterval: 60_000,
  });
  const statusQuery = useAdminStatus({
    enabled: isDataEnabled,
    refetchInterval: 30_000,
  });

  const loginMutation = useAdminLogin({
    onSuccess: async () => {
      setAuthState("authorized");
      setFlash({ type: "success", message: "Админ-сессия активирована" });
      setToken("");
      await queryClient.invalidateQueries({
        predicate: (query) => Array.isArray(query.queryKey) && query.queryKey[0] === "admin",
      });
    },
    onError: (error) => {
      if (isUnauthorizedError(error)) {
        clearAdminAuth();
        setFlash({ type: "error", message: "Неверный токен" });
        return;
      }
      setFlash({ type: "error", message: `Ошибка входа: ${error.message}` });
    },
  });

  const logoutMutation = useAdminLogout({
    onSuccess: async () => {
      setAuthState("unauthorized");
      setFlash({ type: "info", message: "Сессия завершена" });
      await queryClient.invalidateQueries({
        predicate: (query) => Array.isArray(query.queryKey) && query.queryKey[0] === "admin",
      });
    },
    onError: (error) => {
      setFlash({ type: "error", message: `Ошибка выхода: ${error.message}` });
    },
  });

  const cacheLimitsMutation = useAdminCacheSetLimits({
    onSuccess: async () => {
      setFlash({ type: "success", message: "Лимиты кэша обновлены" });
      await statusQuery.refetch();
    },
    onError: (error) => {
      setFlash({ type: "error", message: `Не удалось сохранить лимиты кэша: ${error.message}` });
    },
  });

  const cacheCleanupMutation = useAdminCacheCleanup({
    onSuccess: async (data) => {
      const removed = typeof data.removed === "number" ? `Удалено файлов: ${data.removed}` : "";
      setFlash({ type: "success", message: `Очистка кэша выполнена. ${removed}`.trim() });
      await statusQuery.refetch();
    },
    onError: (error) => {
      setFlash({ type: "error", message: `Ошибка очистки кэша: ${error.message}` });
    },
  });

  const syncLimitsMutation = useAdminSyncSetLimits({
    onSuccess: async () => {
      setFlash({ type: "success", message: "Параметры синхронизации обновлены" });
      await statusQuery.refetch();
    },
    onError: (error) => {
      setFlash({ type: "error", message: `Не удалось обновить лимиты синхронизации: ${error.message}` });
    },
  });

  const nodeRoleMutation = useAdminNodeSetRole({
    onSuccess: async ({ node }) => {
      setFlash({
        type: "success",
        message: `Роль узла ${node.public_key ?? node.ip ?? ""} обновлена до ${node.role}`,
      });
      await nodesQuery.refetch();
    },
    onError: (error) => {
      setFlash({ type: "error", message: `Не удалось обновить роль узла: ${error.message}` });
    },
  });

  const cacheLimitsForm = useForm<CacheLimitsFormValues>({
    values: {
      max_gb: statusQuery.data?.limits.DERIVATIVE_CACHE_MAX_GB ?? 50,
      ttl_days: statusQuery.data?.limits.DERIVATIVE_CACHE_TTL_DAYS ?? 0,
    },
  });

  const cacheFitForm = useForm<CacheFitFormValues>({
    defaultValues: {
      max_gb: statusQuery.data?.limits.DERIVATIVE_CACHE_MAX_GB ?? 50,
    },
  });

  const syncLimitsForm = useForm<SyncLimitsFormValues>({
    values: {
      max_concurrent_pins: statusQuery.data?.limits.SYNC_MAX_CONCURRENT_PINS ?? 4,
      disk_low_watermark_pct: statusQuery.data?.limits.SYNC_DISK_LOW_WATERMARK_PCT ?? 90,
    },
  });

  useEffect(() => {
    cacheLimitsForm.reset({
      max_gb: statusQuery.data?.limits.DERIVATIVE_CACHE_MAX_GB ?? 50,
      ttl_days: statusQuery.data?.limits.DERIVATIVE_CACHE_TTL_DAYS ?? 0,
    });
    cacheFitForm.reset({
      max_gb: statusQuery.data?.limits.DERIVATIVE_CACHE_MAX_GB ?? 50,
    });
    syncLimitsForm.reset({
      max_concurrent_pins: statusQuery.data?.limits.SYNC_MAX_CONCURRENT_PINS ?? 4,
      disk_low_watermark_pct: statusQuery.data?.limits.SYNC_DISK_LOW_WATERMARK_PCT ?? 90,
    });
  }, [statusQuery.data, cacheLimitsForm, cacheFitForm, syncLimitsForm]);

  const overviewCards = useMemo(() => {
    const data = overviewQuery.data;
    if (!data) {
      return [];
    }
    return [
      {
        label: "Хост",
        value: data.project.host || "локальный" ,
        helper: data.project.name,
      },
      {
        label: "TON Master",
        value: data.node.ton_master,
        helper: "Платформа",
      },
      {
        label: "Service Wallet",
        value: data.node.service_wallet,
        helper: data.node.id,
      },
      {
        label: "Контент",
        value: `${numberFormatter.format(data.content.encrypted_total)} зашифр.`,
        helper: `${numberFormatter.format(data.content.derivatives_ready)} деривативов`,
      },
      {
        label: "IPFS Repo",
        value: formatBytes(
          Number((data.ipfs.repo as Record<string, unknown>)?.RepoSize ?? 0),
        ),
        helper: "Размер репозитория",
      },
      {
        label: "Bitswap",
        value: numberFormatter.format(
          Number((data.ipfs.bitswap as Record<string, unknown>)?.Peers ?? 0),
        ),
        helper: "Пиры",
      },
      {
        label: "Билд",
        value: data.codebase.commit ?? "n/a",
        helper: data.codebase.branch ?? "",
      },
      {
        label: "Python",
        value: data.runtime.python,
        helper: data.runtime.implementation,
      },
    ];
  }, [overviewQuery.data]);

  const handleRefreshAll = async () => {
    await queryClient.invalidateQueries({
      predicate: (query) => Array.isArray(query.queryKey) && query.queryKey[0] === "admin",
    });
  };

  const renderFlash = () => {
    if (!flash) {
      return null;
    }
    const tone =
      flash.type === "success"
        ? "border-emerald-500/60 bg-emerald-500/10 text-emerald-100"
        : flash.type === "error"
        ? "border-rose-500/60 bg-rose-500/10 text-rose-100"
        : "border-sky-500/60 bg-sky-500/10 text-sky-100";
    return (
      <div className={clsx("mb-6 rounded-lg border px-4 py-3 text-sm", tone)}>
        {flash.message}
      </div>
    );
  };

  const renderLoginPanel = () => {
    return (
      <div className="mx-auto max-w-xl rounded-2xl border border-slate-800 bg-slate-900/60 p-8 shadow-xl shadow-black/50">
        <h1 className="text-3xl font-semibold text-slate-100">Админ-панель узла</h1>
        <p className="mt-2 text-sm text-slate-400">
          Введите секретный токен ADMIN_API_TOKEN, чтобы получить доступ к мониторингу и управлению.
        </p>
        <form
          className="mt-6 space-y-4"
          onSubmit={(event) => {
            event.preventDefault();
            if (!token) {
              setFlash({ type: "info", message: "Введите токен" });
              return;
            }
            loginMutation.mutate({ secret: token });
          }}
        >
          <label className="block text-sm font-medium text-slate-200">
            Админ-токен
            <input
              type="password"
              className="mt-1 w-full rounded-lg border border-slate-700 bg-slate-950 px-3 py-2 text-base text-slate-100 shadow-inner focus:border-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-500/40"
              value={token}
              onChange={(event) => setToken(event.target.value)}
              placeholder="••••••"
              autoComplete="off"
            />
          </label>
          <div className="flex items-center justify-between">
            <button
              type="submit"
              className="inline-flex items-center justify-center rounded-lg bg-sky-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-sky-500 focus:outline-none focus:ring-2 focus:ring-sky-500/60 disabled:cursor-not-allowed disabled:bg-slate-700"
              disabled={loginMutation.isLoading}
            >
              {loginMutation.isLoading ? "Проверяем…" : "Войти"}
            </button>
            <span className="text-xs text-slate-500">
              Cookie max-age {Math.round(172800 / 3600)} ч
            </span>
          </div>
        </form>
      </div>
    );
  };

  const renderOverview = () => {
    if (!overviewQuery.data) {
      return overviewQuery.isLoading ? <p>Загрузка обзора…</p> : <p>Нет данных.</p>;
    }
    const { project, services, ton, runtime, ipfs } = overviewQuery.data;
    return (
      <Section
        id="overview"
        title="Обзор"
        description="Краткий срез состояния узла, окружения и служб"
        actions={
          <button
            type="button"
            className="rounded-lg bg-slate-800 px-3 py-2 text-xs font-semibold text-slate-200 ring-1 ring-slate-700 transition hover:bg-slate-700"
            onClick={() => overviewQuery.refetch()}
          >
            Обновить
          </button>
        }
      >
        <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-4">
          {overviewCards.map((card) => (
            <div key={card.label} className="rounded-xl bg-slate-950/60 p-4 shadow-inner shadow-black/40 ring-1 ring-slate-800">
              <p className="text-xs uppercase tracking-wide text-slate-500">{card.label}</p>
              <p className="mt-2 break-all text-lg font-semibold text-slate-100">{card.value}</p>
              {card.helper ? <p className="mt-1 text-xs text-slate-500">{card.helper}</p> : null}
            </div>
          ))}
        </div>

        <div className="grid gap-6 lg:grid-cols-2">
          <div className="space-y-3">
            <h3 className="text-lg font-semibold text-slate-100">Проект</h3>
            <dl className="grid grid-cols-1 gap-3 sm:grid-cols-2">
              <InfoRow label="Хост">{project.host || "—"}</InfoRow>
              <InfoRow label="Имя">{project.name}</InfoRow>
              <InfoRow label="Приватность">{project.privacy}</InfoRow>
              <InfoRow label="TON Testnet">{ton.testnet ? "Да" : "Нет"}</InfoRow>
              <InfoRow label="TON API Key">{ton.api_key_configured ? "Настроен" : "Нет"}</InfoRow>
              <InfoRow label="TON Host">{ton.host || "—"}</InfoRow>
            </dl>
          </div>
          <div className="space-y-3">
            <h3 className="text-lg font-semibold text-slate-100">Среда выполнения</h3>
            <dl className="grid grid-cols-1 gap-3 sm:grid-cols-2">
              <InfoRow label="Python">{runtime.python}</InfoRow>
              <InfoRow label="Имплементация">{runtime.implementation}</InfoRow>
              <InfoRow label="Платформа">{runtime.platform}</InfoRow>
              <InfoRow label="UTC сейчас">{formatDate(runtime.utc_now)}</InfoRow>
            </dl>
          </div>
        </div>

        <div className="grid gap-6 lg:grid-cols-2">
          <div className="space-y-3">
            <h3 className="text-lg font-semibold text-slate-100">IPFS</h3>
            <div className="grid gap-3 rounded-xl bg-slate-950/60 p-4 ring-1 ring-slate-800">
              <InfoRow label="ID">{(ipfs.identity as Record<string, unknown>)?.ID ?? "—"}</InfoRow>
              <InfoRow label="Agent">{(ipfs.identity as Record<string, unknown>)?.AgentVersion ?? "—"}</InfoRow>
              <InfoRow label="Peers">{numberFormatter.format(Number((ipfs.bitswap as Record<string, unknown>)?.Peers ?? 0))}</InfoRow>
              <InfoRow label="Repo size">{formatBytes(Number((ipfs.repo as Record<string, unknown>)?.RepoSize ?? 0))}</InfoRow>
              <InfoRow label="Storage max">{formatBytes(Number((ipfs.repo as Record<string, unknown>)?.StorageMax ?? 0))}</InfoRow>
            </div>
          </div>
          <div className="space-y-3">
            <h3 className="text-lg font-semibold text-slate-100">Сервисы</h3>
            <ul className="space-y-2">
              {services.length === 0 ? (
                <li className="text-sm text-slate-400">Нет зарегистрированных сервисов</li>
              ) : (
                services.map((service) => {
                  const status = service.status ?? "—";
                  const tone: "success" | "warn" | "danger" | "neutral" = status.includes("working")
                    ? "success"
                    : status.includes("timeout")
                    ? "danger"
                    : "neutral";
                  return (
                    <li
                      key={service.name}
                      className="flex items-center justify-between rounded-lg bg-slate-950/40 px-3 py-2 ring-1 ring-slate-800"
                    >
                      <span className="text-sm font-medium text-slate-100">{service.name}</span>
                      <div className="flex items-center gap-3 text-xs text-slate-400">
                        <span>
                          {service.last_reported_seconds != null
                            ? `Обновлён ${Math.round(service.last_reported_seconds)} сек назад`
                            : "Нет данных"}
                        </span>
                        <Badge tone={tone}>{status}</Badge>
                      </div>
                    </li>
                  );
                })
              )}
            </ul>
          </div>
        </div>
      </Section>
    );
  };

  const renderStorage = () => {
    if (storageQuery.isLoading) {
      return <Section id="storage" title="Хранилище" description="Пути, размеры и деривативы">Загрузка…</Section>;
    }
    if (!storageQuery.data) {
      return null;
    }
    const { directories, disk, derivatives } = storageQuery.data;
    return (
      <Section id="storage" title="Хранилище" description="Пути на диске, использование и кэш деривативов">
        <div className="overflow-x-auto">
          <table className="min-w-full divide-y divide-slate-800 text-left text-sm">
            <thead className="bg-slate-950/70 text-xs uppercase tracking-wide text-slate-400">
              <tr>
                <th className="px-3 py-3">Путь</th>
                <th className="px-3 py-3">Файлов</th>
                <th className="px-3 py-3">Размер</th>
                <th className="px-3 py-3">Состояние</th>
              </tr>
            </thead>
            <tbody className="divide-y divide-slate-900/60">
              {directories.map((dir) => (
                <tr key={dir.path} className="hover:bg-slate-900/50">
                  <td className="px-3 py-2 font-mono text-xs text-slate-300">{dir.path}</td>
                  <td className="px-3 py-2">{numberFormatter.format(dir.file_count)}</td>
                  <td className="px-3 py-2">{formatBytes(dir.size_bytes)}</td>
                  <td className="px-3 py-2">
                    <Badge tone={dir.exists ? "success" : "danger"}>{dir.exists ? "OK" : "Нет"}</Badge>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
        {disk ? (
          <div className="rounded-xl bg-slate-950/60 p-4 ring-1 ring-slate-800">
            <h3 className="text-sm font-semibold uppercase tracking-wide text-slate-400">Снимок диска</h3>
            <div className="mt-2 grid grid-cols-2 gap-3 text-sm">
              <InfoRow label="Путь">{disk.path}</InfoRow>
              <InfoRow label="Свободно">{formatBytes(disk.free_bytes)}</InfoRow>
              <InfoRow label="Занято">{formatBytes(disk.used_bytes)}</InfoRow>
              <InfoRow label="Всего">{formatBytes(disk.total_bytes)}</InfoRow>
              <InfoRow label="Загруженность">{disk.percent_used != null ? `${disk.percent_used}%` : "—"}</InfoRow>
            </div>
          </div>
        ) : null}
        <div className="rounded-xl bg-slate-950/60 p-4 ring-1 ring-slate-800">
          <h3 className="text-sm font-semibold uppercase tracking-wide text-slate-400">Деривативы</h3>
          <div className="mt-2 grid grid-cols-2 gap-3 text-sm sm:grid-cols-3">
            <InfoRow label="Готово">{numberFormatter.format(derivatives.ready)}</InfoRow>
            <InfoRow label="В обработке">{numberFormatter.format(derivatives.processing)}</InfoRow>
            <InfoRow label="Ожидает">{numberFormatter.format(derivatives.pending)}</InfoRow>
            <InfoRow label="С ошибкой">{numberFormatter.format(derivatives.failed)}</InfoRow>
            <InfoRow label="Суммарно">{formatBytes(derivatives.total_bytes)}</InfoRow>
          </div>
        </div>
      </Section>
    );
  };

  const renderUploads = () => {
    if (!uploadsQuery.data) {
      return uploadsQuery.isLoading ? (
        <Section id="uploads" title="Загрузки" description="Статистика сессий">Загрузка…</Section>
      ) : null;
    }
    const { states, total, recent } = uploadsQuery.data;
    return (
      <Section
        id="uploads"
        title="Загрузки"
        description="Статистика по состояниям и последние сессии"
        actions={
          <button
            type="button"
            className="rounded-lg bg-slate-800 px-3 py-2 text-xs font-semibold text-slate-200 ring-1 ring-slate-700 transition hover:bg-slate-700"
            onClick={() => uploadsQuery.refetch()}
          >
            Обновить
          </button>
        }
      >
        <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
          <MetricCard label="Всего сессий" value={numberFormatter.format(total)} />
          {Object.entries(states).map(([state, count]) => (
            <MetricCard key={state} label={state} value={numberFormatter.format(count)} />
          ))}
        </div>
        <div className="overflow-x-auto">
          <table className="min-w-full divide-y divide-slate-800 text-left text-sm">
            <thead className="bg-slate-950/70 text-xs uppercase tracking-wide text-slate-400">
              <tr>
                <th className="px-3 py-3">ID</th>
                <th className="px-3 py-3">Файл</th>
                <th className="px-3 py-3">Размер</th>
                <th className="px-3 py-3">Статус</th>
                <th className="px-3 py-3">Обновлено</th>
              </tr>
            </thead>
            <tbody className="divide-y divide-slate-900/60">
              {recent.map((item) => (
                <tr key={item.id} className="hover:bg-slate-900/50">
                  <td className="px-3 py-2 font-mono text-xs text-slate-300">{item.id}</td>
                  <td className="px-3 py-2">{item.filename || "—"}</td>
                  <td className="px-3 py-2">{item.size_bytes ? formatBytes(item.size_bytes) : "—"}</td>
                  <td className="px-3 py-2"><Badge tone={item.state === "completed" ? "success" : item.state === "failed" ? "danger" : "neutral"}>{item.state}</Badge></td>
                  <td className="px-3 py-2">{formatDate(item.updated_at)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </Section>
    );
  };

  const renderSystem = () => {
    if (!systemQuery.data) {
      return systemQuery.isLoading ? (
        <Section id="system" title="Система" description="Переменные окружения и конфиг">Загрузка…</Section>
      ) : null;
    }
    const { env, service_config, services, blockchain_tasks, latest_index_items } = systemQuery.data;
    return (
      <Section id="system" title="Система" description="Ключевые переменные и внутренние конфиги">
        <div className="grid gap-6 lg:grid-cols-2">
          <div className="rounded-xl bg-slate-950/60 p-4 ring-1 ring-slate-800">
            <h3 className="text-sm font-semibold uppercase tracking-wide text-slate-400">Окружение</h3>
            <dl className="mt-3 space-y-2">
              {Object.entries(env).map(([key, value]) => (
                <InfoRow key={key} label={key}>{value ?? "—"}</InfoRow>
              ))}
            </dl>
          </div>
          <div className="rounded-xl bg-slate-950/60 p-4 ring-1 ring-slate-800">
            <h3 className="text-sm font-semibold uppercase tracking-wide text-slate-400">Задачи блокчейна</h3>
            <dl className="mt-3 grid grid-cols-2 gap-3">
              {Object.entries(blockchain_tasks).map(([key, value]) => (
                <InfoRow key={key} label={key}>{numberFormatter.format(value)}</InfoRow>
              ))}
            </dl>
            <h4 className="mt-4 text-xs uppercase tracking-wide text-slate-500">Последние индексы</h4>
            <ul className="mt-2 space-y-2">
              {latest_index_items.length === 0 ? (
                <li className="text-xs text-slate-500">Список пуст</li>
              ) : (
                latest_index_items.map((item) => (
                  <li key={item.encrypted_cid || item.updated_at} className="rounded-lg bg-slate-900/60 px-3 py-2 text-xs">
                    <div className="font-mono text-slate-200">{item.encrypted_cid ?? "—"}</div>
                    <div className="text-slate-500">{formatDate(item.updated_at)}</div>
                  </li>
                ))
              )}
            </ul>
          </div>
        </div>
        <div className="rounded-xl bg-slate-950/60 p-4 ring-1 ring-slate-800">
          <h3 className="text-sm font-semibold uppercase tracking-wide text-slate-400">ServiceConfig</h3>
          <div className="mt-3 overflow-x-auto">
            <table className="min-w-full divide-y divide-slate-800 text-left text-sm">
              <thead className="bg-slate-950/70 text-xs uppercase tracking-wide text-slate-400">
                <tr>
                  <th className="px-3 py-3">Ключ</th>
                  <th className="px-3 py-3">Значение</th>
                  <th className="px-3 py-3">RAW</th>
                </tr>
              </thead>
              <tbody className="divide-y divide-slate-900/60">
                {service_config.map((item) => (
                  <tr key={item.key}>
                    <td className="px-3 py-2 font-mono text-xs text-slate-300">{item.key}</td>
                    <td className="px-3 py-2">{item.value ?? "—"}</td>
                    <td className="px-3 py-2 font-mono text-xs text-slate-500">{item.raw ?? "—"}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </div>
        <div className="rounded-xl bg-slate-950/60 p-4 ring-1 ring-slate-800">
          <h3 className="text-sm font-semibold uppercase tracking-wide text-slate-400">Сервисы</h3>
          <ul className="mt-2 space-y-2">
            {services.map((service) => (
              <li key={service.name} className="flex items-center justify-between rounded-lg bg-slate-900/40 px-3 py-2">
                <span className="text-sm text-slate-200">{service.name}</span>
                <Badge tone={service.status?.includes("working") ? "success" : "neutral"}>{service.status ?? "—"}</Badge>
              </li>
            ))}
          </ul>
        </div>
      </Section>
    );
  };

  const renderBlockchain = () => {
    if (!blockchainQuery.data) {
      return blockchainQuery.isLoading ? (
        <Section id="blockchain" title="Блокчейн" description="Очередь задач">Загрузка…</Section>
      ) : null;
    }
    const { counts, recent } = blockchainQuery.data;
    return (
      <Section
        id="blockchain"
        title="Блокчейн"
        description="Очередь задач и последние транзакции"
        actions={
          <button
            type="button"
            className="rounded-lg bg-slate-800 px-3 py-2 text-xs font-semibold text-slate-200 ring-1 ring-slate-700 transition hover:bg-slate-700"
            onClick={() => blockchainQuery.refetch()}
          >
            Обновить
          </button>
        }
      >
        <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
          {Object.entries(counts).map(([key, value]) => (
            <MetricCard key={key} label={key} value={numberFormatter.format(value)} />
          ))}
        </div>
        <div className="overflow-x-auto">
          <table className="min-w-full divide-y divide-slate-800 text-left text-sm">
            <thead className="bg-slate-950/70 text-xs uppercase tracking-wide text-slate-400">
              <tr>
                <th className="px-3 py-3">ID</th>
                <th className="px-3 py-3">Назначение</th>
                <th className="px-3 py-3">Сумма</th>
                <th className="px-3 py-3">Статус</th>
                <th className="px-3 py-3">Epoch · Seqno</th>
                <th className="px-3 py-3">Hash</th>
                <th className="px-3 py-3">Обновлено</th>
              </tr>
            </thead>
            <tbody className="divide-y divide-slate-900/60">
              {recent.map((task) => (
                <tr key={task.id} className="hover:bg-slate-900/50">
                  <td className="px-3 py-2 font-mono text-xs text-slate-300">{task.id}</td>
                  <td className="px-3 py-2">{task.destination || "—"}</td>
                  <td className="px-3 py-2">{task.amount || "—"}</td>
                  <td className="px-3 py-2"><Badge tone={task.status === "done" ? "success" : task.status === "failed" ? "danger" : "neutral"}>{task.status}</Badge></td>
                  <td className="px-3 py-2">{task.epoch ?? "—"} · {task.seqno ?? "—"}</td>
                  <td className="px-3 py-2 font-mono text-xs text-slate-500">{task.transaction_hash || "—"}</td>
                  <td className="px-3 py-2">{formatDate(task.updated)}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </Section>
    );
  };

  const renderNodes = () => {
    if (!nodesQuery.data) {
      return nodesQuery.isLoading ? (
        <Section id="nodes" title="Ноды" description="Доверенные и внешние узлы">Загрузка…</Section>
      ) : null;
    }
    const { items } = nodesQuery.data;
    const handleRoleChange = (nodePublicKey: string | null, nodeIp: string | null) => {
      return (event: ChangeEvent<HTMLSelectElement>) => {
        const role = event.target.value as "trusted" | "read-only" | "deny";
        nodeRoleMutation.mutate({
          role,
          public_key: nodePublicKey ?? undefined,
          host: nodeIp ?? undefined,
        });
      };
    };
    return (
      <Section id="nodes" title="Ноды" description="Роли, версии и последнее появление">
        <div className="overflow-x-auto">
          <table className="min-w-full divide-y divide-slate-800 text-left text-sm">
            <thead className="bg-slate-950/70 text-xs uppercase tracking-wide text-slate-400">
              <tr>
                <th className="px-3 py-3">IP</th>
                <th className="px-3 py-3">Порт</th>
                <th className="px-3 py-3">Публичный ключ</th>
                <th className="px-3 py-3">Роль</th>
                <th className="px-3 py-3">Версия</th>
                <th className="px-3 py-3">Последний онлайн</th>
                <th className="px-3 py-3">Заметки</th>
              </tr>
            </thead>
            <tbody className="divide-y divide-slate-900/60">
              {items.map((node, index) => (
                <tr key={node.public_key ?? node.ip ?? `node-${index}`} className="hover:bg-slate-900/50">
                  <td className="px-3 py-2">{node.ip || "—"}</td>
                  <td className="px-3 py-2">{node.port ?? "—"}</td>
                  <td className="px-3 py-2 font-mono text-xs text-slate-300">{node.public_key || "—"}</td>
                  <td className="px-3 py-2">
                    <select
                      className="rounded-lg border border-slate-700 bg-slate-950 px-2 py-1 text-xs text-slate-100 focus:border-sky-500 focus:outline-none"
                      defaultValue={node.role}
                      onChange={handleRoleChange(node.public_key, node.ip)}
                      disabled={nodeRoleMutation.isLoading}
                    >
                      <option value="trusted">trusted</option>
                      <option value="read-only">read-only</option>
                      <option value="deny">deny</option>
                    </select>
                  </td>
                  <td className="px-3 py-2">{node.version || "—"}</td>
                  <td className="px-3 py-2">{formatDate(node.last_seen)}</td>
                  <td className="px-3 py-2">{node.notes || "—"}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </Section>
    );
  };

  const renderStatus = () => {
    if (!statusQuery.data) {
      return statusQuery.isLoading ? (
        <Section id="status" title="Статус & лимиты" description="IPFS, пины и лимиты">Загрузка…</Section>
      ) : null;
    }
    const { ipfs, pin_counts, derivatives, convert_backlog, limits } = statusQuery.data;
    return (
      <Section id="status" title="Статус & лимиты" description="Информация о синхронизации, кэше и лимитах">
        <div className="grid gap-6 xl:grid-cols-[1.8fr_1fr]">
          <div className="space-y-6">
            <div className="rounded-xl bg-slate-950/60 p-4 ring-1 ring-slate-800">
              <h3 className="text-sm font-semibold uppercase tracking-wide text-slate-400">IPFS</h3>
              <dl className="mt-3 grid grid-cols-2 gap-3 text-sm">
                <InfoRow label="Repo Size">{formatBytes(Number((ipfs.repo as Record<string, unknown>)?.RepoSize ?? 0))}</InfoRow>
                <InfoRow label="Storage Max">{formatBytes(Number((ipfs.repo as Record<string, unknown>)?.StorageMax ?? 0))}</InfoRow>
                <InfoRow label="Bitswap Peers">{numberFormatter.format(Number((ipfs.bitswap as Record<string, unknown>)?.Peers ?? 0))}</InfoRow>
                <InfoRow label="Blocks Received">{numberFormatter.format(Number((ipfs.bitswap as Record<string, unknown>)?.BlocksReceived ?? 0))}</InfoRow>
              </dl>
            </div>
            <div className="rounded-xl bg-slate-950/60 p-4 ring-1 ring-slate-800">
              <h3 className="text-sm font-semibold uppercase tracking-wide text-slate-400">Pin-статистика</h3>
              <dl className="mt-3 grid grid-cols-2 gap-3 text-sm">
                {Object.entries(pin_counts).map(([key, value]) => (
                  <InfoRow key={key} label={key}>{numberFormatter.format(value)}</InfoRow>
                ))}
                <InfoRow label="Backlog деривативов">{numberFormatter.format(convert_backlog)}</InfoRow>
                <InfoRow label="Деривативы">{formatBytes(derivatives.total_bytes)}</InfoRow>
              </dl>
            </div>
          </div>
          <div className="space-y-6">
            <form
              className="rounded-xl bg-slate-950/60 p-4 ring-1 ring-slate-800"
              onSubmit={cacheLimitsForm.handleSubmit((values) => {
                cacheLimitsMutation.mutate(values);
              })}
            >
              <h3 className="text-sm font-semibold uppercase tracking-wide text-slate-400">Лимиты кэша</h3>
              <div className="mt-3 space-y-3 text-sm">
                <label className="block">
                  <span className="text-xs uppercase text-slate-400">Максимум, GB</span>
                  <input
                    type="number"
                    step="0.1"
                    className="mt-1 w-full rounded-lg border border-slate-700 bg-slate-950 px-3 py-2 text-slate-100 focus:border-sky-500 focus:outline-none"
                    {...cacheLimitsForm.register("max_gb", { valueAsNumber: true })}
                  />
                </label>
                <label className="block">
                  <span className="text-xs uppercase text-slate-400">TTL, дней</span>
                  <input
                    type="number"
                    className="mt-1 w-full rounded-lg border border-slate-700 bg-slate-950 px-3 py-2 text-slate-100 focus:border-sky-500 focus:outline-none"
                    {...cacheLimitsForm.register("ttl_days", { valueAsNumber: true })}
                  />
                </label>
              </div>
              <button
                type="submit"
                className="mt-4 w-full rounded-lg bg-emerald-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-emerald-500 disabled:cursor-not-allowed disabled:bg-slate-700"
                disabled={cacheLimitsMutation.isLoading}
              >
                {cacheLimitsMutation.isLoading ? "Сохраняем…" : "Сохранить"}
              </button>
            </form>

            <form
              className="rounded-xl bg-slate-950/60 p-4 ring-1 ring-slate-800"
              onSubmit={cacheFitForm.handleSubmit((values) => {
                cacheCleanupMutation.mutate({ mode: "fit", max_gb: values.max_gb });
              })}
            >
              <h3 className="text-sm font-semibold uppercase tracking-wide text-slate-400">Очистка по размеру</h3>
              <label className="mt-3 block text-sm">
                <span className="text-xs uppercase text-slate-400">Целевой размер, GB</span>
                <input
                  type="number"
                  step="0.1"
                  className="mt-1 w-full rounded-lg border border-slate-700 bg-slate-950 px-3 py-2 text-slate-100 focus:border-sky-500 focus:outline-none"
                  {...cacheFitForm.register("max_gb", { valueAsNumber: true })}
                />
              </label>
              <div className="mt-4 flex flex-col gap-2">
                <button
                  type="submit"
                  className="w-full rounded-lg bg-sky-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-sky-500 disabled:cursor-not-allowed disabled:bg-slate-700"
                  disabled={cacheCleanupMutation.isLoading}
                >
                  {cacheCleanupMutation.isLoading ? "Очищаем…" : "Очистить до размера"}
                </button>
                <button
                  type="button"
                  onClick={() => cacheCleanupMutation.mutate({ mode: "ttl" })}
                  className="w-full rounded-lg bg-amber-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-amber-500 disabled:cursor-not-allowed disabled:bg-slate-700"
                  disabled={cacheCleanupMutation.isLoading}
                >
                  Очистить по TTL
                </button>
              </div>
            </form>

            <form
              className="rounded-xl bg-slate-950/60 p-4 ring-1 ring-slate-800"
              onSubmit={syncLimitsForm.handleSubmit((values) => {
                syncLimitsMutation.mutate(values);
              })}
            >
              <h3 className="text-sm font-semibold uppercase tracking-wide text-slate-400">Sync лимиты</h3>
              <div className="mt-3 space-y-3 text-sm">
                <label className="block">
                  <span className="text-xs uppercase text-slate-400">Одновременные пины</span>
                  <input
                    type="number"
                    className="mt-1 w-full rounded-lg border border-slate-700 bg-slate-950 px-3 py-2 text-slate-100 focus:border-sky-500 focus:outline-none"
                    {...syncLimitsForm.register("max_concurrent_pins", { valueAsNumber: true })}
                  />
                </label>
                <label className="block">
                  <span className="text-xs uppercase text-slate-400">Нижний предел диска, %</span>
                  <input
                    type="number"
                    className="mt-1 w-full rounded-lg border border-slate-700 bg-slate-950 px-3 py-2 text-slate-100 focus:border-sky-500 focus:outline-none"
                    {...syncLimitsForm.register("disk_low_watermark_pct", { valueAsNumber: true })}
                  />
                </label>
              </div>
              <button
                type="submit"
                className="mt-4 w-full rounded-lg bg-emerald-600 px-4 py-2 text-sm font-semibold text-white transition hover:bg-emerald-500 disabled:cursor-not-allowed disabled:bg-slate-700"
                disabled={syncLimitsMutation.isLoading}
              >
                {syncLimitsMutation.isLoading ? "Сохраняем…" : "Сохранить"}
              </button>
            </form>
          </div>
        </div>
      </Section>
    );
  };

  const renderDashboard = () => {
    if (authState === "checking" && overviewQuery.isLoading) {
      return (
        <div className="flex h-[60vh] items-center justify-center text-slate-400">Проверяем сессию…</div>
      );
    }
    return (
      <div className="space-y-10">
        <div className="flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
          <div>
            <h1 className="text-3xl font-semibold text-slate-50">Админ-панель</h1>
            <p className="mt-1 text-sm text-slate-400">
              Управление узлом, мониторинг IPFS/TON и контроль кэша
            </p>
          </div>
          <div className="flex flex-wrap items-center gap-2">
            <button
              type="button"
              onClick={handleRefreshAll}
              className="rounded-lg bg-slate-800 px-4 py-2 text-sm font-semibold text-slate-200 ring-1 ring-slate-700 transition hover:bg-slate-700"
            >
              Обновить всё
            </button>
            <button
              type="button"
              onClick={() => logoutMutation.mutate()}
              className="rounded-lg bg-rose-700 px-4 py-2 text-sm font-semibold text-white transition hover:bg-rose-600"
              disabled={logoutMutation.isLoading}
            >
              Выйти
            </button>
          </div>
        </div>

        <nav className="sticky top-6 z-10 rounded-2xl border border-slate-800 bg-slate-950/80 backdrop-blur">
          <ul className="flex flex-wrap">
            {ADMIN_SECTIONS.map((section) => (
              <li key={section.id} className="flex-1">
                <button
                  type="button"
                  className={clsx(
                    "w-full px-4 py-3 text-xs font-semibold uppercase tracking-wide transition",
                    activeSection === section.id
                      ? "bg-slate-900 text-slate-100"
                      : "text-slate-400 hover:bg-slate-900/60 hover:text-slate-100",
                  )}
                  onClick={() => {
                    setActiveSection(section.id);
                    document.getElementById(section.id)?.scrollIntoView({ behavior: "smooth", block: "start" });
                  }}
                >
                  {section.label}
                </button>
              </li>
            ))}
          </ul>
        </nav>

        {renderOverview()}
        {renderStorage()}
        {renderUploads()}
        {renderSystem()}
        {renderBlockchain()}
        {renderNodes()}
        {renderStatus()}
      </div>
    );
  };

  return (
    <div className="min-h-screen bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950 pb-20 text-slate-100">
      <div className="mx-auto max-w-7xl px-6 py-10">
        {renderFlash()}
        {authState === "authorized" || authState === "checking" ? renderDashboard() : renderLoginPanel()}
      </div>
    </div>
  );
};

const InfoRow = ({ label, children }: { label: string; children: ReactNode }) => {
  return (
    <div>
      <dt className="text-xs uppercase tracking-wide text-slate-500">{label}</dt>
      <dd className="mt-1 text-sm text-slate-100">{children}</dd>
    </div>
  );
};

const MetricCard = ({ label, value }: { label: string; value: string }) => {
  return (
    <div className="rounded-xl bg-slate-950/60 p-4 shadow-inner shadow-black/40 ring-1 ring-slate-800">
      <p className="text-xs uppercase tracking-wide text-slate-500">{label}</p>
      <p className="mt-2 text-2xl font-semibold text-slate-100">{value}</p>
    </div>
  );
};
