/**
 * ptica-adapt honesty tests — audit-issue #1871 P1.3 «ptica-adapt помечает
 * заглушки isReal:true».
 *
 * The adapter is the single source of truth for the real-vs-placeholder
 * decision; a stub must NEVER carry `isReal:true`, or the cockpit presents a
 * fabricated value as a live measurement. These three cases pin the fixed
 * spots:
 *   1. adaptPassport → encumbrances is a stub (never from backend yet).
 *   2. buySignalFromForecast at deficit_index=null → «прогноза нет», not a
 *      real «наблюдать».
 *   3. adaptBuySignalGauge with a report whose deficit_index=null → the gauge
 *      is not real (no synthetic mid-scale 50 dressed up as live).
 */
import { describe, expect, it } from "vitest";

import {
  adaptBuySignalGauge,
  adaptEconomyCard,
  adaptFinanceDrawer,
  adaptInvestmentClearance,
  adaptLegalDrawer,
  adaptPassport,
  adaptPassportDrawer,
  adaptSiteVerdict,
  buySignalFromForecast,
} from "@/components/site-finder/ptica/ptica-adapt";
import type {
  ParcelAnalysis,
  ParcelFinancialEstimate,
} from "@/types/site-finder";
import type { ForecastReport } from "@/types/forecast";

// Tracked mock fixtures (NEXT_PUBLIC_USE_MOCKS source). The dev-only
// __preview/ptica/__fixtures__ dir is git-excluded, so it is absent in CI —
// these committed mocks keep the suite reproducible on the Linux runner.
import forecastFixture from "@/lib/mocks/parcel-forecast.json";
import analyzeFixture from "@/lib/mocks/parcel-analyze.json";

const analysis = analyzeFixture as unknown as ParcelAnalysis;
const baseReport = (forecastFixture as { report: unknown })
  .report as ForecastReport;

/** A report identical to the fixture but with no deficit_index (thin §22). */
function reportWithoutDeficitIndex(): ForecastReport {
  return {
    ...baseReport,
    exec_summary: {
      ...baseReport.exec_summary,
      key_numbers: {
        ...baseReport.exec_summary.key_numbers,
        deficit_index: null,
      },
    },
  };
}

describe("ptica-adapt honesty (#1871 P1.3)", () => {
  it("adaptPassport: encumbrances is a stub, never real", () => {
    const passport = adaptPassport(analysis);
    expect(passport.encumbrances.isReal).toBe(false);
    expect(passport.encumbrances.caption).toBeTruthy();
  });

  it("buySignalFromForecast: deficit_index=null is not a real signal", () => {
    const field = buySignalFromForecast(reportWithoutDeficitIndex());
    expect(field.isReal).toBe(false);
    expect(field.caption).toBeTruthy();
  });

  it("buySignalFromForecast: a real deficit_index yields a real signal", () => {
    const field = buySignalFromForecast({
      ...baseReport,
      exec_summary: {
        ...baseReport.exec_summary,
        key_numbers: {
          ...baseReport.exec_summary.key_numbers,
          deficit_index: 0.2,
        },
      },
    });
    expect(field.isReal).toBe(true);
  });

  it("adaptBuySignalGauge: report with deficit_index=null → gauge not real", () => {
    const card = adaptBuySignalGauge(reportWithoutDeficitIndex());
    expect(card.gauge.isReal).toBe(false);
    expect(card.gauge.value).toBeNull();
    expect(card.available).toBe(false);
  });

  it("drawer «Обременения» rows are stubs, never real (#1871 P1.3)", () => {
    // The passport-card encumbrances fix must also cover its two drawer mirrors
    // (adaptPassportDrawer + adaptLegalDrawer), else the drill-down presents the
    // em-dash stub as a live measurement.
    const findEncumbrance = (
      rows: { k: string; field: { isReal: boolean } }[],
    ) => rows.find((r) => r.k === "Обременения");

    const passportRow = findEncumbrance(adaptPassportDrawer(analysis).registry);
    expect(passportRow?.field.isReal).toBe(false);

    const legalRow = findEncumbrance(adaptLegalDrawer(analysis).registry);
    expect(legalRow?.field.isReal).toBe(false);
  });
});

// ── #1881 PR-4 — финмодель в Investment Clearance ──────────────────────────────

