"use client";

// Overlay 06 "АНАЛИТИКА ДОМА" — markup port from the МЕРА Оценка pixel design.
// Accepts one composite `data` prop (default = the design fixture); page.tsx
// feeds mapAnalytics(...) output. The price-history dots navigate to the
// "Аналогичные объявления" overlay via onNavigate?.(2).

import { useMemo, useState } from "react";
import { tokens } from "./tokens";
import { analytics as ANALYTICS_FIXTURE } from "./fixtures";
import { phYearX as histYearX, PH_GRID_Y } from "./mappers";
import type { Analytics, SellTimeTier } from "./types";

interface AnalyticsViewProps {
  data?: Analytics;
  onNavigate?: (i: number) => void;
}

// ---- price-history geometry -----------------------------------------------
// Parse the SVG polyline point strings into coordinates so the markers and
// the transparent hover targets can be derived from the same source. Empty
// series (graceful-degradation case) parse to [] — no dots, no hover targets.

interface Pt {
  x: number;
  y: number;
}

function parsePoints(s: string): Pt[] {
  return s
    .trim()
    .split(/\s+/)
    .filter((pair) => pair.length > 0)
    .map((pair) => {
      const [x, y] = pair.split(",").map(Number);
      return { x, y };
    });
}

// M5 — X positions of the year axis labels. MUST be DATA-DRIVEN from the number
// of years, using the SAME geometry the mapper uses for the polyline vertices
// (buildPriceHistory: phYearX over [PH_X_LEFT, PH_X_RIGHT]) — imported from
// ./mappers (single source of truth) rather than copy-pasted, so the two never
// drift apart. A fixed 5-slot array broke whenever the real series had ≠5
// years: the extra labels fell back to an undefined x → x=0, so a late year
// (e.g. 2026) was drawn FIRST, on top of the y-axis ticks. Now each label sits
// centred under its own vertex.

// ---- sell-time tier tints --------------------------------------------------
// One-off decorative tints per variant (design lines 553-556). The soft border
// hex (#bfe3d2 / #e3d2ad / #e6c3c3) and the rgba background fills are documented
// one-off design tints with no token equivalent; the label text colour reuses
// semantic tokens (success / accent / warn / danger).

const tierStyles: Record<
  SellTimeTier["variant"],
  { bg: string; border: string; label: string }
> = {
  success: {
    bg: "rgba(27,170,107,.07)",
    border: "1px solid #bfe3d2",
    label: tokens.success,
  },
  accent: {
    bg: "rgba(46,139,255,.08)",
    border: `1.5px solid ${tokens.accent}`,
    label: tokens.accent,
  },
  gold: {
    bg: "rgba(201,154,62,.09)",
    border: "1px solid #e3d2ad",
    label: tokens.warn,
  },
  danger: {
    bg: "rgba(214,90,90,.07)",
    border: "1px solid #e6c3c3",
    label: tokens.danger,
  },
};

const cardStyle = {
  background: tokens.surface.w55,
  border: `1px solid ${tokens.line2}`,
  borderRadius: 8,
  padding: "16px 18px",
} as const;

// L1 — a "обычно X–Y дн" band is only honest with ≥2 lots AND a non-degenerate
// span. From a single analog (n<2) or when min===max ("237–237") the band is a
// fake range, so we suppress it and keep just the point estimate (tier.days) +
// the lot count. Gated on BOTH the parsed n (count line "1 аналог" → 1) and the
// parsed range endpoints, so either signal alone drops the band.
function sellTimeRangeMeaningful(tier: SellTimeTier): boolean {
  if (!tier.range || tier.range === "—") return false;
  const n = Number.parseInt(tier.count, 10); // "1 аналог" → 1
  if (Number.isFinite(n) && n < 2) return false;
  const nums = tier.range.match(/\d+/g)?.map(Number) ?? []; // "18–180" → [18,180]
  if (nums.length >= 2 && nums[0] === nums[nums.length - 1]) return false;
  return true;
}

// M6 — «Срок продажи в зависимости от цены» is not a real trend when built on
// thin buckets: on the audited object +5% mapped to 80 дн (below the 119-дн
// median) off just 3–6 analogs, i.e. noise read as a signal. We therefore GATE
// the chart honestly instead of faking monotonicity:
//   • a tile with fewer than SELLTIME_MIN_N analogs is dropped (too few to trust);
//   • if fewer than SELLTIME_MIN_TILES tiles survive, the whole grid is replaced
//     by a «мало данных» note (nothing meaningful left to compare).
const SELLTIME_MIN_N = 5;
const SELLTIME_MIN_TILES = 3;

