"use client";

/**
 * 6.4 Рекомендация по продукту — §13.4 product_tz (#953/#983).
 *
 * The literal answer to «что строить на участке»: рекомендованный класс (pill) +
 * квартирография (горизонтальные bar'ы по дефициту форматов) + коммерческий
 * сигнал + USP-ниши + §16-обоснование (раскрываемое).
 *
 * Quartirografiya is rendered as pure-Tailwind/token width-% bars (mobile-first,
 * NOT a pie): bar length = |deficit_index| (or `pct` when an explicit mix carries
 * shares), colour = deficit semantics (>0 недонасыщенность success, <0 затоварка
 * danger, ≈0 баланс neutral) — same mapping as 6.1.
 *
 * GRACEFUL: returns null when product_tz is absent/empty; each sub-field is
 * guarded individually so a thin/partial section never crashes.
 */

import type {
  ProductCommercial,
  ProductMixEntry,
  ProductReason,
  ProductReasonRejected,
  ProductUsp,
  ReportProductTz,
} from "@/types/forecast";

import { Badge } from "@/components/ui/Badge";
import type { BadgeVariant } from "@/components/ui/Badge";
import {
  aggregateClassDeficits,
  DEFICIT_BALANCE_EPS,
  deficitBarWidthPct,
  deficitVariant,
  deficitWord,
  fmtNum,
} from "./forecast-helpers";
import type { ClassDeficit } from "./forecast-helpers";

interface Props {
  /** §13.4 section; the whole block is skipped when absent (see Section6Forecast). */
  productTz: ReportProductTz;
}

// Deficit BadgeVariant → bar-fill token. Semantic colours encode deficit sign
// (success/danger/neutral) — this is the documented viz exception (bar = signal).
const BAR_FILL: Record<BadgeVariant, string> = {
  success: "var(--success)",
  danger: "var(--danger)",
  warning: "var(--warn)",
  info: "var(--accent)",
  neutral: "var(--border-strong)",
};

const SUBHEAD_STYLE: React.CSSProperties = {
  fontSize: 11,
  fontWeight: 500,
  letterSpacing: "0.04em",
  textTransform: "uppercase",
  color: "var(--fg-secondary)",
};

function isNonEmpty(productTz: ReportProductTz): boolean {
  return (
    productTz.obj_class != null ||
    productTz.mix.length > 0 ||
    productTz.usp.length > 0 ||
    productTz.reasons.length > 0 ||
    (productTz.commercial != null &&
      (productTz.commercial.available != null ||
        !!productTz.commercial.caveat)) ||
    !!productTz.summary
  );
}

export function ForecastProductTzBlock({ productTz }: Props) {
  // Graceful: nothing meaningful to show → render nothing.
  if (!isNonEmpty(productTz)) return null;

  // #1742 — объяснение выбора класса: per-класс дефицит (агрегат mix по классам).
  const classDeficits = aggregateClassDeficits(productTz.mix);

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
      {/* Recommended class — prominent pill + автоген-фраза про дефицит + 3 класса */}
      <ClassHeadline
        obj_class={productTz.obj_class}
        summary={productTz.summary}
        classDeficits={classDeficits}
      />

      {/* #1742 — отвергнутые альтернативы подняты из-под ката (выше обоснования) */}
      <RejectedAlternatives reasons={productTz.reasons} />

      {/* Квартирография — deficit/share bars */}
      <MixBars mix={productTz.mix} />

      {/* Коммерция */}
      <CommercialSignal commercial={productTz.commercial} />

      {/* USP-ниши */}
      <UspList usp={productTz.usp} />

      {/* §16 обоснование (раскрываемое) — без «отвергнутых» (подняты выше) */}
      <ReasonsDisclosure reasons={productTz.reasons} />
    </div>
  );
}

// ── Recommended class ─────────────────────────────────────────────────────────

