import { Fragment } from "react";
import type { CSSProperties, ReactNode } from "react";

import { safeUrl } from "@/lib/safeUrl";
import type { AggregatedEstimate } from "@/types/trade-in";
import { tokens } from "./tokens";
import { adRows, dealRows, marketAds, marketDeals } from "./fixtures";
import type { AdRowData, DealRowData, SourcesData } from "./mappers";
import { SourcesMap } from "./SourcesMap";

// Overlay 05 — РЫНОК · АНАЛОГИ И СДЕЛКИ.
// Faithful markup port from "МЕРА Оценка.dc.html" (lines 492-537).
// Card A: объявления в продаже (3 KPI + filter chips + adRows table).
// Card B: фактические сделки (3 KPI + dealRows table).
// Markup is data-driven via the `data` prop; the design fixtures stay as the
// DEFAULT for unwired/storybook usage. (M11 below may hide an all-«—» column,
// so the storybook render can differ from the design HTML by a dead column.)
//
// M7 — the two "tables" are CSS grids of <span>s. They now carry real table
// semantics via ARIA roles (table / row / columnheader / cell) layered onto the
// existing grid so the visual layout is unchanged (the row <div> keeps display:
// grid; the link cell wraps its <a> in a display:contents role="cell" so the
// anchor stays the grid item AND keeps its link role).
// M11 — columns whose every cell is empty/«—» (e.g. ДИСТ. when no distances are
// known) are dropped from BOTH the header and the body (no dead column).

// One-off tints not present in the v2 palette (kept literal, faithful to design).
const KPI_BG = "rgba(238,244,250,.5)";
const BADGE_BG = "#eaf1f8";

// Per-column grid fractions live on the AD_COLS / DEAL_COLS `fr` fields below;
// the row's gridTemplateColumns is rebuilt from the VISIBLE columns so a hidden
// (all-dash) column also drops its track. Original full templates, for reference:
//   AD   = "1.9fr 1.3fr 1fr 1fr 1.1fr .6fr .9fr"
//   DEAL = "2fr 1.3fr 1.2fr 1fr 1.1fr 1fr"

const card: CSSProperties = {
  background: tokens.surface.w55,
  border: `1px solid ${tokens.line2}`,
  borderRadius: 8,
  overflow: "hidden",
};

const cardHeader: CSSProperties = {
  display: "flex",
  alignItems: "flex-start",
  justifyContent: "space-between",
  padding: "14px 18px",
  borderBottom: `1px solid ${tokens.lineSoft}`,
};

const eyebrow: CSSProperties = {
  fontSize: "8.5px",
  letterSpacing: "2px",
  color: tokens.muted2,
  marginBottom: 5,
};

const cardTitle: CSSProperties = {
  fontSize: "12px",
  fontWeight: 600,
  color: tokens.ink2,
};

const headerRight: CSSProperties = {
  textAlign: "right",
  fontSize: "9.5px",
  color: tokens.muted2,
  lineHeight: 1.6,
};

const kpiRow: CSSProperties = {
  display: "grid",
  gridTemplateColumns: "repeat(3,1fr)",
  borderBottom: `1px solid ${tokens.lineSoft}`,
  background: KPI_BG,
};

const kpiLabel: CSSProperties = {
  fontSize: "9px",
  letterSpacing: "1.5px",
  // C1 — key caption (labels the headline KPI value) → body2.
  color: tokens.body2,
};

const tableHead: CSSProperties = {
  gap: "10px",
  padding: "9px 18px",
  fontSize: "9px",
  letterSpacing: "1px",
  // C1 — column headers are primary labels → body2.
  color: tokens.body2,
  borderBottom: `1px solid ${tokens.lineSoft2}`,
};

const tableRow: CSSProperties = {
  gap: "10px",
  padding: "11px 18px",
  fontSize: "11px",
  borderBottom: `1px solid ${tokens.lineSoft3}`,
  alignItems: "center",
};

