"use client";

/**
 * PermitsNearbyBlock — «Разрешения на строительство поблизости» (#2367 PR-2).
 *
 * Точный радиус-запрос (500 м) по официальному реестру ГИСОГД Свердловской
 * области: разрешения на строительство (RS) и вводы в эксплуатацию (RV) рядом с
 * участком. Ценность девелоперу — что реально строится/сдаётся в шаговой
 * доступности (ранний сигнал будущей конкуренции/спроса).
 *
 * Источник данных — блок `permits_nearby` из /analyze. Backend держит его как
 * `dict[str, Any] | None` (Pydantic extra=allow), поэтому в api-types.ts он
 * приходит как generic-объект; форму типизируем здесь локально и валидируем
 * обязательные поля в рантайме (`asPermitsNearby`), без `any`.
 *
 * Рендер:
 *   • total_count === 0 → нейтральная строка «В радиусе 500 м … не найдено»
 *     (блок НЕ скрывается — юзер видит «проверили, рядом нет»).
 *   • total_count > 0 → KPI-строка (RS / RV / ближайшее) + список items с
 *     progressive disclosure (первые 8 видимы, остальные под <details>, как в
 *     ResourceReservesCard / ForecastFutureSupplyBlock).
 *   • items_truncated → честная приписка «показаны ближайшие N из M».
 */

import { Building2, FileCheck } from "lucide-react";

import { Section } from "@/components/analytics/Section";
import { KpiCard } from "@/components/site-finder/KpiCard";
import { Badge } from "@/components/ui/Badge";

// ── Локальная форма данных (типизируем сами — см. шапку) ─────────────────────

type PermitDocGroup = "RS" | "RV";

interface PermitItem {
  doc_group: PermitDocGroup;
  doc_name: string | null;
  doc_num: string | null;
  date_doc: string | null;
  approved_organization: string | null;
  distance_m: number | null;
}

interface PermitsNearby {
  radius_m: number;
  total_count: number;
  rs_count: number;
  rv_count: number;
  nearest_distance_m: number | null;
  items: PermitItem[];
  items_truncated: boolean;
  source: string | null;
}

interface Props {
  /**
   * Сырой `permits_nearby` из /analyze (generic-объект / null). Narrow'им в
   * рантайме через asPermitsNearby — на входе `unknown`, чтобы не тащить сюда
   * ложную строгость из api-types.ts.
   */
  permitsNearby: unknown;
}

// ── Runtime narrowing (без any) ──────────────────────────────────────────────

function isRecord(v: unknown): v is Record<string, unknown> {
  return typeof v === "object" && v !== null;
}

function toNum(v: unknown): number | null {
  return typeof v === "number" && Number.isFinite(v) ? v : null;
}

function toStr(v: unknown): string | null {
  return typeof v === "string" && v.length > 0 ? v : null;
}

function toPermitItem(v: unknown): PermitItem | null {
  if (!isRecord(v)) return null;
  const group =
    v.doc_group === "RV" ? "RV" : v.doc_group === "RS" ? "RS" : null;
  if (group === null) return null;
  return {
    doc_group: group,
    doc_name: toStr(v.doc_name),
    doc_num: toStr(v.doc_num),
    date_doc: toStr(v.date_doc),
    approved_organization: toStr(v.approved_organization),
    distance_m: toNum(v.distance_m),
  };
}

/**
 * Narrow сырого `permits_nearby` в строгую форму. Возвращает null, если объекта
 * нет / это не объект / нет обязательного `total_count` (старый кэш без поля).
 */
function asPermitsNearby(raw: unknown): PermitsNearby | null {
  if (!isRecord(raw)) return null;
  const total = toNum(raw.total_count);
  if (total === null) return null;

  const items = Array.isArray(raw.items)
    ? raw.items.map(toPermitItem).filter((it): it is PermitItem => it !== null)
    : [];

  return {
    radius_m: toNum(raw.radius_m) ?? 500,
    total_count: total,
    rs_count: toNum(raw.rs_count) ?? 0,
    rv_count: toNum(raw.rv_count) ?? 0,
    nearest_distance_m: toNum(raw.nearest_distance_m),
    items,
    items_truncated: raw.items_truncated === true,
    source: toStr(raw.source),
  };
}

// ── Форматирование ───────────────────────────────────────────────────────────

const GROUP_LABEL: Record<PermitDocGroup, string> = {
  RS: "Разрешение на строительство",
  RV: "Ввод в эксплуатацию",
};

function formatRuDate(raw: string | null): string {
  if (!raw) return "—";
  const d = new Date(raw);
  return Number.isNaN(d.getTime()) ? "—" : d.toLocaleDateString("ru-RU");
}

function formatMeters(v: number | null): string {
  return v != null ? `${Math.round(v).toLocaleString("ru")} м` : "—";
}

// Первые N видимых сразу; остальные — под <details> (progressive disclosure,
// как в ResourceReservesCard / ForecastFutureSupplyBlock).
const HEAD_COUNT = 8;

// ── PermitsNearbyBlock ───────────────────────────────────────────────────────

export function PermitsNearbyBlock({ permitsNearby }: Props) {
  const data = asPermitsNearby(permitsNearby);

  // Поле отсутствует / null / старый кэш без формы — не рендерим блок совсем
  // (о нём просто нечего сказать, в отличие от честного нуля).
  if (data === null) return null;

  return (
    <Section
      title="Разрешения на строительство поблизости"
      subtitle={`Радиус ${data.radius_m.toLocaleString("ru")} м · официальный реестр ГИСОГД Свердловской области`}
    >
      {data.total_count === 0 ? (
        <p style={{ margin: 0, fontSize: 13, color: "var(--fg-secondary)" }}>
          В радиусе {data.radius_m.toLocaleString("ru")} м новых разрешений не
          найдено.
        </p>
      ) : (
        <PermitsContent data={data} />
      )}
    </Section>
  );
}