function ClassHeadline({
  obj_class,
  summary,
  classDeficits,
}: {
  obj_class: string | null;
  summary: string | null;
  classDeficits: ClassDeficit[];
}) {
  if (obj_class == null && !summary) return null;

  // #1742 — дефицит рекомендованного класса (для автоген-фразы). Ищем его агрегат
  // в per-класс таблице; null → у класса нет измеримого индекса (тонкие данные).
  const recoDeficit =
    obj_class != null
      ? (classDeficits.find((c) => c.obj_class === obj_class)
          ?.meanDeficitIndex ?? null)
      : null;

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
      {obj_class != null && (
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <span style={SUBHEAD_STYLE}>Рекомендованный класс</span>
          <Badge variant="info" size="md">
            {capitalize(obj_class)}
          </Badge>
        </div>
      )}

      {/* Автоген-фраза: почему именно этот класс (#1742) */}
      {obj_class != null && recoDeficit != null && (
        <p
          style={{
            margin: 0,
            fontSize: 13,
            lineHeight: 1.5,
            color: "var(--fg-primary)",
          }}
        >
          <strong style={{ fontWeight: 600 }}>{capitalize(obj_class)}</strong> —
          здесь сильнее всего недозакрыт спрос: индекс дефицита{" "}
          <span
            style={{
              fontVariantNumeric: "tabular-nums",
              fontWeight: 600,
              color: `var(--${deficitVariant(recoDeficit) === "danger" ? "danger" : deficitVariant(recoDeficit) === "success" ? "success" : "fg-secondary"})`,
            }}
            title="Индекс дефицита ∈ [−1, +1]: +1 — острый дефицит (недонасыщенность), −1 — затоварка."
          >
            {recoDeficit > 0 ? "+" : ""}
            {fmtNum(recoDeficit, 2)}
          </span>{" "}
          <span style={{ color: "var(--fg-tertiary)" }}>
            (где +1 — острый дефицит, −1 — затоварка).
          </span>
        </p>
      )}

      {/* Мини-таблица 3 классов с их индексом дефицита (контраст) (#1742) */}
      <ClassDeficitTable
        classDeficits={classDeficits}
        recommended={obj_class}
      />

      {summary && (
        <p
          style={{
            margin: 0,
            fontSize: 13,
            lineHeight: 1.5,
            color: "var(--fg-secondary)",
          }}
        >
          {summary}
        </p>
      )}
    </div>
  );
}

// ── Мини-таблица per-класс дефицита (#1742 — контраст 3 классов) ───────────────

function ClassDeficitTable({
  classDeficits,
  recommended,
}: {
  classDeficits: ClassDeficit[];
  recommended: string | null;
}) {
  if (classDeficits.length === 0) return null;

  return (
    <div
      style={{
        display: "flex",
        flexDirection: "column",
        border: "1px solid var(--border-card)",
        borderRadius: 8,
        overflow: "hidden",
      }}
    >
      {/* header */}
      <div
        style={{
          display: "grid",
          gridTemplateColumns: "1fr auto",
          gap: 12,
          padding: "6px 12px",
          background: "var(--bg-card-alt)",
          borderBottom: "1px solid var(--border-soft)",
        }}
      >
        <span style={SUBHEAD_STYLE}>Класс</span>
        <span style={{ ...SUBHEAD_STYLE, textAlign: "right" }}>
          Индекс дефицита
        </span>
      </div>
      {classDeficits.map((c, i) => {
        const isReco = recommended != null && c.obj_class === recommended;
        const di = c.meanDeficitIndex;
        return (
          <div
            key={c.obj_class}
            style={{
              display: "grid",
              gridTemplateColumns: "1fr auto",
              gap: 12,
              alignItems: "center",
              padding: "7px 12px",
              background: isReco ? "var(--accent-soft)" : "var(--bg-card)",
              borderTop: i === 0 ? "none" : "1px solid var(--border-soft)",
            }}
          >
            <span
              style={{
                display: "flex",
                alignItems: "center",
                gap: 8,
                fontSize: 13,
                color: "var(--fg-primary)",
                fontWeight: isReco ? 600 : 400,
              }}
            >
              {capitalize(c.obj_class)}
              {isReco && (
                <Badge variant="info" size="sm">
                  рекомендован
                </Badge>
              )}
            </span>
            {di != null ? (
              <span
                style={{
                  fontSize: 13,
                  fontVariantNumeric: "tabular-nums",
                  fontWeight: 600,
                  textAlign: "right",
                  color: `var(--${deficitVariant(di) === "danger" ? "danger" : deficitVariant(di) === "success" ? "success" : "fg-secondary"})`,
                }}
              >
                {di > 0 ? "+" : ""}
                {fmtNum(di, 2)}
              </span>
            ) : (
              <span
                style={{
                  fontSize: 12,
                  textAlign: "right",
                  color: "var(--fg-tertiary)",
                }}
              >
                тонкие данные
              </span>
            )}
          </div>
        );
      })}
    </div>
  );
}