function financialFixture(): ParcelFinancialEstimate {
  return {
    revenue_rub: 4_200_000_000,
    cost_rub: 3_100_000_000,
    net_profit_rub: 1_100_000_000,
    roi: 0.355,
    margin_pct: 0.262,
    irr: 0.214,
    irr_is_proxy: false,
    // Нежилое (коммерция/офисы 1-го этажа) — additive выручка к GDV (#1881).
    revenue_office_rub: 180_000_000,
    npv_rub: 640_000_000,
    payback_months: 42,
    discount_rate_used: 0.18,
    price_per_sqm_used: 145_000,
    price_is_calibrated: true,
    price_source: "objective",
    housing_class_inferred: "comfort",
    development_type_inferred: "high_rise",
    schedule_is_default: true,
    sales_duration_months: 30,
    teap_synth: {
      residential_area_sqm: 28_000,
      total_floor_area_sqm: 34_000,
      parking_spaces: 280,
      apartments_count: 520,
    },
    caveat: "Расчёт по максимальной застройке НСПД, а не реальной концепции.",
    // #1881 PR-5 — финансирование (кредит).
    financing_enabled: true,
    annual_rate_used: 0.18,
    peak_debt_rub: 1_800_000_000,
    total_interest_rub: 320_000_000,
    net_profit_after_financing_rub: 780_000_000,
    financing_is_simplified: true,
    // Levered (equity) IRR — доходность на собственные средства при LTC 70 %.
    levered_irr: 0.284,
    levered_irr_is_proxy: false,
  };
}

describe("ptica-adapt finance (#1881 PR-4)", () => {
  it("adaptInvestmentClearance: null → all placeholders, never real", () => {
    const c = adaptInvestmentClearance(null);
    expect(c.bignums.every((b) => b.isReal === false)).toBe(true);
    expect(c.bignums.every((b) => b.value === "—")).toBe(true);
    expect(c.caption).toContain("финмодели");
  });

  it("adaptInvestmentClearance: estimate → real bignums + NPV + payback", () => {
    const c = adaptInvestmentClearance(financialFixture());
    expect(c.bignums.every((b) => b.isReal === true)).toBe(true);
    const labels = c.bignums.map((b) => b.label);
    expect(labels).toContain("NPV");
    expect(labels).toContain("Срок окуп.");
    // GDV 4.2 млрд ₽ formatted ru (запятая-разделитель дробной части).
    const gdv = c.bignums.find((b) => b.label === "GDV · выручка");
    expect(gdv?.value).toBe("4,20");
    // ROI 35,5 %.
    const roi = c.bignums.find((b) => b.label === "ROI");
    expect(roi?.value).toBe("35,5 %");
  });

  it("adaptInvestmentClearance: payback_months=null → «не окуп.»", () => {
    const c = adaptInvestmentClearance({
      ...financialFixture(),
      payback_months: null,
    });
    const payback = c.bignums.find((b) => b.label === "Срок окуп.");
    expect(payback?.value).toBe("не окуп.");
  });

  it("adaptInvestmentClearance: irr_is_proxy flips the IRR caption", () => {
    const proxy = adaptInvestmentClearance({
      ...financialFixture(),
      irr_is_proxy: true,
    });
    const irr = proxy.bignums.find((b) => b.label === "IRR");
    expect(irr?.caption).toContain("proxy");
  });

  it("adaptFinanceDrawer: null → placeholder rows + §22 summary", () => {
    const d = adaptFinanceDrawer(null);
    expect(d.rows.every((r) => r.field.isReal === false)).toBe(true);
    expect(d.summary).toContain("§22");
  });

  it("adaptFinanceDrawer: estimate → real rows + caveat summary", () => {
    const fin = financialFixture();
    const d = adaptFinanceDrawer(fin);
    expect(d.rows.every((r) => r.field.isReal === true)).toBe(true);
    const keys = d.rows.map((r) => r.k);
    expect(keys).toContain("IRR");
    expect(keys).toContain("ТЭП · квартир");
  });

  // #1881 PR-5 — финансирование (кредит)
  it("adaptFinanceDrawer: financing_enabled → debt/interest/net-after rows + caption", () => {
    const fin = financialFixture(); // financing_enabled: true
    const d = adaptFinanceDrawer(fin);
    const keys = d.rows.map((r) => r.k);
    expect(keys).toContain("Пиковый долг");
    expect(keys).toContain("Проценты по кредиту");
    expect(keys).toContain("Чистая прибыль после финанс.");
    const interest = d.rows.find((r) => r.k === "Проценты по кредиту");
    expect(interest?.field.isReal).toBe(true);
    expect(interest?.field.caption).toContain("18");
    expect(interest?.field.caption).toContain("годовых");
  });

  it("adaptFinanceDrawer: financing_is_simplified → кредитная/собственные caveat appended to summary", () => {
    const d = adaptFinanceDrawer(financialFixture());
    expect(d.summary).toContain("кредитная линия");
    expect(d.summary).toContain("собственные средства");
    expect(d.summary).toContain("эскроу");
  });

  it("adaptFinanceDrawer: financing_enabled=false → no financing rows", () => {
    const d = adaptFinanceDrawer({
      ...financialFixture(),
      financing_enabled: false,
    });
    const keys = d.rows.map((r) => r.k);
    expect(keys).not.toContain("Пиковый долг");
    expect(keys).not.toContain("Проценты по кредиту");
    expect(keys).not.toContain("Чистая прибыль после финанс.");
    // Levered (equity) IRR is a financing-only row → absent when no financing.
    expect(keys).not.toContain("IRR на собственные средства");
  });

  // Levered (equity) IRR — доходность на собственные средства при LTC 70 %.
  it("adaptFinanceDrawer: financing_enabled + DCF levered_irr → real row with LTC caption", () => {
    const d = adaptFinanceDrawer(financialFixture()); // levered_irr_is_proxy: false
    const row = d.rows.find((r) => r.k === "IRR на собственные средства");
    expect(row?.field.isReal).toBe(true);
    // 0.284 → «28,4 %» (ru запятая-разделитель).
    expect(row?.field.value).toBe("28,4 %");
    expect(row?.field.caption).toContain("LTC 70");
    expect(row?.field.caption).toContain("проектной IRR");
  });

  it("adaptFinanceDrawer: levered_irr_is_proxy → row not real + «оценка (proxy)» caption", () => {
    const d = adaptFinanceDrawer({
      ...financialFixture(),
      levered_irr_is_proxy: true,
    });
    const row = d.rows.find((r) => r.k === "IRR на собственные средства");
    // A proxy levered IRR must never read as a live DCF measurement.
    expect(row?.field.isReal).toBe(false);
    expect(row?.field.caption).toContain("proxy");
  });

  it("adaptFinanceDrawer: negative levered_irr → Unicode minus, never dressed positive", () => {
    // Negative leverage (project return < cost of debt) is correct finance — the
    // value is shown honestly with «−», not hidden or flipped to positive.
    const d = adaptFinanceDrawer({
      ...financialFixture(),
      levered_irr: -0.116,
    });
    const row = d.rows.find((r) => r.k === "IRR на собственные средства");
    expect(row?.field.value).toBe("−11,6 %");
    expect(row?.field.value.startsWith("−")).toBe(true);
  });

  it("adaptFinanceDrawer: sales_duration_months shown with schedule source", () => {
    const d = adaptFinanceDrawer(financialFixture()); // schedule_is_default: true
    const row = d.rows.find((r) => r.k === "Горизонт продаж");
    expect(row?.field.isReal).toBe(true);
    expect(row?.field.value).toContain("30");
    expect(row?.field.caption).toContain("дефолт");

    const d2 = adaptFinanceDrawer({
      ...financialFixture(),
      schedule_is_default: false,
    });
    const row2 = d2.rows.find((r) => r.k === "Горизонт продаж");
    expect(row2?.field.caption).toContain("поглощения");
  });

  it("adaptInvestmentClearance: financing_enabled → «После финанс.» + relabeled profit", () => {
    const c = adaptInvestmentClearance(financialFixture()); // financing_enabled: true
    const labels = c.bignums.map((b) => b.label);
    expect(labels).toContain("После финанс.");
    expect(labels).toContain("Profit · до финанс.");
    const after = c.bignums.find((b) => b.label === "После финанс.");
    expect(after?.isReal).toBe(true);
    expect(after?.caption).toContain("кредит");
  });

  it("adaptEconomyCard: no financial → all placeholders + badge предв.", () => {
    const card = adaptEconomyCard(null);
    expect(card.badge).toBe("предв.");
    expect(card.rows.every((r) => r.field.isReal === false)).toBe(true);
  });

  it("adaptEconomyCard: financial present → real rows + badge ориент.", () => {
    const card = adaptEconomyCard(financialFixture());
    expect(card.badge).toBe("ориент.");
    expect(card.rows.every((r) => r.field.isReal === true)).toBe(true);
    const rev = card.rows.find((r) => r.key === "Потенц. выручки");
    expect(rev?.field.value).toContain("млрд");
    expect(rev?.field.isReal).toBe(true);
  });
});