const metaSpan: CSSProperties = {
  color: tokens.muted2,
  fontFamily: tokens.font.mono,
  fontSize: "9.5px",
};

const badge: CSSProperties = {
  fontSize: "9px",
  background: BADGE_BG,
  border: `1px solid ${tokens.line2}`,
  borderRadius: 3,
  padding: "2px 8px",
  // body2 (not muted): 9px muted on badgeTint was ~3.3:1 < AA; body2 ≈ 6.3:1.
  color: tokens.body2,
};

const chip: CSSProperties = {
  fontSize: "10px",
  color: tokens.muted,
  border: `1px solid ${tokens.line2}`,
  borderRadius: 20,
  padding: "3px 10px",
};

const monoMuted: CSSProperties = {
  fontFamily: tokens.font.mono,
  color: tokens.muted,
};

const monoInk: CSSProperties = {
  fontFamily: tokens.font.mono,
  color: tokens.ink2,
};

const monoMuted2: CSSProperties = {
  fontFamily: tokens.font.mono,
  color: tokens.muted2,
};

// Shared "open original ↗" cell style (anchor + fixture-fallback span render
// identically; textDecoration:none keeps the anchor visually a button).
const linkBtn: CSSProperties = {
  fontSize: "9.5px",
  color: tokens.accent,
  border: `1px solid ${tokens.line2}`,
  borderRadius: 4,
  padding: "3px 8px",
  textAlign: "center",
  textDecoration: "none",
};

// ── Table column model (M7 semantics + M11 hide-all-dash) ───────────────────
// Each column owns its header cell, grid fraction, body-cell renderer and an
// `empty` test. A column is hidden when EVERY row's cell is empty/«—».

interface Col<R> {
  key: string;
  /** Grid fraction in gridTemplateColumns. */
  fr: string;
  /** Header cell (role="columnheader"); aria-label for a visually-empty header. */
  head: ReactNode;
  /** Body cell — returns the full grid-item element (carries role="cell"). */
  cell: (row: R, index: number) => ReactNode;
  /** True when this row contributes no value to the column (for hide-all-dash). */
  empty: (row: R) => boolean;
}

/** Empty / placeholder cell value (null, blank, or a bare em-dash). */
function isDash(v: unknown): boolean {
  return (
    v == null ||
    (typeof v === "string" && (v.trim() === "" || v.trim() === "—"))
  );
}

/** Drop any column whose every cell is empty/«—»; keep all columns if no rows. */
function visibleCols<R>(cols: Col<R>[], rows: R[]): Col<R>[] {
  if (rows.length === 0) return cols;
  return cols.filter((c) => rows.some((r) => !c.empty(r)));
}