// ── Отвергнутые альтернативы (#1742 — поднято из-под ката) ─────────────────────

function RejectedAlternatives({ reasons }: { reasons: ProductReason[] }) {
  // Собираем уникальные отвергнутые альтернативы из всех §16-причин (де-дуп по
  // alternative — одна и та же альтернатива может встречаться в нескольких reason).
  const seen = new Set<string>();
  const rejected: ProductReasonRejected[] = [];
  for (const r of reasons) {
    for (const rej of r.rejected ?? []) {
      if (!rej.alternative || seen.has(rej.alternative)) continue;
      seen.add(rej.alternative);
      rejected.push(rej);
    }
  }
  if (rejected.length === 0) return null;

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
      <span style={SUBHEAD_STYLE}>Отвергнутые альтернативы</span>
      <div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
        {rejected.map((rej, i) => (
          <span
            key={`${rej.alternative}-${i}`}
            style={{
              display: "inline-flex",
              alignItems: "center",
              gap: 6,
              fontSize: 12,
              color: "var(--fg-secondary)",
              background: "var(--bg-card-alt)",
              border: "1px solid var(--border-soft)",
              borderRadius: 6,
              padding: "3px 8px",
            }}
          >
            <span style={{ color: "var(--fg-primary)" }}>
              {capitalize(rej.alternative)}
            </span>
            {rej.deficit_index != null && (
              <span style={{ fontVariantNumeric: "tabular-nums" }}>
                {rej.deficit_index > 0 ? "+" : ""}
                {fmtNum(rej.deficit_index, 2)}
              </span>
            )}
            <span style={{ color: "var(--fg-tertiary)" }}>· {rej.reason}</span>
          </span>
        ))}
      </div>
    </div>
  );
}

// ── Квартирография (mix bars) ─────────────────────────────────────────────────

function mixLabel(m: ProductMixEntry): string {
  const bucket = m.bucket ?? "формат";
  return m.obj_class ? `${bucket} · ${m.obj_class}` : bucket;
}

function MixBars({ mix }: { mix: ProductMixEntry[] }) {
  // Keep only entries with something to plot (a share or a deficit signal).
  const rows = mix.filter((m) => m.pct != null || m.deficit_index != null);
  if (rows.length === 0) return null;

  // Prefer explicit shares (доли) when the mix carries them; else fall back to
  // the deficit signal as the bar driver (current overlay shape).
  const hasShares = rows.some((m) => m.pct != null);

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
      <span style={SUBHEAD_STYLE}>
        Квартирография{hasShares ? "" : " · по дефициту форматов"}
      </span>
      <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
        {rows.map((m, i) => (
          <MixBar
            key={`${m.bucket ?? "?"}-${m.obj_class ?? "?"}-${i}`}
            entry={m}
          />
        ))}
      </div>
      {!hasShares && (
        <p
          style={{
            margin: 0,
            fontSize: 12,
            lineHeight: 1.5,
            color: "var(--fg-tertiary)",
          }}
        >
          Длина полосы — сила сигнала по индексу дефицита: положительный
          (зелёный) — недонасыщенность, отрицательный (красный) — затоварка.
        </p>
      )}
    </div>
  );
}

