"use client";

/* eslint-disable @next/next/no-img-element -- фото аналога с внешнего CDN, next/image не нужен */

/**
 * HeroSummary — Секция 1 «Сводка» из mockup tradein.html.
 * Показывает медиану + достоверность CV + параметры объекта + 2 ценовых бара (объявления / сделки).
 */
import { useState } from "react";
import type { AggregatedEstimate, TradeInEstimateInput, HouseType, RepairState, ConfidenceLevel } from "@/types/trade-in";
import { asHouseType, asRepairState } from "@/types/trade-in";
import { useActiveBrandSlug, useBrand } from "@/lib/useBrand";
import { sourceDot, sourceLabel } from "@/lib/source-registry";
import { HeroTransparency } from "./HeroTransparency";

interface Props {
  estimate: AggregatedEstimate;
  input: TradeInEstimateInput;
  onResubmit: (patch: { house_type?: HouseType; repair_state?: RepairState }) => void;
  isResubmitting?: boolean;
}

const HOUSE_TYPE_LABELS: Record<string, string> = {
  panel: "Панельный",
  brick: "Кирпичный",
  monolith: "Монолит",
  monolith_brick: "Монолит-кирпич",
  other: "Другое",
};

const REPAIR_LABELS: Record<string, string> = {
  needs_repair: "Требует ремонта",
  standard: "Стандартный",
  good: "Хороший",
  excellent: "Евроремонт",
};

const CONF_LABELS: Record<string, { txt: string; color: string }> = {
  high: { txt: "высокая", color: "var(--success)" },
  medium: { txt: "средняя", color: "var(--warn)" },
  low: { txt: "низкая", color: "var(--danger)" },
};

// #2002: человекочитаемые лейблы класса премиального дома. Ключи — как их отдаёт
// бэкенд (lower-case RU). Неизвестный класс → капитализируем первую букву.
const PREMIUM_CLASS_LABELS: Record<string, string> = {
  премиум: "Премиум",
  элит: "Элит",
  бизнес: "Бизнес-класс",
};

function premiumBadgeLabel(cls: string | null | undefined): string {
  if (!cls) return "Премиальный дом";
  const known = PREMIUM_CLASS_LABELS[cls.trim().toLowerCase()];
  if (known) return known;
  const t = cls.trim();
  return t.charAt(0).toUpperCase() + t.slice(1);
}

function formatMln(rub: number): string {
  return `${(rub / 1_000_000).toFixed(2).replace(".", ",")} млн`;
}

/** Русское склонение слова «источник» по количеству. */
function pluralSources(n: number): string {
  const mod10 = n % 10;
  const mod100 = n % 100;
  if (mod10 === 1 && mod100 !== 11) return "источник";
  if (mod10 >= 2 && mod10 <= 4 && (mod100 < 12 || mod100 > 14)) return "источника";
  return "источников";
}

/**
 * Честная метрика разброса (#2211, находка 4).
 * Прежний код называл «CV» величину (range_high − range_low) / median — это ширина
 * межквартильного диапазона, а НЕ коэффициент вариации. Теперь:
 *   • если бэкенд отдал настоящий cv (std/mean ₽/м², #2043) — показываем его как «CV»;
 *   • иначе (старые оценки без cv) — показываем ширину диапазона под честным лейблом
 *     «разброс», без аббревиатуры CV. Новая метрика не выдумывается.
 */
function dispersionMetric(
  estimate: AggregatedEstimate,
): { label: string; value: string; title: string } | null {
  if (typeof estimate.cv === "number" && estimate.cv > 0) {
    return {
      label: "CV",
      value: `${(estimate.cv * 100).toFixed(1)}%`,
      title: "Коэффициент вариации цен аналогов (σ/среднее ₽/м²)",
    };
  }
  const m = estimate.median_price_rub;
  if (m > 0 && estimate.range_high_rub > estimate.range_low_rub) {
    const width = ((estimate.range_high_rub - estimate.range_low_rub) / m) * 100;
    return {
      label: "разброс",
      value: `${width.toFixed(1)}%`,
      title: "Ширина ценового диапазона относительно медианы (не коэффициент вариации)",
    };
  }
  return null;
}

/** Returns true when address looks like a non-residential geocode fallback */
function isMockAddress(estimate: AggregatedEstimate): boolean {
  const addr = estimate.target_address ?? "";
  if (/^(Склад|Гараж|Подсобка|Нежилое),\s/i.test(addr)) return true;
  if (estimate.confidence_explanation?.startsWith("address_not_geocoded")) return true;
  return false;
}