const AD_COLS: Col<AdRowData>[] = [
  {
    key: "addr",
    fr: "1.9fr",
    head: <span role="columnheader">АДРЕС</span>,
    cell: (r) => (
      <span role="cell" style={{ color: tokens.body }}>
        {r.addr}
      </span>
    ),
    empty: (r) => isDash(r.addr),
  },
  {
    key: "meta",
    fr: "1.3fr",
    head: <span role="columnheader">ПАРАМЕТРЫ</span>,
    cell: (r) => (
      <span role="cell" style={metaSpan}>
        {r.meta}
      </span>
    ),
    empty: (r) => isDash(r.meta),
  },
  {
    key: "source",
    fr: "1fr",
    head: <span role="columnheader">ИСТОЧНИК</span>,
    cell: (r) => (
      <span role="cell">
        <span style={badge}>{r.source ?? "Avito"}</span>
      </span>
    ),
    empty: () => false,
  },
  {
    key: "ppm",
    fr: "1fr",
    head: <span role="columnheader">₽/М²</span>,
    cell: (r) => (
      <span role="cell" style={monoMuted}>
        {r.ppm}
        {r.outlier && (
          <span
            style={{
              display: "inline-block",
              marginLeft: 6,
              fontSize: "8px",
              fontFamily: tokens.font.sans,
              color: tokens.warn,
              border: `1px solid ${tokens.warn}`,
              borderRadius: 3,
              padding: "1px 4px",
              whiteSpace: "nowrap",
              verticalAlign: "middle",
            }}
            title="₽/м² сильно выше медианы выборки — исключено из расчёта «РАЗБРОС ЦЕН»"
          >
            возможный выброс
          </span>
        )}
      </span>
    ),
    empty: (r) => isDash(r.ppm),
  },
  {
    key: "price",
    fr: "1.1fr",
    // §M8 — унификация терминологии: везде «цена» (был «СТОИМОСТЬ»).
    head: <span role="columnheader">ЦЕНА</span>,
    cell: (r) => (
      <span role="cell" style={monoInk}>
        {r.price}
      </span>
    ),
    empty: (r) => isDash(r.price),
  },
  {
    key: "dist",
    fr: ".6fr",
    head: <span role="columnheader">ДИСТ.</span>,
    cell: (r) => (
      <span role="cell" style={monoMuted2}>
        {r.dist ?? "—"}
      </span>
    ),
    empty: (r) => isDash(r.dist),
  },
  {
    key: "link",
    fr: ".9fr",
    // M11 — visually-empty header in the design, labelled for screen readers.
    head: <span role="columnheader" aria-label="Ссылка на оригинал" />,
    // role="cell" sits on a display:contents wrapper so the <a> stays the grid
    // item (identical layout) AND keeps its link role inside the cell.
    cell: (r) => {
      // safeUrl gates href against javascript:/data: schemes (XSS). url is
      // undefined for the design fixtures (→ "Росреестр ↗" fallback, keeps the
      // storybook markup pixel-identical), null for a real row with no link
      // (→ "—"), and a string when a real listing URL is available.
      const href = typeof r.url === "string" ? safeUrl(r.url) : null;
      return (
        <span role="cell" style={{ display: "contents" }}>
          {href ? (
            <a
              className="sv-rr"
              href={href}
              target="_blank"
              rel="noopener noreferrer"
              style={linkBtn}
            >
              Источник ↗
            </a>
          ) : r.url === null ? (
            <span style={monoMuted2}>—</span>
          ) : (
            <span className="sv-rr" style={linkBtn}>
              Росреестр ↗
            </span>
          )}
        </span>
      );
    },
    empty: () => false,
  },
];

const DEAL_COLS: Col<DealRowData>[] = [
  {
    key: "addr",
    fr: "2fr",
    head: <span role="columnheader">АДРЕС</span>,
    cell: (r) => (
      <span role="cell" style={{ color: tokens.body }}>
        {r.addr}
      </span>
    ),
    empty: (r) => isDash(r.addr),
  },
  {
    key: "meta",
    fr: "1.3fr",
    head: <span role="columnheader">ПАРАМЕТРЫ</span>,
    cell: (r) => (
      <span role="cell" style={metaSpan}>
        {r.meta}
      </span>
    ),
    empty: (r) => isDash(r.meta),
  },
  {
    key: "source",
    fr: "1.2fr",
    head: <span role="columnheader">ИСТОЧНИК</span>,
    cell: (r) => (
      <span role="cell">
        <span style={badge}>{r.source ?? "Росреестр"}</span>{" "}
        <span style={{ fontSize: "9px", color: tokens.muted2 }}>
          {r.tier ?? "по улице"}
        </span>
      </span>
    ),
    empty: () => false,
  },
  {
    key: "ppm",
    fr: "1fr",
    head: <span role="columnheader">₽/М²</span>,
    cell: (r) => (
      <span role="cell" style={monoMuted}>
        {r.ppm}
      </span>
    ),
    empty: (r) => isDash(r.ppm),
  },
  {
    key: "price",
    fr: "1.1fr",
    head: <span role="columnheader">ЦЕНА СДЕЛКИ</span>,
    cell: (r) => (
      <span role="cell" style={monoInk}>
        {r.price}
      </span>
    ),
    empty: (r) => isDash(r.price),
  },
  {
    key: "date",
    fr: "1fr",
    head: <span role="columnheader">ДАТА</span>,
    cell: (r) => (
      <span role="cell" style={monoMuted2}>
        {r.date ?? "—"}
      </span>
    ),
    empty: (r) => isDash(r.date),
  },
];

