"use client";

/**
 * SectionAlternatives — «Как участок сходится» (#2181).
 *
 * Компактный автоматический ранжир вариантов программы застройки
 * (класс × этажность) по NPV. Backend кладёт `program_alternatives` в ответ
 * /analyze ТОЛЬКО при отрицательном базовом фин-вердикте (program_optimizer.py);
 * на позитивных участках поле null/отсутствует и блок НЕ рендерится вовсе.
 *
 * Это НЕ дубль §7 «Концепция»: там ручная интерактивная концепция с картой/3D,
 * здесь — статичный топ-3 без геометрии, отвечающий на вопрос «а вообще эта
 * земля сходится при какой-нибудь программе?». Оценка по МАКСИМАЛЬНОЙ застройке
 * зоны (синтез ТЭП из предельных параметров), НЕ инвест-рекомендация — тон
 * advisory, честный caveat из ответа показываем полным предложением.
 */

import { HeadlineBar } from "@/components/ui/HeadlineBar";
import { Badge } from "@/components/ui/Badge";
import {
  DEVELOPMENT_TYPE_LABELS,
  HOUSING_CLASS_LABELS,
  type DevelopmentType,
  type HousingClass,
} from "@/lib/concept-api";
import type {
  PendingConceptProgram,
  ProgramAlternatives,
  ProgramVariant,
} from "@/types/site-finder";

const SCROLL_MARGIN = 72;
const TOP_N = 3;

// ── Formatters (ru microcopy, Unicode-минус) ──────────────────────────────────

const MINUS = "−"; // U+2212 — типографский минус, не hyphen-minus.

const nf = new Intl.NumberFormat("ru-RU", { maximumFractionDigits: 0 });

/**
 * Деньги округлением до млн со знаком: «+42 млн ₽» / «−216 млн ₽». Ниже 1 млн —
 * точная сумма (редко, но честно). Знак «+» показываем только для положительных.
 */
function formatMoneySigned(rub: number): string {
  const abs = Math.abs(rub);
  const sign = rub > 0 ? "+" : rub < 0 ? MINUS : "";
  if (abs >= 1e9) return `${sign}${(abs / 1e9).toFixed(1)} млрд ₽`;
  if (abs >= 1e6) return `${sign}${Math.round(abs / 1e6)} млн ₽`;
  return `${sign}${nf.format(Math.round(abs))} ₽`;
}

/** ₽/м² с пробелами-разрядами: «142 500 ₽/м²». */
function formatPricePerSqm(rub: number): string {
  return `${nf.format(Math.round(rub))} ₽/м²`;
}

/** Проценты доли: 0.184 → «18.4%». Отрицательные — с Unicode-минусом. */
function formatPct(fraction: number): string {
  const v = (fraction * 100).toFixed(1);
  return v.startsWith("-") ? `${MINUS}${v.slice(1)}%` : `${v}%`;
}

function formatInt(n: number): string {
  return nf.format(Math.round(n));
}

// ── Narrowing (backend отдаёт класс/тип/источник строкой, extra=allow) ─────────

const HOUSING_CLASSES: readonly HousingClass[] = [
  "econom",
  "comfort",
  "business",
];
const DEVELOPMENT_TYPES: readonly DevelopmentType[] = [
  "spot",
  "mid_rise",
  "high_rise",
];
function housingClassLabel(value: string): string {
  return (HOUSING_CLASSES as readonly string[]).includes(value)
    ? HOUSING_CLASS_LABELS[value as HousingClass]
    : value;
}

function developmentTypeLabel(value: string): string {
  return (DEVELOPMENT_TYPES as readonly string[]).includes(value)
    ? DEVELOPMENT_TYPE_LABELS[value as DevelopmentType]
    : value;
}

/**
 * Честная подпись источника цены (тот же набор, что priceSourceCaption в
 * concept-api + objective_geo_radius из parcel_financial). Вариант несёт
 * price_source строкой, а не FinancialModel-объект, поэтому берём каноничные
 * фразы по значению — норматив класса НИКОГДА не выдаётся за рыночную цену,
 * и наоборот: рыночный источник не должен падать в норматив-фразу (инверсия
 * честности; неизвестный источник показываем как есть, не как норматив).
 */