function MixBar({ entry }: { entry: ProductMixEntry }) {
  const di = entry.deficit_index;
  // Width: explicit share if present, else deficit magnitude.
  const widthPct =
    entry.pct != null
      ? Math.max(0, Math.min(100, entry.pct))
      : di != null
        ? deficitBarWidthPct(di)
        : 0;
  const variant: BadgeVariant = di != null ? deficitVariant(di) : "neutral";
  const balanced = di != null && Math.abs(di) < DEFICIT_BALANCE_EPS;

  // Right-hand value: share % takes priority, else the signed deficit index.
  const valueStr =
    entry.pct != null
      ? `${fmtNum(entry.pct, entry.pct % 1 === 0 ? 0 : 1)}%`
      : di != null
        ? `${di > 0 ? "+" : ""}${fmtNum(di, 2)}`
        : "—";

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 3 }}>
      <div
        style={{
          display: "flex",
          alignItems: "baseline",
          justifyContent: "space-between",
          gap: 12,
        }}
      >
        <span
          style={{
            fontSize: 13,
            color: "var(--fg-primary)",
            minWidth: 0,
            overflow: "hidden",
            textOverflow: "ellipsis",
            whiteSpace: "nowrap",
          }}
        >
          {mixLabel(entry)}
        </span>
        <span
          style={{
            fontSize: 12,
            color: "var(--fg-secondary)",
            fontVariantNumeric: "tabular-nums",
            flexShrink: 0,
          }}
        >
          {valueStr}
          {di != null && entry.pct == null && (
            <span style={{ marginLeft: 6, color: "var(--fg-tertiary)" }}>
              {balanced ? "баланс" : deficitWord(di)}
            </span>
          )}
        </span>
      </div>
      <div
        style={{
          position: "relative",
          height: 8,
          borderRadius: 6,
          background: "var(--bg-card-alt)",
          border: "1px solid var(--border-soft)",
          overflow: "hidden",
        }}
        role="img"
        aria-label={`${mixLabel(entry)}: ${valueStr}`}
      >
        <div
          style={{
            position: "absolute",
            insetBlock: 0,
            insetInlineStart: 0,
            width: `${widthPct}%`,
            background: BAR_FILL[variant],
            borderRadius: 6,
          }}
        />
      </div>
    </div>
  );
}

// ── Коммерция ─────────────────────────────────────────────────────────────────

function CommercialSignal({
  commercial,
}: {
  commercial: ProductCommercial | null;
}) {
  if (
    commercial == null ||
    (commercial.available == null && !commercial.caveat)
  ) {
    return null;
  }
  const available = commercial.available === true;
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
      <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
        <span style={SUBHEAD_STYLE}>Коммерция</span>
        {commercial.available != null && (
          <Badge variant={available ? "success" : "neutral"} size="sm">
            {available ? "есть сигнал" : "нет данных"}
          </Badge>
        )}
      </div>
      {commercial.caveat && (
        <p
          style={{
            margin: 0,
            fontSize: 13,
            lineHeight: 1.5,
            color: "var(--fg-secondary)",
          }}
        >
          {capitalize(commercial.caveat)}
        </p>
      )}
    </div>
  );
}

// ── USP-ниши ──────────────────────────────────────────────────────────────────