function KpiCell({
  label,
  value,
  unit,
  valueColor = tokens.ink2,
  unitColor = tokens.muted,
  last = false,
}: {
  label: string;
  value: string;
  unit: string;
  valueColor?: string;
  unitColor?: string;
  last?: boolean;
}) {
  return (
    <div
      style={{
        padding: "12px 18px",
        borderRight: last ? undefined : `1px solid ${tokens.lineSoft2}`,
      }}
    >
      <div style={kpiLabel}>{label}</div>
      <div
        style={{
          fontFamily: tokens.font.mono,
          fontSize: "22px",
          fontWeight: 300,
          color: valueColor,
          marginTop: 4,
        }}
      >
        {value}{" "}
        <span style={{ fontSize: "11px", color: unitColor }}>{unit}</span>
      </div>
    </div>
  );
}

/** Header row + body rows wrapped in role="table"; columns are pre-filtered. */
function DataTable<R>({
  label,
  cols,
  rows,
  grid,
}: {
  label: string;
  cols: Col<R>[];
  rows: R[];
  grid: string;
}) {
  return (
    <div role="table" aria-label={label}>
      <div
        role="row"
        style={{ ...tableHead, display: "grid", gridTemplateColumns: grid }}
      >
        {cols.map((c) => (
          <Fragment key={c.key}>{c.head}</Fragment>
        ))}
      </div>
      {rows.map((r, i) => (
        <div
          key={i}
          role="row"
          className="sv-row"
          style={{ ...tableRow, display: "grid", gridTemplateColumns: grid }}
        >
          {cols.map((c) => (
            <Fragment key={c.key}>{c.cell(r, i)}</Fragment>
          ))}
        </div>
      ))}
    </div>
  );
}

// Default presentation data (unwired/storybook usage): the existing design
// fixtures. AdRow[]/DealRow[] satisfy AdRowData[]/DealRowData[] (extras optional).
const SOURCES_FIXTURE: SourcesData = {
  adRows,
  dealRows,
  marketAds,
  marketDeals,
};

interface SourcesViewProps {
  data?: SourcesData;
  // Raw estimate (real lat/lon per lot) — the table rows above are pre-mapped
  // display strings with no geometry, so SourcesMap needs the source object
  // directly. Optional: unwired/storybook usage (no estimate) renders the
  // tables only, same honest-empty contract as the rest of the mappers.
  estimate?: AggregatedEstimate | null;
}