function priceSourceLabel(source: string): string {
  switch (source) {
    case "objective_district_median":
      return "рынок: медиана объявлений Objective по району";
    case "objective_geo_radius":
      return "рынок: медиана новостроек в радиусе 3 км";
    case "district_reference":
      return "рынок: справочная медиана района";
    case "class_norm":
      return "норматив класса (нет рыночных данных по участку)";
    default:
      return source;
  }
}

// ── Props ─────────────────────────────────────────────────────────────────────

interface Props {
  /** program_alternatives из ответа /analyze (null/пусто → блок не рендерится). */
  alternatives?: ProgramAlternatives | null;
  /**
   * «Пересчитать §7 с этой программой» (план #2181, хвост). Если задан — на
   * каждой карточке появляется secondary-CTA, по клику передающая класс/этажность/
   * тип застройки варианта в §7 «Концепция» (родитель ставит pendingProgram +
   * скроллит к #section-7). Не задан → кнопки нет (блок остаётся статичным топ-3).
   */
  onApplyToConcept?: (program: PendingConceptProgram) => void;
  /** §7 сейчас считает — CTA дизейблится (одна раскладка за раз). */
  conceptPending?: boolean;
}

// ── Variant card ──────────────────────────────────────────────────────────────

function VariantCard({
  variant,
  isBest,
  onApply,
  applyDisabled,
}: {
  variant: ProgramVariant;
  isBest: boolean;
  /** Задан → рендерим CTA «Пересчитать §7 с этой программой». */
  onApply?: (program: PendingConceptProgram) => void;
  applyDisabled: boolean;
}) {
  const npvPositive = variant.npv_rub > 0;
  const npvColor = npvPositive
    ? "var(--success)"
    : variant.npv_rub < 0
      ? "var(--danger)"
      : "var(--fg-primary)";

  const title = `${housingClassLabel(variant.housing_class)} · ${variant.floors} эт · ${developmentTypeLabel(variant.development_type)}`;

  return (
    <div
      style={{
        background: "var(--bg-card)",
        border: "1px solid var(--border-card)",
        borderRadius: 12,
        padding: "16px 18px",
        display: "flex",
        flexDirection: "column",
        gap: 10,
      }}
    >
      {/* Титул + бейдж «лучший» */}
      <div
        style={{
          display: "flex",
          alignItems: "flex-start",
          justifyContent: "space-between",
          gap: 8,
        }}
      >
        <div
          style={{
            fontSize: 14,
            fontWeight: 600,
            color: "var(--fg-primary)",
            lineHeight: 1.35,
          }}
        >
          {title}
        </div>
        {isBest && (
          <Badge variant={npvPositive ? "success" : "neutral"} size="sm">
            лучший
          </Badge>
        )}
      </div>

      {/* NPV — крупно */}
      <div>
        <div
          style={{
            fontSize: 12,
            textTransform: "uppercase",
            letterSpacing: "0.04em",
            color: "var(--fg-secondary)",
          }}
        >
          NPV
        </div>
        <div
          style={{
            marginTop: 2,
            fontSize: 28,
            fontWeight: 600,
            color: npvColor,
            fontVariantNumeric: "tabular-nums",
          }}
        >
          {formatMoneySigned(variant.npv_rub)}
        </div>
      </div>

      {/* ROI / IRR строкой */}
      <div style={{ fontSize: 13, color: "var(--fg-secondary)" }}>
        ROI {formatPct(variant.roi)} · IRR {formatPct(variant.irr)}
      </div>

      {/* Жилая S · квартиры */}
      <div style={{ fontSize: 13, color: "var(--fg-secondary)" }}>
        Жилая {formatInt(variant.residential_sqm)} м² ·{" "}
        {formatInt(variant.apartments_count)} квартир
      </div>

      {/* Цена м² + источник */}
      <div style={{ fontSize: 12, color: "var(--fg-tertiary)" }}>
        Цена {formatPricePerSqm(variant.price_per_sqm_used)} (
        {priceSourceLabel(variant.price_source)})
      </div>

      {/* CTA «Пересчитать §7» — secondary orange (--accent-2), не primary: одна
          primary на экран (в §7 — «Рассчитать концепцию»). Передаём только
          класс/этажность/тип; §7 добирает тип секции и пятно из каталога. */}
      {onApply && (
        <button
          type="button"
          disabled={applyDisabled}
          aria-label="Пересчитать раздел 7 «Концепция» с программой этого варианта"
          onClick={() =>
            onApply({
              housing_class: variant.housing_class,
              floors: variant.floors,
              development_type: variant.development_type,
            })
          }
          style={{
            marginTop: 4,
            alignSelf: "flex-start",
            display: "inline-flex",
            alignItems: "center",
            gap: 6,
            padding: "6px 12px",
            height: 32,
            background: applyDisabled
              ? "var(--bg-card-alt)"
              : "var(--accent-2)",
            color: applyDisabled ? "var(--fg-tertiary)" : "#fff",
            border: applyDisabled ? "1px solid var(--border-card)" : "none",
            borderRadius: 6,
            fontSize: 13,
            fontWeight: 500,
            cursor: applyDisabled ? "not-allowed" : "pointer",
          }}
        >
          {applyDisabled ? "§7 считает…" : "Пересчитать §7 с этой программой"}
        </button>
      )}
    </div>
  );
}

