"use client";

// 01 ПАРАМЕТРЫ КВАРТИРЫ — left card of the /trade-in/v2 "МЕРА Оценка" port.
// Faithful markup port (МЕРА Оценка.dc.html lines 146-240). Wire phase: this is
// now a CONTROLLED form. Each field is local useState producing a
// TradeInEstimateInput that is handed to onSubmit; the visual markup / layout /
// styles are UNCHANGED — only the data plumbing differs (display <div>s became
// <input>s styled identically, dropdowns now feed real enum values). RU dropdown
// labels <-> API enum values go through HOUSE_TYPE_*/REPAIR_* maps in ./mappers.
// РАДИУС is now wired (radius_m on submit + the outer map ring scales with it);
// the CRM dropdown still has no backend → kept visually but disabled. Hover/active
// + @keyframes live in a pp-prefixed local <style>.

import { useRef, useState, type CSSProperties } from "react";
import { tokens } from "./tokens";
import { dropdownOptions } from "./fixtures";
import {
  HOUSE_TYPE_FROM_RU,
  HOUSE_TYPE_RU,
  REPAIR_FROM_RU,
  REPAIR_RU,
  type MapMarker,
} from "./mappers";
import { useGeocodeSuggest } from "@/lib/trade-in-api";
import type {
  GeocodeSuggestion,
  HouseType,
  RepairState,
  TradeInEstimateInput,
} from "@/types/trade-in";

type DdKey = "rooms" | "houseType" | "repair" | "radius" | null;

interface DdProps {
  open: boolean;
  onToggle: () => void;
  value: string;
  options: string[];
  onSelect: (v: string) => void;
  mono: boolean;
  triggerFontSize: number;
  optionFontSize: number;
  variant?: "solid" | "dashed";
  openUp?: boolean;
  triggerHeight?: number;
  triggerBg?: string;
  triggerColor?: string;
  caretColor?: string;
}