// ── #1881 PR-5 gate / site-verdict ─────────────────────────────────────────────

describe("ptica-adapt gate (#1892 ЗОУИТ area-gate)", () => {
  it("adaptSiteVerdict: verdict_label «С ограничениями» → word=«С ограничениями» tone=warn", () => {
    const a: ParcelAnalysis = {
      ...analyzeFixture,
      gate_verdict: {
        can_build_mkd: true,
        verdict_label: "С ограничениями",
        blockers: [],
        warnings: [
          {
            code: "ZOUIT_ENGINEERING_PARTIAL",
            detail: "Охранная зона ЛЭП перекрывает 12 % участка",
          },
        ],
        source: "nspd_dump",
      },
    } as unknown as ParcelAnalysis;
    const v = adaptSiteVerdict(a);
    expect(v.word).toBe("С ограничениями");
    expect(v.tone).toBe("warn");
  });

  it("adaptSiteVerdict: can_build_mkd=true without ограничения → Приобретать good", () => {
    const a: ParcelAnalysis = {
      ...analyzeFixture,
      gate_verdict: {
        can_build_mkd: true,
        verdict_label: "Можно",
        blockers: [],
        warnings: [],
        source: "nspd_dump",
      },
    } as unknown as ParcelAnalysis;
    const v = adaptSiteVerdict(a);
    expect(v.word).toBe("Приобретать");
    expect(v.tone).toBe("good");
  });
});