function UspList({ usp }: { usp: ProductUsp[] }) {
  const items = usp.filter((u) => !!u.usp_text || !!u.segment);
  if (items.length === 0) return null;

  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
      <span style={SUBHEAD_STYLE}>УТП · ниши по дефициту ({items.length})</span>
      <ul
        style={{
          margin: 0,
          padding: 0,
          listStyle: "none",
          display: "flex",
          flexDirection: "column",
          gap: 6,
        }}
      >
        {items.map((u, i) => (
          <li
            key={`${u.segment ?? "?"}-${u.obj_class ?? "?"}-${i}`}
            style={{
              display: "flex",
              alignItems: "flex-start",
              gap: 8,
              padding: "8px 12px",
              background: "var(--bg-card-alt)",
              border: "1px solid var(--border-soft)",
              borderRadius: 8,
            }}
          >
            <span
              aria-hidden
              style={{
                marginTop: 7,
                width: 6,
                height: 6,
                borderRadius: "50%",
                background: "var(--accent)",
                flexShrink: 0,
              }}
            />
            <div style={{ flex: 1, minWidth: 0 }}>
              <div
                style={{
                  fontSize: 13,
                  lineHeight: 1.5,
                  color: "var(--fg-primary)",
                }}
              >
                {u.usp_text ?? uspFallbackText(u)}
              </div>
              {u.deficit_index != null && (
                <div
                  style={{
                    fontSize: 12,
                    color: "var(--fg-tertiary)",
                    marginTop: 2,
                    fontVariantNumeric: "tabular-nums",
                  }}
                >
                  Индекс дефицита {u.deficit_index > 0 ? "+" : ""}
                  {fmtNum(u.deficit_index, 2)}
                </div>
              )}
            </div>
          </li>
        ))}
      </ul>
    </div>
  );
}

function uspFallbackText(u: ProductUsp): string {
  const seg = u.segment ?? "формат";
  return u.obj_class ? `Ниша: ${seg} (${u.obj_class}).` : `Ниша: ${seg}.`;
}

// ── §16 обоснование (раскрываемое) ────────────────────────────────────────────

function ReasonsDisclosure({ reasons }: { reasons: ProductReason[] }) {
  const items = reasons.filter(
    (r) =>
      !!r.why ||
      (r.rejected?.length ?? 0) > 0 ||
      (r.what_would_change?.length ?? 0) > 0,
  );
  if (items.length === 0) return null;

  return (
    <details
      style={{
        borderTop: "1px solid var(--border-soft)",
        paddingTop: 12,
      }}
    >
      <summary
        style={{
          cursor: "pointer",
          fontSize: 12,
          fontWeight: 500,
          letterSpacing: "0.04em",
          textTransform: "uppercase",
          color: "var(--fg-secondary)",
          listStyle: "none",
        }}
      >
        Обоснование рекомендации
      </summary>
      <div
        style={{
          display: "flex",
          flexDirection: "column",
          gap: 12,
          marginTop: 12,
        }}
      >
        {items.map((r, i) => (
          <ReasonCard key={i} reason={r} />
        ))}
      </div>
    </details>
  );
}

function ReasonCard({ reason }: { reason: ProductReason }) {
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
      {reason.why && (
        <p
          style={{
            margin: 0,
            fontSize: 13,
            lineHeight: 1.5,
            color: "var(--fg-primary)",
          }}
        >
          {reason.why}
        </p>
      )}

      {/* «Отвергнутые альтернативы» подняты на уровень блока (#1742) —
          здесь не дублируем. */}

      {reason.what_would_change && reason.what_would_change.length > 0 && (
        <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
          <span style={SUBHEAD_STYLE}>Что изменит вывод</span>
          <ul
            style={{
              margin: 0,
              paddingInlineStart: 18,
              display: "flex",
              flexDirection: "column",
              gap: 3,
            }}
          >
            {reason.what_would_change.map((w, i) => (
              <li
                key={i}
                style={{
                  fontSize: 12,
                  lineHeight: 1.5,
                  color: "var(--fg-secondary)",
                }}
              >
                {w}
              </li>
            ))}
          </ul>
        </div>
      )}
    </div>
  );
}

// ── helpers ───────────────────────────────────────────────────────────────────

function capitalize(text: string): string {
  return text.charAt(0).toUpperCase() + text.slice(1);
}