export function HeroSummary({ estimate, input, onResubmit, isResubmitting = false }: Props) {
  const dispersion = dispersionMetric(estimate);
  // Defensive depth (audit #1871 P1.2): ghost-anchor guard. Если legacy-строка /
  // rehydrate / контракт-drift отдаст confidence='high' при n_analogs=0, UI должен
  // показывать «низкая», а не «высокая · 0 объектов». Бэкенд уже не отдаёт high при
  // n_analogs=0, это второй слой защиты для отображения (сырое значение в analytics
  // не нужно). Caveat-текст приходит из estimate.confidence_explanation (backend).
  const effectiveConfidence: ConfidenceLevel =
    estimate.n_analogs === 0 ? "low" : estimate.confidence;
  const conf = CONF_LABELS[effectiveConfidence] ?? CONF_LABELS.low;
  // Brand-aware PDF + lead-тема. useActiveBrandSlug (merged) reuse, brand name из /brand.
  const activeBrandSlug = useActiveBrandSlug();
  const { data: brand } = useBrand();

  // Бейдж точности гео-привязки адреса (DaData qc_geo). null/undefined → ничего не рисуем.
  const precisionBadge =
    estimate.address_precision === "house"
      ? { text: "адрес: до дома", className: "precision-badge precision-house" }
      : estimate.address_precision === "street"
        ? { text: "адрес: до улицы", className: "precision-badge precision-street" }
        : estimate.address_precision === "approximate"
          ? { text: "адрес: приблизительно", className: "precision-badge precision-approx" }
          : null;
  const m = estimate.median_price_rub;
  const lo = estimate.range_low_rub;
  const hi = estimate.range_high_rub;
  // Пустая оценка (backend #697): нет аналогов/сделок → median<=0. Вместо
  // «0,00 млн ₽» в headline показываем явный empty-state.
  const insufficientData = estimate.insufficient_data || m <= 0;

  // ── Ожидаемая цена продажи (S3). Рисуем второй блок только при валидном числе:
  //    null/undefined/0 (старые оценки или пустая ratio-таблица) → одиночный layout. ──
  const sold = estimate.expected_sold_price_rub;
  const hasSold = typeof sold === "number" && sold > 0;
  const soldLo = estimate.expected_sold_range_low_rub;
  const soldHi = estimate.expected_sold_range_high_rub;
  const soldPerM2 = estimate.expected_sold_per_m2;
  // Скидка запрос→продажа: из ratio при наличии, иначе из самих чисел. Только если ≥1%.
  const discountPct = hasSold
    ? Math.round(
        (typeof estimate.asking_to_sold_ratio === "number" && estimate.asking_to_sold_ratio > 0
          ? 1 - estimate.asking_to_sold_ratio
          : 1 - (sold as number) / m) * 100,
      )
    : 0;
  const showDiscount = hasSold && discountPct >= 1;

  const showMockDisclaimer = isMockAddress(estimate);

  // ── #2002: сигналы «ручной оценки» + «премиального дома» ──
  // Старые/кешированные оценки эти поля не содержат → undefined ≈ false / [].
  const showManualReview = estimate.manual_review_recommended === true;
  const manualReviewReasons = estimate.manual_review_reasons ?? [];
  const showPremiumBadge = estimate.premium_building === true;
  const premiumLabel = premiumBadgeLabel(estimate.premium_building_class);

  // ── #651: Avito IMV anchor — реальная рыночная оценка дома. Маркер у цены. ──
  const imv = estimate.avito_imv;
  const imvPrice =
    imv && typeof imv.recommended_price === "number" && imv.recommended_price > 0
      ? imv.recommended_price
      : null;
  // ── #652: коридор реальных ДКП-сделок (advisory). ₽/м² → млн через площадь. ──
  const dkp = estimate.dkp_corridor;
  const dkpArea = estimate.area_m2 ?? input.area_m2 ?? 0;
  const showDkp =
    !!dkp && dkp.count > 0 && dkpArea > 0 && dkp.low_ppm2 > 0 && dkp.high_ppm2 > 0;

  // Progressive enrichment state
  const needsHouseType = estimate.house_type === null;
  const needsRepairState = estimate.repair_state === null;
  const showEnrichment =
    (needsHouseType || needsRepairState) && effectiveConfidence !== "low";
  const [enrichHouseType, setEnrichHouseType] = useState<string>("");
  const [enrichRepairState, setEnrichRepairState] = useState<string>("");
  // Фото первого аналога с картинкой — вместо пустого серого плейсхолдера.
  const heroPhoto = estimate.analogs.find((a) => a.photo_url)?.photo_url ?? null;
  // Расчёт ширины для price bar (50% = середина): медиана внутри min/max
  const span = hi - lo;
  const medianPctRaw = span > 0 ? ((m - lo) / span) * 100 : 50;
  const medianPct = Math.max(5, Math.min(95, medianPctRaw));

  // Параметры квартиры берём из estimate (надёжно при восстановлении по ?id=),
  // с откатом на форму input — у свежей оценки оба источника совпадают.
  const floor = estimate.floor ?? input.floor;
  const totalFloors = estimate.total_floors ?? input.total_floors;
  const areaM2 = estimate.area_m2 ?? input.area_m2;
  const rooms = estimate.rooms ?? input.rooms;
  const yearBuilt = estimate.year_built ?? input.year_built;
  const houseType = estimate.house_type ?? input.house_type;
  const repairState = estimate.repair_state ?? input.repair_state;
  const hasBalcony = estimate.has_balcony ?? input.has_balcony;
  const estDaysOnMarket = estimate.est_days_on_market;

  function handleEnrichSubmit(e: React.FormEvent) {
    e.preventDefault();
    const patch: { house_type?: HouseType; repair_state?: RepairState } = {};
    const ht = asHouseType(enrichHouseType);
    if (ht) patch.house_type = ht;
    const rs = asRepairState(enrichRepairState);
    if (rs) patch.repair_state = rs;
    onResubmit(patch);
  }

  return (
    <article className="card hero-card">
      {showMockDisclaimer && (
        <div
          role="status"
          aria-live="polite"
          style={{
            margin: "16px 16px 0",
            padding: "12px 16px",
            border: "1px solid var(--accent-2)",
            background: "var(--accent-2-soft)",
            color: "var(--fg)",
            borderRadius: "var(--radius)",
            fontSize: 13,
            lineHeight: 1.4,
          }}
        >
          <span
            aria-hidden="true"
            style={{
              display: "inline-block",
              width: 8,
              height: 8,
              borderRadius: "50%",
              background: "var(--warn)",
              marginRight: 8,
              verticalAlign: "middle",
            }}
          />
          Адрес распознан неточно — оценка может быть приближённой.
          Уточните адрес и пересчитайте для более точного результата.
        </div>
      )}
      <div className="card-head">
        <div>
          <div className="section-kicker">Секция 1 · Сводка</div>
          <h2>Анализ рынка и расчёт стоимости</h2>
        </div>
        <div className="card-meta">
          <div>
            Источник:{" "}
            <b>
              {estimate.sources_used.length > 0
                ? `агрегация ${estimate.sources_used.length} ${pluralSources(estimate.sources_used.length)}`
                : "источники не зафиксированы"}
            </b>
          </div>
          <div style={{ marginTop: 4 }}>
            Достоверность · <b style={{ color: conf.color }}>{conf.txt}</b>
            {dispersion && (
              <>
                {" · "}
                <abbr title={dispersion.title}>{dispersion.label}</abbr> {dispersion.value}
              </>
            )}
            {precisionBadge && (
              <span className={precisionBadge.className} title="Точность определения адреса">
                {precisionBadge.text}
              </span>
            )}
          </div>
        </div>
      </div>

      <div className="hero-top">
        <div className="hero-photo" role="img" aria-label="Фото квартиры-аналога">
          {heroPhoto && (
            <img
              src={heroPhoto}
              alt="Фото похожего объекта"
              onError={(e) => {
                e.currentTarget.style.display = "none";
              }}
              style={{ width: "100%", height: "100%", objectFit: "cover", display: "block" }}
            />
          )}
          <div className="photo-meta">
            {heroPhoto
              ? `фото аналога${estimate.sources_used[0] ? ` · ${sourceLabel(estimate.sources_used[0])}` : ""}`
              : estimate.sources_used.length > 0
                ? `${sourceLabel(estimate.sources_used[0])} · ${estimate.n_analogs} аналогов`
                : "Нет фото"}
          </div>
        </div>
        <div className="hero-meta">
          <div className="hero-address">
            {estimate.target_address ?? input.address}
          </div>
          {showPremiumBadge && (
            <span
              className="premium-badge"
              title="Объект в премиальном жилом комплексе"
            >
              {premiumLabel}
            </span>
          )}
          <div className="hero-cad">
            ЭТАЖ {floor}/{totalFloors}
            {yearBuilt ? ` · ${yearBuilt}` : ""}
          </div>

          <div className="meta-grid">
            {yearBuilt && (
              <div className="meta-row">
                <span className="k">Год постройки</span>
                <span className="v mono">{yearBuilt}</span>
              </div>
            )}
            {houseType && (
              <div className="meta-row">
                <span className="k">Тип дома</span>
                <span className="v">{HOUSE_TYPE_LABELS[houseType] ?? houseType}</span>
              </div>
            )}
            <div className="meta-row">
              <span className="k">Этаж</span>
              <span className="v mono">
                {floor} / {totalFloors}
              </span>
            </div>
            <div className="meta-row">
              <span className="k">Площадь</span>
              <span className="v mono">{areaM2} м²</span>
            </div>
            <div className="meta-row">
              <span className="k">Планировка</span>
              <span className="v">
                {rooms === 0 ? "Студия" : `${rooms}-к`}
              </span>
            </div>
            {repairState && (
              <div className="meta-row">
                <span className="k">Состояние</span>
                <span className="v">{REPAIR_LABELS[repairState] ?? repairState}</span>
              </div>
            )}
            <div className="meta-row">
              <span className="k">Балкон</span>
              <span className="v">{hasBalcony ? "есть" : "нет"}</span>
            </div>
            <div className="meta-row">
              <span className="k">Аналогов</span>
              <span className="v mono">{estimate.n_analogs}</span>
            </div>
            <div className="meta-row">
              <span className="k">Срок продажи</span>
              {estDaysOnMarket !== null ? (
                <span className="v mono">{estDaysOnMarket} дн.</span>
              ) : (
                <span className="v" style={{ color: "var(--muted)" }}>нет данных</span>
              )}
            </div>
          </div>
        </div>
      </div>

      {/* ── #2002: баннер «рекомендуется ручная оценка». Рядом с headline-числом,
          amber/non-alarming (токены --accent-2). Не презентуем авто-оценку как
          твёрдую оферту, когда бэкенд считает её рискованной. */}
      {showManualReview && (
        <div
          className="manual-review"
          role="note"
          aria-label="Рекомендуется ручная оценка"
        >
          <div className="manual-review__head">
            <span className="manual-review__icon" aria-hidden="true">
              <svg
                width="16"
                height="16"
                viewBox="0 0 24 24"
                fill="none"
                stroke="currentColor"
                strokeWidth="2.2"
                strokeLinecap="round"
                strokeLinejoin="round"
              >
                <path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0Z" />
                <line x1="12" y1="9" x2="12" y2="13" />
                <line x1="12" y1="17" x2="12.01" y2="17" />
              </svg>
            </span>
            <span className="manual-review__title">
              Рекомендуется ручная оценка
            </span>
          </div>
          <p className="manual-review__lead">
            Автоматический расчёт по этому объекту менее надёжен — используйте его
            как ориентир, а не как окончательную цену. Для точной стоимости уточните
            оценку у специалиста.
          </p>
          {manualReviewReasons.length > 0 && (
            <ul className="manual-review__reasons">
              {manualReviewReasons.map((reason, i) => (
                <li key={`mr-${i}`}>{reason}</li>
              ))}
            </ul>
          )}
        </div>
      )}

      {/* ── §1.1 «Одна цифра + честный разброс»: expected_sold — цифра-герой
          («оценка» / ожидаемая цена продажи), median — вторичный ориентир
          «рекомендованная цена в объявлении», НЕ конкурирует за внимание.
          При hasSold=false (старые оценки / пустая ratio) median остаётся
          единственной честной цифрой. Empty-state при insufficientData. */}
      <div
        className={
          !insufficientData && hasSold
            ? "hero-duo hero-duo--sold-primary"
            : "hero-duo hero-duo--single"
        }
      >
        {insufficientData ? (
          <div className="hero-duo__cell">
            <div className="hero-duo__label">Оценка недоступна</div>
            <div className="headline-value-empty">Недостаточно данных для оценки</div>
            <p className="headline-note">
              Рядом не нашлось достаточно сопоставимых объявлений и сделок, чтобы
              рассчитать цену. Уточните адрес или параметры объекта и попробуйте
              снова.
            </p>
          </div>
        ) : hasSold ? (
          <>
            {/* Цифра-герой: «оценка» = ожидаемая цена продажи (expected_sold) */}
            <div className="hero-duo__cell" data-role="sold">
              <div className="hero-duo__label">
                Оценка · ожидаемая цена продажи
                {showDiscount && (
                  <span
                    className="hero-duo__delta"
                    title="Насколько ожидаемая цена сделки ниже цены в объявлении. Это разрыв «объявление → реальная сделка», а не скидка сервиса."
                    aria-label={`ожидаемая цена продажи на ${discountPct}% ниже цены в объявлении`}
                  >
                    −{discountPct}% к объявлению
                  </span>
                )}
              </div>
              <div className="hero-duo__value mono">{formatMln(sold as number)} ₽</div>
              {typeof soldLo === "number" && typeof soldHi === "number" && (
                <div className="hero-duo__range mono">
                  рыночный разброс: {formatMln(soldLo)} – {formatMln(soldHi)} ₽
                </div>
              )}
              {typeof soldPerM2 === "number" && (
                <div className="hero-duo__perm2 mono">
                  {soldPerM2.toLocaleString("ru-RU")} ₽/м²
                </div>
              )}
              <p className="hero-duo__hint">
                Диапазон отражает разброс цен по рынку, а не погрешность оценки.
                {/* #2209: честное раскрытие точности — замеренное покрытие бэктестом
                    по реальным ДКП Росреестра (2026-07: n=276, coverage 81.5%).
                    Обновлять при существенном изменении baseline-метрик. */}{" "}
                По нашему замеру на реальных сделках Росреестра фактическая цена
                продажи попадает в диапазон в&nbsp;~80% случаев (июль&nbsp;2026).
              </p>
              {estimate.ratio_basis === "global_fallback" ? (
                <div className="hero-duo__note">
                  Ср.-городской коэффициент (мало сделок в сегменте)
                </div>
              ) : estimate.ratio_basis === "per_rooms" ? (
                <div className="hero-duo__note">
                  Коэффициент по сделкам той же планировки
                </div>
              ) : null}
            </div>

            {/* Вторичный ориентир: рекомендованная цена в объявлении (median) */}
            <div className="hero-duo__cell" data-role="asking">
              <div className="hero-duo__label">Рекомендованная цена в объявлении</div>
              <div className="hero-duo__value mono">{formatMln(m)} ₽</div>
              <div className="hero-duo__range mono">
                {formatMln(lo)} – {formatMln(hi)} ₽
              </div>
              <div className="hero-duo__perm2 mono">
                {estimate.median_price_per_m2.toLocaleString("ru-RU")} ₽/м²
              </div>
            </div>
          </>
        ) : (
          <div className="hero-duo__cell" data-role="asking">
            <div className="hero-duo__label">Рекомендованная цена в объявлении</div>
            <div className="hero-duo__value mono">{formatMln(m)} ₽</div>
            <div className="hero-duo__range mono">
              {formatMln(lo)} – {formatMln(hi)} ₽
            </div>
            <div className="hero-duo__perm2 mono">
              {estimate.median_price_per_m2.toLocaleString("ru-RU")} ₽/м²
            </div>
          </div>
        )}
      </div>

      {/* ── Компактный ряд бенчмарков: Avito IMV · Cian · ДКП ──
          Вторичный контекст-якорь под hero, не конкурирует по размеру. */}
      {!insufficientData && (imvPrice !== null || estimate.cian_valuation?.sale_price_rub != null || showDkp) && (
        <div className="hero-benchmarks">
          {imvPrice !== null && (
            <div className="bench-chip">
              <span className="bench-chip__src">Avito <abbr title="Information Market Value — оценка рыночной стоимости от Avito">IMV</abbr></span>
              <span className="bench-chip__val mono">{formatMln(imvPrice)} ₽</span>
            </div>
          )}
          {estimate.cian_valuation?.sale_price_rub != null && (
            <div className="bench-chip">
              <span className="bench-chip__src">Cian</span>
              <span className="bench-chip__val mono">
                {formatMln(estimate.cian_valuation.sale_price_rub)} ₽
              </span>
              {estimate.cian_valuation.rent_price_rub != null && (
                <span className="bench-chip__sub mono">
                  аренда {(estimate.cian_valuation.rent_price_rub / 1000).toFixed(0)} т.р./мес
                </span>
              )}
            </div>
          )}
          {showDkp && dkp && (
            <div className="bench-chip">
              <span className="bench-chip__src"><abbr title="Договор купли-продажи (реальные сделки Росреестра)">ДКП</abbr> · Росреестр</span>
              <span className="bench-chip__val mono">
                {formatMln(dkp.median_ppm2 * dkpArea)} ₽
              </span>
              <span className="bench-chip__sub mono">
                {dkp.median_ppm2.toLocaleString("ru-RU")} ₽/м² · {dkp.count} сд.
              </span>
            </div>
          )}
        </div>
      )}

      {/* ── Доверие + действия (web-преимущества над PDF): свежесть данных,
          collapsible «Как рассчитано», PDF-отчёт, CTA трейд-ин. Headline выше
          остаётся чистым — прозрачность вторична/collapsible. ── */}
      <HeroTransparency
        estimate={estimate}
        brandSlug={activeBrandSlug}
        brandName={brand?.name ?? null}
      />

      {/* ── Два сопоставимых ценовых ориентира рынка (РАЗНЫЕ источники) ──
          asking (объявления) vs реальные сделки ДКП. Оба — рыночный контекст
          под headline-итогом, поэтому одинакового веса.
          При insufficient_data (median=0) оба блока скрыты — иначе «0,00 млн ₽» (#748). */}
      {!insufficientData && (
        <>
      <div className={`hero-prices${showDkp ? "" : " single"}`}>
        <div className="price-figure" data-kind="asking">
          <div className="price-figure-label">Рекомендованная цена в объявлении</div>
          <div className="price-figure-value mono">{formatMln(m)} ₽</div>
          <div className="price-figure-range mono">
            {formatMln(lo)} – {formatMln(hi)} ₽
          </div>
          <div className="price-figure-perm2 mono">
            {estimate.median_price_per_m2.toLocaleString("ru-RU")} ₽/м² · по объявлениям
          </div>
          {imvPrice !== null && (
            <div
              className="price-figure-imv mono"
              title="Индекс Market Value Avito по этому дому — реальный рыночный якорь"
              style={{ marginTop: 6, fontSize: 12, color: "var(--muted)" }}
            >
              Avito <abbr title="Information Market Value — оценка рыночной стоимости от Avito">IMV</abbr>: {formatMln(imvPrice)} ₽
            </div>
          )}
        </div>

        {showDkp && dkp && (
          <div className="price-figure" data-kind="dkp">
            <div className="price-figure-label">
              Цены по фактическим сделкам (<abbr title="Договор купли-продажи (реальные сделки Росреестра)">ДКП</abbr> · Росреестр)
            </div>
            <div className="price-figure-value mono">
              {formatMln(dkp.median_ppm2 * dkpArea)} ₽
            </div>
            <div className="price-figure-range mono">
              {formatMln(dkp.low_ppm2 * dkpArea)} – {formatMln(dkp.high_ppm2 * dkpArea)} ₽
            </div>
            <div className="price-figure-perm2 mono">
              {dkp.median_ppm2.toLocaleString("ru-RU")} ₽/м² · {dkp.count} <abbr title="Договор купли-продажи (реальные сделки Росреестра)">ДКП</abbr> за{" "}
              {dkp.period_months} мес
            </div>
          </div>
        )}
      </div>

      <div className="hero-bars">
        <div className="bar-block">
          <div className="bar-head">
            <span className="bar-title">
              Диапазон цен в объявлениях{" "}
              <span style={{ color: "var(--muted-2)" }}>(без учёта ремонта)</span>
            </span>
            <span className="bar-cv">медиана · {formatMln(m)} ₽</span>
          </div>
          <div className="pricebar">
            <div className="axis">
              <div className="range" style={{ left: "5%", right: "5%" }} />
              <div className="median" style={{ left: `${medianPct}%` }} />
            </div>
            <div className="endpoint" style={{ left: "5%" }}>
              <div className="endpoint-val">{formatMln(lo)}</div>
              <div className="endpoint-tick" />
            </div>
            <div className="endpoint" style={{ left: "95%" }}>
              <div className="endpoint-val">{formatMln(hi)}</div>
              <div className="endpoint-tick" />
            </div>
            <div className="median-label" style={{ left: `${medianPct}%`, top: -2 }}>
              {formatMln(m)} ₽
            </div>
            <div className="exp left">{estimate.median_price_per_m2.toLocaleString("ru-RU")} ₽/м²</div>
            <div className="exp right">{estimate.n_analogs} аналог.</div>
          </div>
        </div>

        {estimate.actual_deals.length > 0 && (() => {
          const dealsPrices = estimate.actual_deals.map((d) => d.price_rub);
          const dLo = Math.min(...dealsPrices);
          const dHi = Math.max(...dealsPrices);
          const dM = dealsPrices.sort((a, b) => a - b)[Math.floor(dealsPrices.length / 2)];
          const dSpan = dHi - dLo;
          const dMedPct = dSpan > 0 ? Math.max(5, Math.min(95, ((dM - dLo) / dSpan) * 100)) : 50;
          return (
            <div className="bar-block">
              <div className="bar-head">
                <span className="bar-title">Диапазон цен по фактическим сделкам</span>
                <span className="bar-cv">медиана · {formatMln(dM)} ₽</span>
              </div>
              <div className="pricebar">
                <div className="axis">
                  <div
                    className="range"
                    style={{
                      left: "5%",
                      right: "5%",
                      background: "linear-gradient(90deg,var(--viz-3),var(--viz-4))",
                    }}
                  />
                  <div className="median" style={{ left: `${dMedPct}%` }} />
                </div>
                <div className="endpoint" style={{ left: "5%" }}>
                  <div className="endpoint-val">{formatMln(dLo)}</div>
                  <div className="endpoint-tick" />
                </div>
                <div className="endpoint" style={{ left: "95%" }}>
                  <div className="endpoint-val">{formatMln(dHi)}</div>
                  <div className="endpoint-tick" />
                </div>
                <div className="median-label" style={{ left: `${dMedPct}%`, top: -2 }}>
                  {formatMln(dM)} ₽
                </div>
                <div className="exp left">{estimate.actual_deals.length} сделок</div>
                <div className="exp right">Росреестр · ДомКлик</div>
              </div>
            </div>
          );
        })()}

        {/* ── Задача 3: Бар «Ожидаемая цена сделки» (expected_sold) ──
            Показывается только если поле присутствует и > 0. Graceful при null.
            Визуально отличается: акцентный градиент (--viz-5/--viz-6) и пометка. */}
        {hasSold && (() => {
          // Строим бар либо из диапазона (low/high), либо из точечного значения ±0 span.
          const esLo =
            typeof soldLo === "number" && soldLo > 0 ? soldLo : (sold as number);
          const esHi =
            typeof soldHi === "number" && soldHi > 0 ? soldHi : (sold as number);
          const esM = sold as number;
          const esSpan = esHi - esLo;
          const esMedPct =
            esSpan > 0
              ? Math.max(5, Math.min(95, ((esM - esLo) / esSpan) * 100))
              : 50;
          return (
            <div className="bar-block">
              <div className="bar-head">
                <span className="bar-title">
                  Ожидаемая цена сделки{" "}
                  <span style={{ color: "var(--muted-2)" }}>(прогноз)</span>
                </span>
                <span className="bar-cv">медиана · {formatMln(esM)} ₽</span>
              </div>
              <div className="pricebar">
                <div className="axis">
                  <div
                    className="range"
                    style={{
                      left: "5%",
                      right: "5%",
                      background:
                        "linear-gradient(90deg,var(--viz-5,#a78bfa),var(--viz-6,#7c3aed))",
                    }}
                  />
                  <div className="median" style={{ left: `${esMedPct}%` }} />
                </div>
                <div className="endpoint" style={{ left: "5%" }}>
                  <div className="endpoint-val">{formatMln(esLo)}</div>
                  <div className="endpoint-tick" />
                </div>
                <div className="endpoint" style={{ left: "95%" }}>
                  <div className="endpoint-val">{formatMln(esHi)}</div>
                  <div className="endpoint-tick" />
                </div>
                <div className="median-label" style={{ left: `${esMedPct}%`, top: -2 }}>
                  {formatMln(esM)} ₽
                </div>
                {typeof soldPerM2 === "number" && (
                  <div className="exp left">
                    {soldPerM2.toLocaleString("ru-RU")} ₽/м²
                  </div>
                )}
                {showDiscount && (
                  <div className="exp right" style={{ color: "var(--danger)" }}>
                    −{discountPct}% к объявлению
                  </div>
                )}
              </div>
            </div>
          );
        })()}
      </div>
        </>
      )}

      {/* ── Источники данных (#648 IA, task #5) — какие источники участвовали ──
          #2211 находка 2: пустой sources_used → честный текст, а НЕ фейковые чипы
          avito/cian/yandex/rosreestr, которых в оценке не было. Чипы строятся
          только из фактического sources_used (лейблы/цвета — из source-registry). */}
      <div className="filters-row" style={{ padding: "14px 22px 0" }}>
        <span className="lbl">Источники</span>
        <div className="sources">
          {estimate.sources_used.length > 0 ? (
            estimate.sources_used.map((src) => (
              <span key={src} className="source-chip">
                <span className={`src-dot ${sourceDot(src)}`} /> {sourceLabel(src)}
              </span>
            ))
          ) : (
            <span style={{ color: "var(--muted-2)" }}>источники не зафиксированы</span>
          )}
        </div>
      </div>

      <div className="hero-warnings">
        {estimate.confidence_explanation && (
          <div className="warn-row">
            <span className="ic">
              <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">
                <circle cx="12" cy="12" r="10" />
                <line x1="12" y1="8" x2="12" y2="12" />
                <line x1="12" y1="16" x2="12.01" y2="16" />
              </svg>
            </span>
            <div>{estimate.confidence_explanation}</div>
          </div>
        )}
        {/* 3 статичных explainer-карточки (#648 IA, всегда показываем). */}
        <div className="warn-row">
          <span className="ic">
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">
              <circle cx="12" cy="12" r="10" />
              <line x1="12" y1="8" x2="12" y2="12" />
              <line x1="12" y1="16" x2="12.01" y2="16" />
            </svg>
          </span>
          <div>
            <b>Цены в объявлениях — это ожидания собственников.</b> Реальные сделки
            проходят на <b>5–12% ниже</b> по данным <abbr title="Договор купли-продажи (реальные сделки Росреестра)">ДКП</abbr> Росреестра.
          </div>
        </div>
        <div className="warn-row">
          <span className="ic">
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">
              <circle cx="12" cy="12" r="10" />
              <line x1="12" y1="8" x2="12" y2="12" />
              <line x1="12" y1="16" x2="12.01" y2="16" />
            </svg>
          </span>
          <div>
            <b>Ремонт оценивается по состоянию, а не по вложенным суммам.</b>{" "}
            Рынок платит за итоговый вид квартиры, а не за чеки на материалы.
          </div>
        </div>
        <div className="warn-row">
          <span className="ic">
            <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round">
              <circle cx="12" cy="12" r="10" />
              <line x1="12" y1="8" x2="12" y2="12" />
              <line x1="12" y1="16" x2="12.01" y2="16" />
            </svg>
          </span>
          <div>
            <b>Скрытые расходы при самостоятельной продаже — до 15%</b> на торге,
            риелторе, нотариусе и аренде жилья на время сделки.
          </div>
        </div>
      </div>

      {showEnrichment && (
        <div
          style={{
            margin: "16px 16px 0",
            padding: 16,
            border: "1px solid var(--border, #e5e7eb)",
            background: "var(--surface-2, #f8fafc)",
            borderRadius: "var(--radius, 8px)",
            display: "flex",
            flexDirection: "column",
            gap: 12,
          }}
        >
          <p style={{ fontSize: 13, fontWeight: 500, margin: 0 }}>
            Уточните для повышения точности оценки:
          </p>
          <form onSubmit={handleEnrichSubmit}>
            <div style={{ display: "flex", flexWrap: "wrap", gap: 10, marginBottom: 12 }}>
              {needsHouseType && (
                <select
                  className="control"
                  value={enrichHouseType}
                  onChange={(e) => setEnrichHouseType(e.target.value)}
                  aria-label="Тип дома"
                  style={{ flex: "1 1 160px", minWidth: 140 }}
                >
                  <option value="">Тип дома…</option>
                  <option value="panel">Панельный</option>
                  <option value="brick">Кирпичный</option>
                  <option value="monolith">Монолит</option>
                  <option value="monolith_brick">Монолит-кирпич</option>
                  <option value="other">Другое</option>
                </select>
              )}
              {needsRepairState && (
                <select
                  className="control"
                  value={enrichRepairState}
                  onChange={(e) => setEnrichRepairState(e.target.value)}
                  aria-label="Состояние ремонта"
                  style={{ flex: "1 1 160px", minWidth: 140 }}
                >
                  <option value="">Состояние…</option>
                  <option value="needs_repair">Требует ремонта</option>
                  <option value="standard">Стандартный</option>
                  <option value="good">Хороший</option>
                  <option value="excellent">Евроремонт</option>
                </select>
              )}
            </div>
            <button
              type="submit"
              className="btn btn-primary"
              disabled={isResubmitting || (!enrichHouseType && !enrichRepairState)}
              style={{ opacity: isResubmitting || (!enrichHouseType && !enrichRepairState) ? 0.55 : 1 }}
            >
              {isResubmitting ? "Пересчитываем…" : "Пересчитать"}
            </button>
          </form>
        </div>
      )}
    </article>
  );
}