export default function SourcesView({
  data = SOURCES_FIXTURE,
  estimate = null,
}: SourcesViewProps) {
  const adCols = visibleCols(AD_COLS, data.adRows);
  const adGrid = adCols.map((c) => c.fr).join(" ");
  const dealCols = visibleCols(DEAL_COLS, data.dealRows);
  const dealGrid = dealCols.map((c) => c.fr).join(" ");

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 22 }}>
      <style>{`
        .sv-row { transition: background .12s ease; }
        .sv-row:hover { background: ${tokens.surface.w50}; }
        .sv-rr { transition: background .12s ease, border-color .12s ease; }
        .sv-rr:hover { background: ${BADGE_BG}; border-color: ${tokens.accent}; }
      `}</style>

      <SourcesMap estimate={estimate} />

      {/* объявления */}
      <div style={card}>
        <div style={cardHeader}>
          <div>
            <div style={eyebrow}>ПОХОЖИЕ КВАРТИРЫ · В ПРОДАЖЕ</div>
            <div style={cardTitle}>
              Объявления — аналогичные квартиры в продаже
            </div>
          </div>
          <div style={headerRight}>
            {/* §M8 — литеральный «Обновлено: —» скрыт (нет реального значения). */}
            Источников:{" "}
            <b style={{ color: tokens.muted }}>{data.sourceCount ?? "—"}</b>
          </div>
        </div>

        <div style={kpiRow}>
          <KpiCell
            label="ОБЪЯВЛЕНИЙ ПО АНАЛОГАМ"
            value={data.marketAds.kpi.count}
            unit="шт"
          />
          <KpiCell
            label="МЕДИАНА"
            value={data.marketAds.kpi.median}
            unit={`млн ₽ · ${data.marketAds.kpi.ppm} ₽/м²`}
          />
          <KpiCell
            // §M8 — «СРЕДНИЕ 50% РЫНКА» вместо статистического «ДИАПАЗОН P25–P75»
            // (P25–P75 оставлен в скобках); «CV» → «разброс» (внутренний термин).
            label="СРЕДНИЕ 50% РЫНКА (P25–P75)"
            value={data.marketAds.kpi.range}
            unit={`млн · разброс ${data.marketAds.kpi.cv}`}
            valueColor={tokens.accent}
            last
          />
        </div>

        <div
          style={{
            display: "flex",
            alignItems: "center",
            gap: 10,
            padding: "11px 18px",
            borderBottom: `1px solid ${tokens.lineSoft3}`,
          }}
        >
          <span
            style={{
              fontSize: "9px",
              letterSpacing: "1.5px",
              color: tokens.muted2,
            }}
          >
            ФИЛЬТР
          </span>
          {data.marketAds.filters.map((f, i) => (
            <span key={i} style={chip}>
              {f}
            </span>
          ))}
        </div>

        <DataTable
          label="Объявления — аналогичные квартиры в продаже"
          cols={adCols}
          rows={data.adRows}
          grid={adGrid}
        />

        {/* Fix #1/#8 — discloses the top-10 display cap vs. the true n_analogs
            total (mirrors the deals-table footer below) + any outlier exclusion. */}
        {data.adsFootnote && (
          <div
            style={{
              padding: "11px 18px",
              fontSize: "10px",
              color: tokens.hint,
              background: KPI_BG,
            }}
          >
            {data.adsFootnote}
          </div>
        )}
      </div>

      {/* фактические сделки */}
      <div style={card}>
        <div style={cardHeader}>
          <div>
            <div style={eyebrow}>ПОХОЖИЕ КВАРТИРЫ · СДЕЛКИ</div>
            <div style={cardTitle}>
              Фактические сделки по аналогичным квартирам
            </div>
          </div>
          <div style={headerRight}>
            Период: <b style={{ color: tokens.muted }}>за 12 мес.</b>
            <br />
            проверенные данные · подтверждено Росреестром
          </div>
        </div>

        <div style={kpiRow}>
          <KpiCell
            label="СДЕЛОК ПО АНАЛОГАМ"
            value={data.marketDeals.kpi.count}
            unit="шт · Росреестр"
          />
          <KpiCell
            label="МЕДИАНА СДЕЛОК"
            value={data.marketDeals.kpi.median}
            unit={`млн ₽ · ${data.marketDeals.kpi.delta}`}
            unitColor={tokens.success}
          />
          <KpiCell
            label="ДИАПАЗОН"
            value={data.marketDeals.kpi.range}
            unit="млн"
            valueColor={tokens.accent}
            last
          />
        </div>

        <DataTable
          label="Фактические сделки по аналогичным квартирам"
          cols={dealCols}
          rows={data.dealRows}
          grid={dealGrid}
        />

        <div
          style={{
            padding: "11px 18px",
            fontSize: "10px",
            color: tokens.hint,
            background: KPI_BG,
          }}
        >
          Показано {data.dealRows.length} из {data.marketDeals.kpi.count}{" "}
          фактических сделок
        </div>
      </div>
    </div>
  );
}