// ── SectionAlternatives ───────────────────────────────────────────────────────

export function SectionAlternatives({
  alternatives,
  onApplyToConcept,
  conceptPending = false,
}: Props) {
  // Рендер-guard: нет поля / пустой список → блок не рендерится вовсе.
  const variants = alternatives?.variants ?? [];
  if (!alternatives || variants.length === 0) return null;

  // Клик по CTA: сначала ставим pendingProgram в родителе (это триггерит §7),
  // затем плавно скроллим к §7, чтобы пользователь увидел пересчёт. Скролл здесь
  // (не в родителе) — секция §7 всегда в DOM, якорь #section-7 гарантированно есть.
  const handleApply = onApplyToConcept
    ? (program: PendingConceptProgram) => {
        onApplyToConcept(program);
        if (typeof document !== "undefined") {
          document
            .getElementById("section-7")
            ?.scrollIntoView({ behavior: "smooth", block: "start" });
        }
      }
    : undefined;

  const topVariants = variants.slice(0, TOP_N);
  const { any_viable, grid_size, caveat } = alternatives;

  const subtitle = any_viable
    ? "Базовая оценка отрицательна, но участок сходится при другой программе — топ вариантов по NPV:"
    : `При текущих вводных ни один из ${grid_size} вариантов программы (класс × этажность) не выходит в плюс. Ниже — наименее убыточные:`;

  // «Лучший» = вариант с максимальным NPV. Список уже отсортирован по NPV убыв.
  // на backend, но берём индекс явно (устойчиво к любому порядку прихода).
  let bestIndex = 0;
  for (let i = 1; i < topVariants.length; i++) {
    if (topVariants[i].npv_rub > topVariants[bestIndex].npv_rub) bestIndex = i;
  }

  // Honest-caveat одним предложением: caveat ответа + допущение по графику, если
  // хоть один показанный вариант считался по типовому графику строительства.
  const scheduleIsDefault = topVariants.some((v) => v.schedule_is_default);
  const caveatText = scheduleIsDefault
    ? `${caveat} График строительства — типовое допущение, не график проекта.`
    : caveat;

  return (
    <section
      id="section-alternatives"
      style={{ scrollMarginTop: SCROLL_MARGIN }}
    >
      <HeadlineBar title="Как участок сходится" subtitle={subtitle} />

      {/* Карточки топ-3 — auto-fit грид как KPI-ряды. */}
      <div
        style={{
          display: "grid",
          gridTemplateColumns: "repeat(auto-fit, minmax(220px, 1fr))",
          gap: 12,
          marginTop: 12,
        }}
      >
        {topVariants.map((variant, i) => (
          <VariantCard
            key={`${variant.housing_class}-${variant.floors}-${variant.development_type}`}
            variant={variant}
            isBest={i === bestIndex}
            onApply={handleApply}
            applyDisabled={conceptPending}
          />
        ))}
      </div>

      {/* Один честный caveat — полным предложением, --fg-tertiary. */}
      <p
        style={{
          margin: "12px 0 0",
          fontSize: 12,
          lineHeight: "18px",
          color: "var(--fg-tertiary)",
        }}
      >
        {caveatText}
      </p>
    </section>
  );
}
