"use client";

/**
 * MapCard — карта аналитики: целевая квартира + аналоги (asking) + ДКП-сделки.
 * Reuses the same CDN-loaded Leaflet + OSM approach as MapPicker.tsx (no npm dep).
 * Пины с null lat/lon пропускаются; карточка не рендерится без гео-точек.
 */
/* eslint-disable @typescript-eslint/no-explicit-any -- интероп с CDN-библиотекой Leaflet */
import { useEffect, useMemo, useRef, useState } from "react";

import type { AggregatedEstimate, AnalogLot } from "@/types/trade-in";

const LEAFLET_VER = "1.9.4";
const LEAFLET_CSS = `https://unpkg.com/leaflet@${LEAFLET_VER}/dist/leaflet.css`;
const LEAFLET_JS = `https://unpkg.com/leaflet@${LEAFLET_VER}/dist/leaflet.js`;
const LEAFLET_CSS_SRI = "sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=";
const LEAFLET_JS_SRI = "sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=";

// Цвета пинов: asking (синий --viz-1) / ДКП (бирюза --viz-3) / target (янтарь --viz-4).
// Leaflet принимает строку-цвет напрямую (не CSS vars) — используем hex-значения токенов.
const COLOR_ANALOG = "#1d4ed8"; // --viz-1
const COLOR_DEAL = "#14b8a6";   // --viz-3
const COLOR_TARGET = "#f59e0b"; // --viz-4

/** Подгружает Leaflet с CDN один раз, резолвит window.L. (mirror MapPicker) */
function loadLeaflet(): Promise<any> {
  return new Promise((resolve, reject) => {
    const w = window as any;
    if (w.L) {
      resolve(w.L);
      return;
    }
    if (!document.querySelector(`link[data-leaflet]`)) {
      const link = document.createElement("link");
      link.rel = "stylesheet";
      link.href = LEAFLET_CSS;
      link.integrity = LEAFLET_CSS_SRI;
      link.crossOrigin = "anonymous";
      link.setAttribute("data-leaflet", "1");
      document.head.appendChild(link);
    }
    const existing = document.querySelector<HTMLScriptElement>(`script[data-leaflet]`);
    if (existing) {
      existing.addEventListener("load", () => resolve(w.L));
      existing.addEventListener("error", () => reject(new Error("leaflet load failed")));
      return;
    }
    const script = document.createElement("script");
    script.src = LEAFLET_JS;
    script.integrity = LEAFLET_JS_SRI;
    script.crossOrigin = "anonymous";
    script.setAttribute("data-leaflet", "1");
    script.onload = () => resolve(w.L);
    script.onerror = () => reject(new Error("leaflet load failed"));
    document.body.appendChild(script);
  });
}

interface Props {
  estimate: AggregatedEstimate;
}

function fmtRub(v: number): string {
  return v.toLocaleString("ru-RU");
}