// ── Содержимое при total_count > 0 ───────────────────────────────────────────

function PermitsContent({ data }: { data: PermitsNearby }) {
  const head = data.items.slice(0, HEAD_COUNT);
  const rest = data.items.slice(HEAD_COUNT);

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      {/* KPI-строка: RS / RV / ближайшее (паттерн KpiCard-грид). */}
      <div
        style={{
          display: "grid",
          gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))",
          gap: 12,
        }}
      >
        <KpiCard
          label="Разрешений на строительство"
          value={data.rs_count.toLocaleString("ru")}
          color="blue"
        />
        <KpiCard
          label="Вводов в эксплуатацию"
          value={data.rv_count.toLocaleString("ru")}
          color="green"
        />
        <KpiCard
          label="Ближайшее, м"
          value={
            data.nearest_distance_m != null
              ? Math.round(data.nearest_distance_m).toLocaleString("ru")
              : "—"
          }
          color="neutral"
        />
      </div>

      {/* Список разрешений (первые HEAD_COUNT видимы). */}
      {data.items.length > 0 ? (
        <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
          <PermitsTable items={head} />

          {rest.length > 0 && (
            <details>
              <summary
                style={{
                  fontSize: 12,
                  color: "var(--accent)",
                  cursor: "pointer",
                }}
              >
                Раскрыть всё · ещё {rest.length.toLocaleString("ru")}
              </summary>
              <div style={{ marginTop: 8 }}>
                <PermitsTable items={rest} />
              </div>
            </details>
          )}

          {/* Честная приписка, если бэкенд капнул список (cap 30). */}
          {data.items_truncated && (
            <span style={{ fontSize: 12, color: "var(--fg-tertiary)" }}>
              Показаны ближайшие {data.items.length.toLocaleString("ru")} из{" "}
              {data.total_count.toLocaleString("ru")}.
            </span>
          )}
        </div>
      ) : null}
    </div>
  );
}

// ── Таблица разрешений ───────────────────────────────────────────────────────

const TH_STYLE: React.CSSProperties = {
  textAlign: "left",
  padding: "8px 12px",
  fontSize: 11,
  fontWeight: 500,
  letterSpacing: "0.04em",
  textTransform: "uppercase",
  color: "var(--fg-secondary)",
  whiteSpace: "nowrap",
  borderBottom: "1px solid var(--border-card)",
};

const TD_STYLE: React.CSSProperties = {
  padding: "10px 12px",
  fontSize: 13,
  color: "var(--fg-primary)",
  verticalAlign: "top",
  borderBottom: "1px solid var(--border-soft)",
};

const NUM_TH: React.CSSProperties = { ...TH_STYLE, textAlign: "right" };
const NUM_TD: React.CSSProperties = {
  ...TD_STYLE,
  textAlign: "right",
  whiteSpace: "nowrap",
  fontVariantNumeric: "tabular-nums",
};

function PermitsTable({ items }: { items: PermitItem[] }) {
  return (
    <div style={{ overflowX: "auto" }}>
      <table
        style={{ width: "100%", borderCollapse: "collapse", minWidth: 640 }}
      >
        <thead>
          <tr>
            <th style={TH_STYLE}>Тип</th>
            <th style={TH_STYLE}>Документ</th>
            <th style={{ ...TH_STYLE, whiteSpace: "nowrap" }}>Дата</th>
            <th style={NUM_TH}>Расстояние</th>
            <th style={{ ...TH_STYLE, minWidth: 200 }}>Орган</th>
          </tr>
        </thead>
        <tbody>
          {items.map((it, i) => (
            <PermitRow
              key={`${it.doc_num ?? it.doc_name ?? "permit"}-${i}`}
              item={it}
            />
          ))}
        </tbody>
      </table>
    </div>
  );
}

function PermitRow({ item }: { item: PermitItem }) {
  const isRs = item.doc_group === "RS";
  const label = GROUP_LABEL[item.doc_group];
  // doc_name основной текст (в реестре он несёт полный «№ … от …»); при его
  // отсутствии деградируем к doc_num.
  const docText = item.doc_name ?? item.doc_num ?? "—";
  const org = item.approved_organization;

  return (
    <tr>
      <td style={TD_STYLE}>
        <Badge
          variant={isRs ? "info" : "success"}
          size="sm"
          icon={
            isRs ? (
              <Building2 size={12} aria-hidden />
            ) : (
              <FileCheck size={12} aria-hidden />
            )
          }
        >
          {label}
        </Badge>
      </td>
      <td style={{ ...TD_STYLE, maxWidth: 320 }}>{docText}</td>
      <td style={{ ...TD_STYLE, whiteSpace: "nowrap" }}>
        {formatRuDate(item.date_doc)}
      </td>
      <td style={NUM_TD}>{formatMeters(item.distance_m)}</td>
      <td style={{ ...TD_STYLE, maxWidth: 280 }}>
        {org ? (
          <span
            title={org}
            style={{
              display: "block",
              overflow: "hidden",
              textOverflow: "ellipsis",
              whiteSpace: "nowrap",
              color: "var(--fg-secondary)",
            }}
          >
            {org}
          </span>
        ) : (
          <span style={{ color: "var(--fg-tertiary)" }}>—</span>
        )}
      </td>
    </tr>
  );
}