function Dd({
  open,
  onToggle,
  value,
  options,
  onSelect,
  mono,
  triggerFontSize,
  optionFontSize,
  variant = "solid",
  openUp = false,
  triggerHeight = 33,
  triggerBg = tokens.surface.w62,
  triggerColor = tokens.ink,
  caretColor = tokens.muted2,
}: DdProps) {
  const panelStyle: CSSProperties = {
    position: "absolute",
    left: 0,
    right: 0,
    background: tokens.surface.w98,
    border: `1px solid ${tokens.line}`,
    borderRadius: 6,
    zIndex: 30,
    padding: 3,
    ...(openUp
      ? {
          bottom: "calc(100% + 4px)",
          boxShadow: "0 -10px 26px rgba(40,80,130,.18)",
        }
      : {
          top: "calc(100% + 4px)",
          boxShadow: "0 10px 26px rgba(40,80,130,.18)",
        }),
  };

  return (
    <div style={{ position: "relative" }}>
      <div
        className={
          variant === "dashed" ? "pp-dd-trigger-dashed" : "pp-dd-trigger"
        }
        onClick={onToggle}
        style={{
          height: triggerHeight,
          background: triggerBg,
          borderRadius: 6,
          display: "flex",
          alignItems: "center",
          justifyContent: "space-between",
          padding: "0 13px",
          fontFamily: mono ? tokens.font.mono : tokens.font.sans,
          fontSize: triggerFontSize,
          color: triggerColor,
          cursor: "pointer",
        }}
      >
        {value}
        <span
          style={{
            color: caretColor,
            fontSize: 9,
            fontFamily: tokens.font.sans,
          }}
        >
          ▼
        </span>
      </div>
      {open && (
        <div style={panelStyle}>
          {options.map((o) => (
            <div
              key={o}
              className="pp-dd-opt"
              onClick={() => onSelect(o)}
              style={{
                padding: "7px 10px",
                fontSize: optionFontSize,
                fontFamily: mono ? tokens.font.mono : tokens.font.sans,
                borderRadius: 4,
                cursor: "pointer",
                color: o === value ? tokens.accent : tokens.ink,
                fontWeight: o === value ? 600 : 400,
              }}
            >
              {o}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

const styles = `
@keyframes pp-pulsedot{0%,100%{opacity:.45}50%{opacity:1}}
.pp-dot{animation:pp-pulsedot 2.6s ease-in-out infinite}
.pp-dd-trigger{border:1px solid ${tokens.line};transition:border-color .15s}
.pp-dd-trigger:hover{border-color:${tokens.accent}}
.pp-dd-trigger-dashed{border:1px dashed ${tokens.line};transition:border-color .15s}
.pp-dd-trigger-dashed:hover{border-color:${tokens.accent}}
.pp-dd-opt{transition:background .15s}
.pp-dd-opt:hover{background:rgba(46,139,255,.1)}
.pp-eval-btn{background:linear-gradient(90deg,rgba(255,255,255,.7),rgba(233,242,252,.7));box-shadow:0 0 0 0 rgba(46,139,255,0);transition:all .18s}
.pp-eval-btn:hover{background:linear-gradient(90deg,rgba(46,139,255,.12),rgba(46,139,255,.18));box-shadow:0 6px 22px rgba(46,139,255,.28)}
.pp-eval-btn:active{transform:translateY(1px)}
`;

const hintLabel: CSSProperties = {
  fontSize: 9.5,
  letterSpacing: 1.5,
  color: tokens.muted2,
  marginBottom: 3,
};

const optHint: CSSProperties = {
  fontSize: 9,
  color: tokens.muted4,
};

const fieldRow: CSSProperties = {
  display: "grid",
  gridTemplateColumns: "1fr 1fr",
  gap: 12,
};

// Numeric text input — visually identical to the former display <div> (textField)
// but as a real <input>: inputs do NOT inherit font-family, so it is set
// explicitly; outline removed to keep the HUD look (focus ring would break it).
const inputField: CSSProperties = {
  height: 33,
  width: "100%",
  boxSizing: "border-box",
  background: tokens.surface.w62,
  border: `1px solid ${tokens.line}`,
  borderRadius: 6,
  padding: "0 13px",
  fontFamily: tokens.font.mono,
  fontSize: 14,
  color: tokens.ink,
  outline: "none",
};

// Address input (sans, room on the right for the decorative search glyph).
const addressField: CSSProperties = {
  height: 33,
  width: "100%",
  boxSizing: "border-box",
  background: tokens.surface.w62,
  border: `1px solid ${tokens.line}`,
  borderRadius: 6,
  padding: "0 38px 0 13px",
  fontFamily: tokens.font.sans,
  fontSize: 13,
  color: tokens.ink,
  outline: "none",
};

// Autocomplete dropdown — mirrors the <Dd> HUD panel (surface.w98 + soft blue
// shadow). Absolutely positioned under the address input; scrolls past ~6 rows.
const suggestPanel: CSSProperties = {
  position: "absolute",
  left: 0,
  right: 0,
  top: "calc(100% + 4px)",
  background: tokens.surface.w98,
  border: `1px solid ${tokens.line}`,
  borderRadius: 6,
  zIndex: 30,
  padding: 3,
  maxHeight: 196,
  overflowY: "auto",
  boxShadow: "0 10px 26px rgba(40,80,130,.18)",
};

const suggestOpt: CSSProperties = {
  padding: "7px 10px",
  borderRadius: 4,
  cursor: "pointer",
};

const suggestOptMain: CSSProperties = {
  fontFamily: tokens.font.sans,
  fontSize: 12.5,
  color: tokens.ink,
};

const suggestOptSub: CSSProperties = {
  fontFamily: tokens.font.sans,
  fontSize: 10,
  color: tokens.muted2,
  marginTop: 1,
};

const suggestNote: CSSProperties = {
  padding: "8px 10px",
  fontFamily: tokens.font.sans,
  fontSize: 11,
  color: tokens.muted2,
};

// Inline validation / server error — only rendered when there is an error, so
// the happy-path layout is pixel-unchanged.
const errorText: CSSProperties = {
  marginTop: 8,
  fontSize: 10.5,
  letterSpacing: 0.5,
  lineHeight: 1.4,
  color: tokens.danger,
};

// Analog price pin on the 01 map. left/top come from mapMarkers() per-analog
// (projected from the real estimate); the chrome matches the former fixture
// pins 1:1 — only the data source changed (Finding #2).
const analogPin: CSSProperties = {
  position: "absolute",
  background: tokens.surface.w70,
  border: `1px solid ${tokens.line}`,
  borderRadius: 4,
  padding: "3px 7px",
  fontFamily: tokens.font.mono,
  fontSize: 9,
  lineHeight: 1.4,
  color: tokens.ink,
};

// РАДИУС dropdown panel — mirrors the <Dd> HUD panel (surface.w98 + soft blue
// shadow), sized to the narrow radius trigger and dropped just beneath it.
const radiusPanel: CSSProperties = {
  position: "absolute",
  left: 0,
  right: 0,
  top: "calc(100% + 4px)",
  background: tokens.surface.w98,
  border: `1px solid ${tokens.line}`,
  borderRadius: 6,
  zIndex: 30,
  padding: 3,
  boxShadow: "0 10px 26px rgba(40,80,130,.18)",
};

interface ParamsPanelProps {
  /** Called with the validated form payload on «ОЦЕНИТЬ КВАРТИРУ». Optional so
   *  the still-unwired v2 page renders without props (tsc-safe). */
  onSubmit?: (input: TradeInEstimateInput) => void;
  /** Disables the button + shows a pending label while the estimate is running. */
  isPending?: boolean;
  /** Server-side error to surface inline (validation errors are handled locally). */
  error?: string | null;
  /** Prefill for restore-by-id (?id=) — maps API enums back to RU dropdown labels. */
  initialValues?: Partial<TradeInEstimateInput>;
  /** Analog price pins for the 01 map, projected from the real estimate via
   *  mapMarkers(). Default [] → no price pins (never the fabricated fixtures). */
  markers?: MapMarker[];
}

// rooms number -> dropdown label. The design has no «Студия» option, so studio
// (0) and 1-room both map to "1"; >=5 collapses to "5+". null -> design default.
function initRoomsLabel(rooms: number | null | undefined): string {
  if (rooms == null) return "2";
  if (rooms >= 5) return "5+";
  if (rooms <= 1) return "1";
  return String(rooms);
}

function initHouseTypeLabel(ht: HouseType | undefined): string {
  return ht ? HOUSE_TYPE_RU[ht] : "Панельный";
}

function initRepairLabel(rs: RepairState | undefined): string {
  return rs ? REPAIR_RU[rs] : "Хороший";
}

// РАДИУС options. "Авто" (default) sends no radius_m → the backend keeps its
// two-tier default (1000 m primary / 2000 m fallback). A fixed value overrides
// both ("ищем строго в пределах X м"). Design dropdown was values-only.
const RADIUS_OPTIONS = ["Авто", "300 м", "500 м", "1000 м", "2000 м"];

// radius_m (metres) -> dropdown label. null/absent → "Авто" (legacy two-tier
// default), so a re-submit never silently narrows the search.
function initRadiusLabel(radiusM: number | null | undefined): string {
  if (radiusM == null) return "Авто";
  const label = `${radiusM} м`;
  return RADIUS_OPTIONS.includes(label) ? label : "Авто";
}

export default function ParamsPanel({
  onSubmit,
  isPending = false,
  error = null,
  initialValues,
  markers = [],
}: ParamsPanelProps) {
  const [openDd, setOpenDd] = useState<DdKey>(null);
  // Map zoom — applied as transform: scale() on the map content layer (not the
  // whole panel). Default 1 (identity → pixel-identical), step .25, clamp .6–2.5.
  const [zoom, setZoom] = useState(1);
  const [address, setAddress] = useState(initialValues?.address ?? "");
  const [area, setArea] = useState(
    initialValues?.area_m2 != null ? String(initialValues.area_m2) : "",
  );
  const [rooms, setRooms] = useState(initRoomsLabel(initialValues?.rooms));
  const [floor, setFloor] = useState(
    initialValues?.floor != null ? String(initialValues.floor) : "",
  );
  const [totalFloors, setTotalFloors] = useState(
    initialValues?.total_floors != null
      ? String(initialValues.total_floors)
      : "",
  );
  const [year, setYear] = useState(
    initialValues?.year_built != null ? String(initialValues.year_built) : "",
  );
  const [houseType, setHouseType] = useState(
    initHouseTypeLabel(initialValues?.house_type),
  );
  const [repair, setRepair] = useState(
    initRepairLabel(initialValues?.repair_state),
  );
  const [radius, setRadius] = useState(
    initRadiusLabel(initialValues?.radius_m),
  );
  const [balcony, setBalcony] = useState(initialValues?.has_balcony ?? true);
  const [validationError, setValidationError] = useState<string | null>(null);

  // ── Address autocomplete (geocode suggest, ЕКБ viewbox) ──
  // The typed string is debounced into `addressQuery` from inside the change
  // handler (a plain timer, not a useEffect-for-HTTP); `useGeocodeSuggest` is
  // gated at >=3 chars and fires on the debounced value. Picking a suggestion
  // fills the full address and captures lat/lon so the backend can skip geocoding.
  const [addressQuery, setAddressQuery] = useState(initialValues?.address ?? "");
  const [suggestOpen, setSuggestOpen] = useState(false);
  const [coords, setCoords] = useState<{ lat: number; lon: number } | null>(
    initialValues?.lat != null && initialValues?.lon != null
      ? { lat: initialValues.lat, lon: initialValues.lon }
      : null,
  );
  const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const suggest = useGeocodeSuggest(addressQuery);

  const handleAddressChange = (v: string) => {
    setAddress(v);
    setCoords(null); // a manual edit invalidates a previously picked point
    setSuggestOpen(v.trim().length >= 3);
    if (debounceRef.current) clearTimeout(debounceRef.current);
    debounceRef.current = setTimeout(() => setAddressQuery(v), 200);
  };

  const pickSuggestion = (s: GeocodeSuggestion) => {
    setAddress(s.full_address);
    setAddressQuery(s.full_address);
    setCoords({ lat: s.lat, lon: s.lon });
    setSuggestOpen(false);
  };

  // TODO(#395): CRM-интеграция не подключена — дропдаун присутствует визуально,
  // но неактивен (выбор ничего не делает).
  const crm = dropdownOptions.crm[0];

  const toggle = (k: Exclude<DdKey, null>) =>
    setOpenDd((cur) => (cur === k ? null : k));
  const pick = (setter: (v: string) => void) => (v: string) => {
    setter(v);
    setOpenDd(null);
  };

  const handleSubmit = () => {
    const trimmedAddress = address.trim();
    const areaNum = Number(area.replace(",", "."));
    if (trimmedAddress.length < 3) {
      setValidationError("Укажите адрес квартиры — минимум 3 символа");
      return;
    }
    if (!area.trim()) {
      setValidationError("Укажите площадь квартиры");
      return;
    }
    if (!Number.isFinite(areaNum) || areaNum <= 10) {
      setValidationError("Площадь должна быть больше 10 м²");
      return;
    }
    setValidationError(null);
    setSuggestOpen(false);
    onSubmit?.({
      address: trimmedAddress,
      area_m2: areaNum,
      rooms: rooms === "5+" ? 5 : Number(rooms),
      floor: floor.trim() ? Number(floor) : null,
      total_floors: totalFloors.trim() ? Number(totalFloors) : null,
      year_built: year.trim() ? Number(year) : undefined,
      house_type: HOUSE_TYPE_FROM_RU[houseType],
      repair_state: REPAIR_FROM_RU[repair],
      has_balcony: balcony,
      lat: coords?.lat ?? null,
      lon: coords?.lon ?? null,
      // "Авто" → null → backend keeps its two-tier default (1000 m primary /
      // 2000 m fallback). A fixed value overrides both. (Не отправлять 500 по
      // умолчанию — это сузило бы поиск аналогов и убрало fallback.)
      radius_m: radius === "Авто" ? null : parseInt(radius, 10),
    });
  };

  const balOn: CSSProperties = {
    border: `1px solid ${tokens.accent}`,
    color: tokens.accent,
    fontWeight: 600,
    background: "rgba(46,139,255,.1)",
  };
  const balOff: CSSProperties = {
    border: `1px solid ${tokens.line}`,
    color: tokens.muted,
    fontWeight: 400,
    background: "transparent",
  };
  const balBase: CSSProperties = {
    flex: 1,
    borderRadius: 6,
    display: "flex",
    alignItems: "center",
    justifyContent: "center",
    fontSize: 11,
    letterSpacing: 1,
    cursor: "pointer",
    transition: "all .15s",
  };

  // Outer radius ring tracks the selected РАДИУС (subtle, best-effort): 500 м
  // keeps the design's r=112 exactly; other values scale gently (and clamp) so the
  // ring never blows past the map box. The inner two rings stay fixed.
  const radiusM = parseInt(radius, 10);
  const outerRingR = Number.isFinite(radiusM)
    ? Math.round(Math.max(70, Math.min(155, 112 * Math.pow(radiusM / 500, 0.35))))
    : 112;

  return (
    <div
      style={{
        position: "relative",
        background: tokens.surface.w50,
        backdropFilter: "blur(6px)",
        border: `1px solid ${tokens.line2}`,
        borderRadius: 8,
        padding: "16px 18px",
        display: "flex",
        flexDirection: "column",
      }}
    >
      <style>{styles}</style>

      {/* HUD corner brackets */}
      <div
        style={{
          position: "absolute",
          left: -1,
          top: -1,
          width: 13,
          height: 13,
          borderLeft: `1.5px solid ${tokens.bracket}`,
          borderTop: `1.5px solid ${tokens.bracket}`,
        }}
      />
      <div
        style={{
          position: "absolute",
          right: -1,
          bottom: -1,
          width: 13,
          height: 13,
          borderRight: `1.5px solid ${tokens.bracket}`,
          borderBottom: `1.5px solid ${tokens.bracket}`,
        }}
      />

      {/* header */}
      <div
        style={{
          display: "flex",
          alignItems: "baseline",
          justifyContent: "space-between",
          marginBottom: 13,
        }}
      >
        <div style={{ display: "flex", alignItems: "baseline", gap: 10 }}>
          <span
            style={{
              fontFamily: tokens.font.mono,
              fontSize: 15,
              color: tokens.accent,
            }}
          >
            01
          </span>
          <span style={{ fontSize: 13, fontWeight: 600, letterSpacing: 2 }}>
            ПАРАМЕТРЫ КВАРТИРЫ
          </span>
        </div>
        <span
          style={{
            fontFamily: tokens.font.mono,
            fontSize: 10,
            letterSpacing: 1,
            color: tokens.muted2,
          }}
        >
          ШАГ 1/1
        </span>
      </div>

      {/* MAP */}
      <div
        style={{
          position: "relative",
          height: 140,
          border: `1px solid ${tokens.line}`,
          borderRadius: 6,
          overflow: "hidden",
          background: tokens.mapBg,
          flex: "0 0 auto",
        }}
      >
        {/* zoomable map content — scaled by the +/− controls. The map controls
            and corner bracket below are SIBLINGS (outside this layer) so they
            never scale. zoom=1 → scale() identity → pixel-identical to design. */}
        <div
          style={{
            position: "absolute",
            inset: 0,
            transform: `scale(${zoom})`,
            transformOrigin: "center",
            transition: "transform .15s ease-out",
          }}
        >
        <svg
          viewBox="0 0 440 210"
          preserveAspectRatio="xMidYMid slice"
          style={{
            position: "absolute",
            inset: 0,
            width: "100%",
            height: "100%",
          }}
        >
          <rect width="440" height="210" fill="#e6edf4" />
          <g stroke="#dbe5ef" strokeWidth={1}>
            <path d="M0 38H440M0 78H440M0 118H440M0 158H440M0 198H440" />
            <path d="M40 0V210M110 0V210M180 0V210M250 0V210M320 0V210M390 0V210" />
          </g>
          <g stroke="#f5f8fc" strokeWidth={8} strokeLinecap="round">
            <path d="M-10 92 H450" />
            <path d="M150 -10 V220" />
            <path d="M-10 30 L260 0" />
          </g>
          <g stroke="#eef3f9" strokeWidth={5}>
            <path d="M300 -10 L470 150" />
            <path d="M-10 170 H450" />
          </g>
          <path d="M150 92 L470 200" stroke="#cfe0f3" strokeWidth={6} />
          <g fill="none" stroke="#2e8bff" strokeDasharray="3 4">
            <circle cx="208" cy="100" r="44" opacity={0.55} />
            <circle cx="208" cy="100" r="80" opacity={0.4} />
            <circle cx="208" cy="100" r={outerRingR} opacity={0.28} />
          </g>
          <circle cx="208" cy="100" r="80" fill="#2e8bff" opacity={0.04} />
        </svg>

        {/* center pin */}
        <div
          style={{
            position: "absolute",
            left: "47%",
            top: "48%",
            transform: "translate(-50%,-100%)",
            display: "flex",
            flexDirection: "column",
            alignItems: "center",
          }}
        >
          <div
            style={{
              display: "flex",
              alignItems: "center",
              gap: 6,
              background: tokens.surface.w85,
              border: `1px solid ${tokens.accent}`,
              borderRadius: 4,
              padding: "4px 8px",
              whiteSpace: "nowrap",
              boxShadow: "0 3px 10px rgba(46,139,255,.25)",
            }}
          >
            <span
              style={{
                width: 7,
                height: 7,
                borderRadius: "50%",
                background: tokens.accent,
              }}
            />
            <span style={{ fontSize: 11, fontWeight: 600 }}>
              {address.trim() || "Адрес квартиры"}
            </span>
          </div>
          <div
            style={{
              width: 0,
              height: 0,
              borderLeft: "5px solid transparent",
              borderRight: "5px solid transparent",
              borderTop: `7px solid ${tokens.accent}`,
            }}
          />
        </div>

        {/* analog price pins — projected from the real estimate via mapMarkers().
            Empty markers (no estimate yet) → no price pins, just the subject pin +
            radius rings (Finding #2: never the fabricated fixture prices/dots). */}
        {markers.map((m, i) => (
          <div key={i} style={{ ...analogPin, left: m.left, top: m.top }}>
            {m.label}
            <br />
            <span style={{ color: tokens.muted }}>{m.sub}</span>
          </div>
        ))}
        </div>

        {/* map controls */}
        <div
          style={{
            position: "absolute",
            right: 9,
            bottom: 9,
            display: "flex",
            flexDirection: "column",
            gap: 5,
          }}
        >
          <div
            style={{
              width: 24,
              height: 24,
              background: tokens.surface.w80,
              border: `1px solid ${tokens.line}`,
              borderRadius: 4,
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              cursor: "pointer",
              transition: "all .15s",
              fontSize: 15,
              color: tokens.muted,
            }}
            onClick={() => setZoom((z) => Math.min(2.5, z + 0.25))}
          >
            +
          </div>
          <div
            style={{
              width: 24,
              height: 24,
              background: tokens.surface.w80,
              border: `1px solid ${tokens.line}`,
              borderRadius: 4,
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              cursor: "pointer",
              transition: "all .15s",
              fontSize: 15,
              color: tokens.muted,
            }}
            onClick={() => setZoom((z) => Math.max(0.6, z - 0.25))}
          >
            −
          </div>
          <div
            style={{
              width: 24,
              height: 24,
              background: tokens.surface.w80,
              border: `1px solid ${tokens.line}`,
              borderRadius: 4,
              display: "flex",
              alignItems: "center",
              justifyContent: "center",
              cursor: "pointer",
              transition: "all .15s",
              color: tokens.accent,
            }}
          >
            <svg width="13" height="13" viewBox="0 0 13 13" fill="none">
              <circle cx="6.5" cy="6.5" r="4" stroke="#2e8bff" />
              <line x1="6.5" y1="0" x2="6.5" y2="2.5" stroke="#2e8bff" />
              <line x1="6.5" y1="10.5" x2="6.5" y2="13" stroke="#2e8bff" />
              <line x1="0" y1="6.5" x2="2.5" y2="6.5" stroke="#2e8bff" />
              <line x1="10.5" y1="6.5" x2="13" y2="6.5" stroke="#2e8bff" />
            </svg>
          </div>
        </div>

        {/* map corner bracket */}
        <div
          style={{
            position: "absolute",
            left: 8,
            top: 8,
            width: 12,
            height: 12,
            borderLeft: `1.5px solid ${tokens.accent}`,
            borderTop: `1.5px solid ${tokens.accent}`,
          }}
        />
      </div>

      {/* radius row */}
      <div
        style={{
          display: "flex",
          alignItems: "center",
          gap: 14,
          marginTop: 13,
          flex: "0 0 auto",
        }}
      >
        <span style={{ fontSize: 10, letterSpacing: 1.5, color: tokens.muted }}>
          РАДИУС АНАЛИЗА
        </span>
        {/* РАДИУС — selectable analysis radius (wired: parsed to radius_m on
            submit). Trigger markup unchanged; now toggles a Dd-style option panel
            and uses the shared pp-dd-trigger hover cue. */}
        <div style={{ position: "relative" }}>
          <div
            className="pp-dd-trigger"
            onClick={() => toggle("radius")}
            style={{
              display: "flex",
              alignItems: "center",
              gap: 8,
              background: tokens.surface.w60,
              borderRadius: 5,
              padding: "6px 10px",
              fontSize: 12,
              fontFamily: tokens.font.mono,
              cursor: "pointer",
            }}
          >
            <span>{radius}</span>
            <span style={{ color: tokens.muted2, fontSize: 9 }}>▼</span>
          </div>
          {openDd === "radius" && (
            <div style={radiusPanel}>
              {RADIUS_OPTIONS.map((o) => (
                <div
                  key={o}
                  className="pp-dd-opt"
                  onClick={() => pick(setRadius)(o)}
                  style={{
                    padding: "7px 10px",
                    fontSize: 12,
                    fontFamily: tokens.font.mono,
                    borderRadius: 4,
                    cursor: "pointer",
                    color: o === radius ? tokens.accent : tokens.ink,
                    fontWeight: o === radius ? 600 : 400,
                  }}
                >
                  {o}
                </div>
              ))}
            </div>
          )}
        </div>
        <div
          style={{
            display: "flex",
            alignItems: "center",
            gap: 16,
            fontSize: 10.5,
            color: tokens.muted,
            marginLeft: "auto",
          }}
        >
          <span style={{ display: "flex", alignItems: "center", gap: 6 }}>
            <span
              style={{
                width: 8,
                height: 8,
                borderRadius: "50%",
                background: tokens.accent,
              }}
            />
            Объявления
          </span>
          <span style={{ display: "flex", alignItems: "center", gap: 6 }}>
            <span
              style={{
                width: 8,
                height: 8,
                borderRadius: "50%",
                background: tokens.dealDot,
              }}
            />
            Сделки
          </span>
        </div>
      </div>

      {/* INPUTS */}
      <div
        style={{
          marginTop: 10,
          display: "flex",
          flexDirection: "column",
          gap: 5,
          flex: 1,
          minHeight: 0,
        }}
      >
        <div>
          <div style={hintLabel}>АДРЕС (YANDEX / NOMINATIM)</div>
          <div style={{ position: "relative" }}>
            {/* Address autocomplete (geocode suggest, ЕКБ viewbox). Typed text
                is debounced into `addressQuery`; the dropdown below mirrors the
                HUD <Dd> panel styling. Picking an item fills the full address
                and captures lat/lon for the estimate payload. */}
            <input
              type="text"
              value={address}
              onChange={(e) => handleAddressChange(e.target.value)}
              onFocus={() => {
                if (address.trim().length >= 3) setSuggestOpen(true);
              }}
              onBlur={() => setSuggestOpen(false)}
              onKeyDown={(e) => {
                if (e.key === "Enter") handleSubmit();
                if (e.key === "Escape") setSuggestOpen(false);
              }}
              placeholder="Город, улица, дом"
              autoComplete="off"
              style={addressField}
            />
            <svg
              style={{
                position: "absolute",
                right: 12,
                top: "50%",
                transform: "translateY(-50%)",
              }}
              width="15"
              height="15"
              viewBox="0 0 15 15"
              fill="none"
            >
              <circle cx="7.5" cy="7.5" r="4.5" stroke="#2e8bff" />
              <line x1="7.5" y1="0" x2="7.5" y2="3" stroke="#2e8bff" />
              <line x1="7.5" y1="12" x2="7.5" y2="15" stroke="#2e8bff" />
              <line x1="0" y1="7.5" x2="3" y2="7.5" stroke="#2e8bff" />
              <line x1="12" y1="7.5" x2="15" y2="7.5" stroke="#2e8bff" />
            </svg>
            {suggestOpen && addressQuery.trim().length >= 3 && (
              <div style={suggestPanel}>
                {suggest.data && suggest.data.length > 0 ? (
                  suggest.data.map((s, i) => (
                    <div
                      key={`${s.full_address}-${i}`}
                      className="pp-dd-opt"
                      // onMouseDown (not onClick) + preventDefault fires before
                      // the input's onBlur, so the pick lands before the list closes.
                      onMouseDown={(e) => {
                        e.preventDefault();
                        pickSuggestion(s);
                      }}
                      style={suggestOpt}
                    >
                      <div style={suggestOptMain}>{s.label}</div>
                      {s.full_address !== s.label && (
                        <div style={suggestOptSub}>{s.full_address}</div>
                      )}
                    </div>
                  ))
                ) : suggest.isFetching ? (
                  <div style={suggestNote}>Поиск…</div>
                ) : (
                  <div style={suggestNote}>Ничего не найдено</div>
                )}
              </div>
            )}
          </div>
        </div>

        <div style={fieldRow}>
          <div>
            <div style={hintLabel}>ПЛОЩАДЬ, М²</div>
            <input
              type="text"
              inputMode="decimal"
              value={area}
              onChange={(e) =>
                setArea(e.target.value.replace(/[^\d.,]/g, ""))
              }
              onKeyDown={(e) => {
                if (e.key === "Enter") handleSubmit();
              }}
              placeholder="напр. 54"
              style={inputField}
            />
          </div>
          <div>
            <div style={hintLabel}>КОМНАТ</div>
            <Dd
              open={openDd === "rooms"}
              onToggle={() => toggle("rooms")}
              value={rooms}
              options={dropdownOptions.rooms}
              onSelect={pick(setRooms)}
              mono
              triggerFontSize={14}
              optionFontSize={13}
            />
          </div>
        </div>

        <div style={fieldRow}>
          <div>
            <div style={{ display: "flex", justifyContent: "space-between" }}>
              <span style={hintLabel}>ЭТАЖ</span>
              <span style={optHint}>если знаешь</span>
            </div>
            <input
              type="text"
              inputMode="numeric"
              value={floor}
              onChange={(e) => setFloor(e.target.value.replace(/\D/g, ""))}
              onKeyDown={(e) => {
                if (e.key === "Enter") handleSubmit();
              }}
              placeholder="—"
              style={inputField}
            />
          </div>
          <div>
            <div style={{ display: "flex", justifyContent: "space-between" }}>
              <span style={hintLabel}>ВСЕГО ЭТАЖЕЙ</span>
              <span style={optHint}>если знаешь</span>
            </div>
            <input
              type="text"
              inputMode="numeric"
              value={totalFloors}
              onChange={(e) =>
                setTotalFloors(e.target.value.replace(/\D/g, ""))
              }
              onKeyDown={(e) => {
                if (e.key === "Enter") handleSubmit();
              }}
              placeholder="—"
              style={inputField}
            />
          </div>
        </div>

        <div
          style={{
            fontSize: 10,
            color: tokens.hint,
            lineHeight: 1.4,
            marginTop: -3,
          }}
        >
          1-й и последний этаж снижают цену на 5–10% — заполни если знаешь
        </div>

        <div style={fieldRow}>
          <div>
            <div style={{ display: "flex", justifyContent: "space-between" }}>
              <span style={hintLabel}>ГОД ПОСТРОЙКИ</span>
              <span style={optHint}>опц.</span>
            </div>
            <input
              type="text"
              inputMode="numeric"
              value={year}
              onChange={(e) => setYear(e.target.value.replace(/\D/g, ""))}
              onKeyDown={(e) => {
                if (e.key === "Enter") handleSubmit();
              }}
              placeholder="—"
              style={inputField}
            />
          </div>
          <div>
            <div style={{ display: "flex", justifyContent: "space-between" }}>
              <span style={hintLabel}>ТИП ДОМА</span>
              <span style={optHint}>опц.</span>
            </div>
            <Dd
              open={openDd === "houseType"}
              onToggle={() => toggle("houseType")}
              value={houseType}
              options={dropdownOptions.houseType}
              onSelect={pick(setHouseType)}
              mono={false}
              triggerFontSize={13}
              optionFontSize={12}
            />
          </div>
        </div>

        <div style={fieldRow}>
          <div>
            <div style={{ display: "flex", justifyContent: "space-between" }}>
              <span style={hintLabel}>СОСТОЯНИЕ РЕМОНТА</span>
              <span style={optHint}>опц.</span>
            </div>
            <Dd
              open={openDd === "repair"}
              onToggle={() => toggle("repair")}
              value={repair}
              options={dropdownOptions.repair}
              onSelect={pick(setRepair)}
              mono={false}
              triggerFontSize={13}
              optionFontSize={12}
            />
          </div>
          <div>
            <div style={hintLabel}>БАЛКОН / ЛОДЖИЯ</div>
            <div style={{ height: 40, display: "flex", gap: 6 }}>
              <div
                onClick={() => setBalcony(false)}
                style={{ ...balBase, ...(balcony ? balOff : balOn) }}
              >
                НЕТ
              </div>
              <div
                onClick={() => setBalcony(true)}
                style={{ ...balBase, ...(balcony ? balOn : balOff) }}
              >
                ДА
              </div>
            </div>
          </div>
        </div>

        <div>
          <div style={{ display: "flex", justifyContent: "space-between" }}>
            <span style={hintLabel}>CRM — ДЛЯ МЕНЕДЖЕРА</span>
            <span style={optHint}>опц.</span>
          </div>
          {/* CRM — non-functional (no backend, #395). Markup kept; open is
              forced false + no-op handlers so it never expands. TODO(wire). */}
          <Dd
            open={false}
            onToggle={() => undefined}
            value={crm}
            options={dropdownOptions.crm}
            onSelect={() => undefined}
            mono={false}
            triggerFontSize={12}
            optionFontSize={12}
            variant="dashed"
            openUp
            triggerHeight={34}
            triggerBg={tokens.surface.w50}
            triggerColor={tokens.hint}
            caretColor={tokens.muted4}
          />
        </div>
      </div>

      {/* button */}
      <button
        type="button"
        className="pp-eval-btn"
        onClick={handleSubmit}
        disabled={isPending}
        style={{
          marginTop: 9,
          height: 46,
          width: "100%",
          border: `1px solid ${tokens.accent}`,
          borderRadius: 7,
          display: "flex",
          alignItems: "center",
          justifyContent: "center",
          position: "relative",
          cursor: isPending ? "wait" : "pointer",
          opacity: isPending ? 0.6 : 1,
          fontFamily: tokens.font.sans,
          color: tokens.ink,
          flex: "0 0 auto",
        }}
      >
        <span style={{ fontSize: 14, fontWeight: 600, letterSpacing: 3 }}>
          {isPending ? "ОЦЕНИВАЕМ…" : "ОЦЕНИТЬ КВАРТИРУ"}
        </span>
        <span
          style={{
            position: "absolute",
            right: 16,
            display: "flex",
            alignItems: "center",
            justifyContent: "center",
            width: 26,
            height: 26,
            border: `1px solid ${tokens.accent}`,
            borderRadius: "50%",
            color: tokens.accent,
          }}
        >
          →
        </span>
      </button>

      {/* inline error (validation or server) — only when present */}
      {(validationError || error) && (
        <div style={errorText} role="alert">
          {validationError ?? error}
        </div>
      )}

      {/* cache status */}
      <div
        style={{
          display: "flex",
          alignItems: "center",
          justifyContent: "space-between",
          marginTop: 11,
          flex: "0 0 auto",
          fontSize: 9.5,
          letterSpacing: 1.5,
          color: tokens.muted2,
        }}
      >
        <span>КЭШ ПО АДРЕСУ — 24 Ч</span>
        <span
          style={{
            display: "flex",
            alignItems: "center",
            gap: 6,
            color: tokens.success,
          }}
        >
          <span
            className="pp-dot"
            style={{
              width: 7,
              height: 7,
              borderRadius: "50%",
              background: tokens.success,
              boxShadow: `0 0 6px ${tokens.success}`,
            }}
          />
          ГОТОВ
        </span>
      </div>
    </div>
  );
}