/** Безопасное экранирование для вставки в HTML popup. */
function esc(s: string): string {
  return s.replace(/[&<>"']/g, (c) =>
    c === "&" ? "&amp;" : c === "<" ? "&lt;" : c === ">" ? "&gt;" : c === '"' ? "&quot;" : "&#39;",
  );
}

function popupHtml(lot: AnalogLot, kind: string): string {
  const expo =
    lot.days_on_market !== null && lot.days_on_market !== undefined
      ? `${lot.days_on_market} дн. экспозиции`
      : "срок не указан";
  return `<div style="font-size:12px;line-height:1.5">
    <b>${esc(lot.address)}</b><br/>
    <span style="color:#6b7280">${kind}</span><br/>
    ${fmtRub(lot.price_per_m2)} ₽/м² · ${fmtRub(lot.price_rub)} ₽<br/>
    ${esc(expo)}
  </div>`;
}

export function MapCard({ estimate }: Props) {
  const mapRef = useRef<HTMLDivElement>(null);
  const [mapError, setMapError] = useState(false);

  const analogs = useMemo(
    () =>
      estimate.analogs.filter(
        (l) => typeof l.lat === "number" && typeof l.lon === "number",
      ),
    [estimate.analogs],
  );
  const deals = useMemo(
    () =>
      estimate.actual_deals.filter(
        (l) => typeof l.lat === "number" && typeof l.lon === "number",
      ),
    [estimate.actual_deals],
  );

  const hasTarget =
    typeof estimate.target_lat === "number" && typeof estimate.target_lon === "number";
  const totalPins = analogs.length + deals.length + (hasTarget ? 1 : 0);

  useEffect(() => {
    if (totalPins === 0) return;
    let map: any = null;
    let cancelled = false;

    loadLeaflet()
      .then((L) => {
        if (cancelled || !mapRef.current) return;
        const firstPin = analogs[0] ?? deals[0];
        let center: [number, number] | null = null;
        if (hasTarget) {
          center = [estimate.target_lat as number, estimate.target_lon as number];
        } else if (firstPin) {
          center = [firstPin.lat as number, firstPin.lon as number];
        }
        if (!center) return; // guarded by totalPins>0, but satisfies TS
        map = L.map(mapRef.current).setView(center, 14);
        L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
          attribution: "© OpenStreetMap",
          maxZoom: 19,
        }).addTo(map);
        setTimeout(() => map && map.invalidateSize(), 120);

        const bounds: [number, number][] = [];

        // Аналоги (asking) — синие.
        for (const l of analogs) {
          const ll: [number, number] = [l.lat as number, l.lon as number];
          bounds.push(ll);
          L.circleMarker(ll, {
            radius: 6,
            color: COLOR_ANALOG,
            weight: 2,
            fillColor: COLOR_ANALOG,
            fillOpacity: 0.55,
          })
            .addTo(map)
            .bindPopup(popupHtml(l, "Объявление (asking)"));
        }

        // ДКП-сделки — зелёные.
        for (const l of deals) {
          const ll: [number, number] = [l.lat as number, l.lon as number];
          bounds.push(ll);
          L.circleMarker(ll, {
            radius: 6,
            color: COLOR_DEAL,
            weight: 2,
            fillColor: COLOR_DEAL,
            fillOpacity: 0.55,
          })
            .addTo(map)
            .bindPopup(popupHtml(l, "Сделка ДКП"));
        }

        // Целевая квартира — крупный оранжевый маркер + радиус-круг.
        if (hasTarget) {
          const ll: [number, number] = [
            estimate.target_lat as number,
            estimate.target_lon as number,
          ];
          bounds.push(ll);
          L.circle(ll, {
            radius: 2000,
            color: COLOR_TARGET,
            weight: 1,
            fillColor: COLOR_TARGET,
            fillOpacity: 0.05,
            // Декоративный радиус-круг рисуется ПОСЛЕ маркеров и по умолчанию
            // перехватывал бы клики по пинам под ним (#786) → bindPopup не
            // срабатывал. interactive:false пропускает клики к маркерам.
            interactive: false,
          }).addTo(map);
          L.circleMarker(ll, {
            radius: 9,
            color: COLOR_TARGET,
            weight: 3,
            fillColor: "#fff",
            fillOpacity: 1,
          })
            .addTo(map)
            .bindPopup(
              `<div style="font-size:12px"><b>Ваша квартира</b><br/>${esc(
                estimate.target_address ?? "—",
              )}</div>`,
            )
            .openPopup();
        }

        if (bounds.length > 1) {
          map.fitBounds(bounds, { padding: [30, 30], maxZoom: 16 });
        }
      })
      .catch(() => setMapError(true));

    return () => {
      cancelled = true;
      if (map) map.remove();
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps -- лоты стабильны для одной оценки; пересоздаём map только при смене estimate
  }, [estimate.estimate_id, totalPins]);

  // Empty state: нет ни одной гео-точки — карточку не показываем вовсе.
  if (totalPins === 0) return null;

  return (
    <article className="card">
      <div className="card-head">
        <div>
          <div className="section-kicker">Аналитика · Карта</div>
          <h2>Аналоги и сделки на карте</h2>
        </div>
        <div className="card-meta">
          <div>
            <span style={{ color: COLOR_ANALOG }}>●</span> объявления · {analogs.length}
          </div>
          {deals.length > 0 && (
            <div style={{ marginTop: 4 }}>
              <span style={{ color: COLOR_DEAL }}>●</span> сделки ДКП · {deals.length}
            </div>
          )}
        </div>
      </div>

      {mapError ? (
        <div className="card-body">
          <p style={{ color: "var(--muted)", textAlign: "center", padding: "24px 0" }}>
            Не удалось загрузить карту. Проверьте интернет-соединение.
          </p>
        </div>
      ) : (
        <div
          ref={mapRef}
          style={{ height: 420, width: "100%", background: "var(--surface-2)" }}
          aria-label="Карта аналогов и сделок"
        />
      )}

      <div className="table-foot">
        <span>
          <span style={{ color: COLOR_TARGET }}>●</span> ваша квартира ·{" "}
          <span style={{ color: COLOR_ANALOG }}>●</span> asking
          {deals.length > 0 && (
            <>
              {" · "}
              <span style={{ color: COLOR_DEAL }}>●</span> ДКП
            </>
          )}
          {" · клик по пину — детали"}
        </span>
      </div>
    </article>
  );
}