/** Parse the analog count out of a tier ("6 аналогов" → 6). Unknown → 0. */
function sellTimeTierN(tier: SellTimeTier): number {
  const n = Number.parseInt(tier.count, 10);
  return Number.isFinite(n) ? n : 0;
}

/**
 * A tier is trustworthy only with a real point estimate, ≥ SELLTIME_MIN_N lots,
 * AND the backend not having flagged it as insufficient (V3) — a bucket the
 * backend itself calls too thin is dropped like any other untrustworthy tile,
 * never shown as a confident value.
 */
function sellTimeTierValid(tier: SellTimeTier): boolean {
  return (
    !tier.insufficientData &&
    tier.days !== "—" &&
    sellTimeTierN(tier) >= SELLTIME_MIN_N
  );
}

export default function AnalyticsView({
  data = ANALYTICS_FIXTURE,
  onNavigate,
}: AnalyticsViewProps) {
  const [hoverHist, setHoverHist] = useState(false);

  // M6 — only trustworthy price/sell-time tiles (≥ SELLTIME_MIN_N analogs).
  const sellTimeTiles = useMemo(
    () => data.sellTime.filter(sellTimeTierValid),
    [data.sellTime],
  );

  // Price-history series + hover targets, derived from the data prop. Яндекс
  // only draws dots where its series diverges from Avito (последние 3 года).
  const avitoPts = useMemo(
    () => parsePoints(data.priceHistory.avito),
    [data.priceHistory.avito],
  );
  const yandexPts = useMemo(
    () => parsePoints(data.priceHistory.yandex),
    [data.priceHistory.yandex],
  );
  const yandexDots = useMemo(
    () =>
      yandexPts.filter(
        (p, i) =>
          !avitoPts[i] || p.x !== avitoPts[i].x || p.y !== avitoPts[i].y,
      ),
    [yandexPts, avitoPts],
  );
  const hoverPts = useMemo<Pt[]>(
    () => [...avitoPts, ...yandexDots],
    [avitoPts, yandexDots],
  );

  // L2 — the subtitle is mapper-built and hardcodes "Avito + Яндекс"; drop any
  // series that drew no points so the subtitle matches the rendered legend (the
  // swatches below are already gated on *Pts.length). If the note format changes
  // the replace is a graceful no-op.
  const activeSeriesNames = [
    avitoPts.length > 0 ? "Avito" : null,
    yandexPts.length > 0 ? "Яндекс" : null,
  ].filter((s): s is string => s != null);
  const historyNote =
    activeSeriesNames.length > 0
      ? data.priceHistory.note.replace(
          /Avito \+ Яндекс/,
          activeSeriesNames.join(" + "),
        )
      : data.priceHistory.note;

  return (
    <div
      role="region"
      tabIndex={0}
      aria-label="Аналитика дома — прокручиваемая область"
      style={{ display: "flex", flexDirection: "column", gap: 22 }}
    >
      {/* header */}
      <div
        style={{
          display: "flex",
          alignItems: "flex-end",
          justifyContent: "space-between",
        }}
      >
        <div style={{ fontSize: 13, fontWeight: 600, color: tokens.ink2 }}>
          Аналитика дома
        </div>
        <div style={{ fontSize: 10, color: tokens.muted2 }}>
          {data.header.ads} · {data.header.radius}
        </div>
      </div>

      {/* KPI row */}
      <div
        style={{
          display: "grid",
          gridTemplateColumns: "repeat(3,1fr)",
          gap: 16,
        }}
      >
        {data.kpis.map((kpi, i) => (
          <div key={i} style={cardStyle}>
            <div
              style={{
                fontSize: 9.5,
                letterSpacing: 1.5,
                // C1: primary KPI label → body2 (matches ParamsPanel field-label
                // pattern); the muted value/sub below keep the secondary tier.
                color: tokens.body2,
              }}
            >
              {kpi.label}
            </div>
            <div
              style={{
                fontFamily: tokens.font.mono,
                fontSize: 30,
                fontWeight: 300,
                color: kpi.color ?? tokens.ink2,
                margin: "8px 0 5px",
              }}
            >
              {kpi.value}
              {kpi.unit ? (
                <>
                  {" "}
                  <span style={{ fontSize: 14, color: tokens.muted }}>
                    {kpi.unit}
                  </span>
                </>
              ) : null}
            </div>
            <div style={{ fontSize: 10, color: tokens.hint }}>{kpi.sub}</div>
          </div>
        ))}
      </div>

      {/* срок продажи в зависимости от цены */}
      <div style={cardStyle}>
        <div style={{ fontSize: 12, fontWeight: 600, color: tokens.ink2 }}>
          Срок продажи в зависимости от цены
        </div>
        <div
          style={{ fontSize: 10, color: tokens.muted2, margin: "4px 0 14px" }}
        >
          {data.sellTimeNote}
        </div>
        {sellTimeTiles.length >= SELLTIME_MIN_TILES ? (
          <div
            style={{
              display: "grid",
              gridTemplateColumns: `repeat(${sellTimeTiles.length},1fr)`,
              gap: 12,
            }}
          >
            {sellTimeTiles.map((tier, i) => {
              const v = tierStyles[tier.variant];
              return (
                <div
                  key={i}
                  style={{
                    background: v.bg,
                    border: v.border,
                    borderRadius: 7,
                    padding: "13px 15px",
                  }}
                >
                  <div
                    style={{ fontSize: 9.5, color: v.label, letterSpacing: 0.5 }}
                  >
                    {tier.tier}
                  </div>
                  <div
                    style={{
                      fontFamily: tokens.font.mono,
                      fontSize: 26,
                      fontWeight: 300,
                      color: tokens.ink2,
                      margin: "6px 0 4px",
                    }}
                  >
                    {tier.days}
                  </div>
                  <div
                    style={{
                      fontSize: 9.5,
                      color: tokens.hint,
                      lineHeight: 1.5,
                    }}
                  >
                    {sellTimeRangeMeaningful(tier) ? (
                      <>
                        {tier.range}
                        <br />
                      </>
                    ) : null}
                    {tier.count}
                  </div>
                </div>
              );
            })}
          </div>
        ) : (
          <div
            style={{
              fontSize: 11,
              color: tokens.muted2,
              lineHeight: 1.5,
              padding: "6px 0 2px",
            }}
          >
            Мало данных для оценки чувствительности срока продажи к цене.
          </div>
        )}
      </div>

      {/* история цен + цена × срок продажи — side by side so both fit the
          fixed-height v2 artboard panel without scrolling (reclaims ~194px
          of vertical space vs. stacking). Both SVGs share height: 260 (viewBox
          heights differ — 220 vs 300 — but preserveAspectRatio="none" stretches
          them to match) so the two cards render at equal height. */}
      <div
        style={{
          display: "grid",
          gridTemplateColumns: "1fr 1fr",
          gap: 16,
          alignItems: "stretch",
        }}
      >
        {/* история цен */}
        <div style={{ position: "relative", ...cardStyle }}>
          <div
            style={{
              display: "flex",
              alignItems: "flex-end",
              justifyContent: "space-between",
            }}
          >
            <div>
              <div style={{ fontSize: 12, fontWeight: 600, color: tokens.ink2 }}>
                История цен в этом доме
              </div>
              <div style={{ fontSize: 10, color: tokens.muted2, marginTop: 4 }}>
                {historyNote}
              </div>
            </div>
            <div style={{ display: "flex", gap: 16, fontSize: 10 }}>
              {avitoPts.length > 0 ? (
                <span
                  style={{
                    display: "flex",
                    alignItems: "center",
                    gap: 6,
                    color: tokens.muted,
                  }}
                >
                  <span
                    style={{ width: 14, height: 2, background: tokens.accent }}
                  />
                  Avito
                </span>
              ) : null}
              {yandexPts.length > 0 ? (
                <span
                  style={{
                    display: "flex",
                    alignItems: "center",
                    gap: 6,
                    color: tokens.muted,
                  }}
                >
                  <span
                    style={{ width: 14, height: 2, background: tokens.gold }}
                  />
                  Яндекс
                </span>
              ) : null}
            </div>
          </div>
          <svg
            viewBox="0 0 900 220"
            style={{ width: "100%", height: 260, marginTop: 12 }}
            preserveAspectRatio="none"
            role="img"
            aria-label="График истории цен в этом доме: медиана ₽/м² по годам, серии Avito и Яндекс"
          >
            <title>История цен в этом доме — медиана ₽/м² по годам</title>
            <g stroke={tokens.lineSoft2} strokeWidth={1}>
              {PH_GRID_Y.map((y) => (
                <line key={y} x1={40} y1={y} x2={900} y2={y} />
              ))}
            </g>
            <g fontFamily={tokens.font.mono} fontSize={11} fill={tokens.muted2}>
              {data.priceHistory.yTicks.map((t) => (
                <text key={t.label} x={0} y={t.y + 4}>
                  {t.label}
                </text>
              ))}
            </g>
            <g
              fontFamily={tokens.font.mono}
              fontSize={11}
              fill={tokens.muted2}
              textAnchor="middle"
            >
              {data.priceHistory.years.map((yr, i) => (
                <text
                  key={yr}
                  x={histYearX(i, data.priceHistory.years.length)}
                  y={214}
                >
                  {yr}
                </text>
              ))}
            </g>
            <polyline
              points={data.priceHistory.avito}
              fill="none"
              stroke={tokens.accent}
              strokeWidth={2.5}
            />
            <polyline
              points={data.priceHistory.yandex}
              fill="none"
              stroke={tokens.gold}
              strokeWidth={2.5}
            />
            {/* white dot fill on an accent ring — documented one-off, no token (design l.568) */}
            <g
              fill="#fff"
              stroke={tokens.accent}
              strokeWidth={2}
              style={{ cursor: "pointer" }}
            >
              <title>Архивные объявления Avito · нажмите, чтобы открыть</title>
              {avitoPts.map((p, i) => (
                <circle key={i} cx={p.x} cy={p.y} r={3.5} />
              ))}
            </g>
            <g
              fill={tokens.gold}
              stroke={tokens.gold}
              strokeWidth={2}
              style={{ cursor: "pointer" }}
            >
              {yandexDots.map((p, i) => (
                <circle key={i} cx={p.x} cy={p.y} r={3.5} />
              ))}
            </g>
            {/* WCAG 2.1.1 (#4): the chart's "open archived listings" affordance
                was a mouse-only <g onClick>. Expose it as a real focusable
                button (role + tabIndex + Enter/Space) so keyboard/SR users can
                open the «Аналогичные объявления» overlay too — the transparent
                hover circles stay as the pointer target. Only interactive when
                there are points to click (no empty tab stop on a data-less
                chart). onFocus/onBlur mirror hover so the hint tooltip shows on
                keyboard focus as well. */}
            <g
              fill="transparent"
              role={hoverPts.length > 0 ? "button" : undefined}
              tabIndex={hoverPts.length > 0 ? 0 : undefined}
              aria-label={
                hoverPts.length > 0
                  ? "Открыть архивные объявления этого дома"
                  : undefined
              }
              style={{ cursor: "pointer" }}
              onMouseEnter={() => setHoverHist(true)}
              onMouseLeave={() => setHoverHist(false)}
              onFocus={() => setHoverHist(true)}
              onBlur={() => setHoverHist(false)}
              onClick={() => onNavigate?.(2)}
              onKeyDown={(e) => {
                if (e.key === "Enter" || e.key === " ") {
                  e.preventDefault();
                  onNavigate?.(2);
                }
              }}
            >
              {hoverPts.map((p, i) => (
                <circle key={i} cx={p.x} cy={p.y} r={13} />
              ))}
            </g>
          </svg>
          {hoverHist ? (
            // Dark navy tooltip surface + white text — documented one-off literals
            // matching the design (l.572); no token equivalent.
            <div
              style={{
                position: "absolute",
                left: 64,
                top: 54,
                background: "rgba(23,38,58,.96)",
                color: "#fff",
                borderRadius: 7,
                padding: "9px 13px",
                fontSize: 11,
                lineHeight: 1.5,
                zIndex: 6,
                pointerEvents: "none",
                boxShadow: "0 10px 26px rgba(20,40,70,.28)",
              }}
            >
              <div style={{ fontWeight: 600 }}>
                Архивные объявления этого дома
              </div>
              <div style={{ color: tokens.barMid, fontSize: 10, marginTop: 2 }}>
                Нажмите, чтобы открыть список →
              </div>
            </div>
          ) : null}
        </div>

        {/* цена × срок продажи (детально) */}
        <div style={cardStyle}>
          <div
            style={{
              display: "flex",
              alignItems: "flex-end",
              justifyContent: "space-between",
            }}
          >
            <div>
              <div style={{ fontSize: 12, fontWeight: 600, color: tokens.ink2 }}>
                Цена × срок на рынке
              </div>
              <div style={{ fontSize: 10, color: tokens.muted2, marginTop: 4 }}>
                {/* Honesty (#2): active analogs carry «дней на рынке так далеко»
                    (today − listing_date, нижняя граница), not realized
                    time-to-sell — the axis reads «срок на рынке», not «срок
                    продажи», so an ещё-продающийся лот isn't shown as a
                    measured sale time. */}
                Цена в зависимости от срока на рынке · {data.scatterDetail.note}
              </div>
            </div>
            <div style={{ display: "flex", gap: 14, fontSize: 10 }}>
              <span
                style={{
                  display: "flex",
                  alignItems: "center",
                  gap: 6,
                  color: tokens.muted,
                }}
              >
                <span
                  style={{
                    width: 8,
                    height: 8,
                    borderRadius: "50%",
                    background: tokens.accent,
                  }}
                />
                аналоги
              </span>
              {data.scatterDetail.deals.length > 0 ? (
                <span
                  style={{
                    display: "flex",
                    alignItems: "center",
                    gap: 6,
                    color: tokens.muted,
                  }}
                >
                  <span
                    style={{
                      width: 8,
                      height: 8,
                      borderRadius: "50%",
                      background: tokens.scatterDeal,
                    }}
                  />
                  сделки
                </span>
              ) : null}
              <span
                style={{
                  display: "flex",
                  alignItems: "center",
                  gap: 6,
                  color: tokens.muted,
                }}
              >
                <span
                  style={{
                    width: 8,
                    height: 8,
                    borderRadius: "50%",
                    background: tokens.ink,
                  }}
                />
                объект
              </span>
            </div>
          </div>
          <svg
            viewBox="0 0 900 300"
            style={{ width: "100%", height: 260, marginTop: 12 }}
            preserveAspectRatio="none"
            role="img"
            aria-label="Точечный график зависимости цены от срока лота на рынке: аналоги, сделки и ваш объект"
          >
            <title>Цена × срок на рынке</title>
            <g stroke={tokens.lineSoft2} strokeWidth={1}>
              <line x1={60} y1={20} x2={880} y2={20} />
              <line x1={60} y1={77} x2={880} y2={77} />
              <line x1={60} y1={134} x2={880} y2={134} />
              <line x1={60} y1={191} x2={880} y2={191} />
              <line x1={60} y1={250} x2={880} y2={250} />
              <line x1={265} y1={20} x2={265} y2={250} />
              <line x1={470} y1={20} x2={470} y2={250} />
              <line x1={675} y1={20} x2={675} y2={250} />
            </g>
            <line
              x1={60}
              y1={20}
              x2={60}
              y2={250}
              stroke={tokens.line3}
              strokeWidth={1.5}
            />
            <line
              x1={60}
              y1={250}
              x2={880}
              y2={250}
              stroke={tokens.line3}
              strokeWidth={1.5}
            />
            <g fill={tokens.scatterDeal}>
              {data.scatterDetail.deals.map((p, i) => (
                <circle key={i} cx={p.x} cy={p.y} r={p.r} />
              ))}
            </g>
            <g fill={tokens.accent}>
              {data.scatterDetail.analogs.map((p, i) => (
                <circle key={i} cx={p.x} cy={p.y} r={p.r} />
              ))}
            </g>
            <g opacity={data.scatterDetail.subjectApprox ? 0.55 : 1}>
              {/* V4: subjectApprox marks a fallback x (est_days_on_market unknown) —
                  dashed ring + title flag it as an estimate, not a measurement. */}
              {data.scatterDetail.subjectApprox ? (
                <title>
                  Срок продажи — оценка (~90 дн.), точных данных нет
                </title>
              ) : null}
              <circle
                cx={data.scatterDetail.subject.x}
                cy={data.scatterDetail.subject.y}
                r={data.scatterDetail.subject.r}
                fill={tokens.ink}
              />
              <circle
                cx={data.scatterDetail.subject.x}
                cy={data.scatterDetail.subject.y}
                r={11}
                fill="none"
                stroke={tokens.ink}
                strokeWidth={1.2}
                strokeDasharray={
                  data.scatterDetail.subjectApprox ? "3 3" : undefined
                }
                opacity={0.4}
              />
            </g>
            <g
              fontFamily={tokens.font.mono}
              fontSize={11}
              fill={tokens.muted2}
              textAnchor="end"
            >
              {data.scatterDetail.yTicks.map((t) => (
                <text key={t.label} x={52} y={t.y}>
                  {t.label}
                </text>
              ))}
            </g>
            <g
              fontFamily={tokens.font.mono}
              fontSize={11}
              fill={tokens.muted2}
              textAnchor="middle"
            >
              {data.scatterDetail.xTicks.map((t) => (
                <text key={t.label} x={t.x} y={268}>
                  {t.label}
                </text>
              ))}
            </g>
            <text
              x={470}
              y={288}
              fontSize={10}
              letterSpacing={1}
              fill={tokens.muted2}
              textAnchor="middle"
            >
              СРОК НА РЫНКЕ, ДН
            </text>
            <text
              x={18}
              y={135}
              fontSize={10}
              letterSpacing={1}
              fill={tokens.muted2}
              textAnchor="middle"
              transform="rotate(-90 18 135)"
            >
              ЦЕНА ПРОДАЖИ, ₽
            </text>
          </svg>
        </div>
      </div>
    </div>
  );
}
