"use client";

/**
 * PticaCompare — ПТИЦА «Сравнение» tab (PR#3). Dark-styled, but REUSES the real
 * shortlist + compare data flow rather than inventing data:
 *
 *   • Shortlist persistence — compare-shortlist.ts (gd_compare_shortlist in
 *     localStorage, isValidCad, COMPARE_MAX). The CURRENT cad is auto-pinned and
 *     can't be removed; the rest are user-managed chips.
 *   • Per-cad metrics — ParcelCompareLoader (the SAME headless loader the
 *     /site-finder/compare page uses), which runs useParcelAnalyzeQuery per cad
 *     and lifts a flattened CompareColumn up. Cache is shared with the analysis
 *     page, so the current parcel is served warm.
 *
 * The table is a dark reimplementation (CompareTable ships light tokens) but
 * consumes the identical CompareColumn shape and the same winner-highlight +
 * verdict-colour logic. Embedded — not a link-fallback.
 */

import { useCallback, useEffect, useMemo, useState } from "react";
import { X } from "lucide-react";

import { ParcelCompareLoader } from "@/components/site-finder/compare/ParcelCompareLoader";
import type { CompareColumn } from "@/components/site-finder/compare/CompareTable";
import {
  COMPARE_MAX,
  getShortlist,
  isValidCad,
  saveShortlist,
} from "@/lib/compare-shortlist";
import type { GateVerdictLabel } from "@/types/site-finder";
import styles from "@/app/site-finder/analysis/[cad]/ptica/ptica.module.css";

interface Props {
  cad: string;
}

// ── Metric-row model (mirrors compare/CompareTable winner logic) ──────────────

type WinnerDir = "high" | "low" | "none";

interface MetricRow {
  key: string;
  label: string;
  winner: WinnerDir;
  value: (c: CompareColumn) => number | null;
  render: (c: CompareColumn) => React.ReactNode;
}

const DASH = "—";

const CONFIDENCE_RU: Record<"high" | "medium" | "low", string> = {
  high: "высокая",
  medium: "средняя",
  low: "низкая",
};

// Verdict → semantic colour class (green/amber/red like the prototype).
const VERDICT_CLASS: Record<GateVerdictLabel, string> = {
  Можно: styles.verdictBuy,
  Нельзя: styles.verdictSkip,
  "С ограничениями": styles.verdictWatch,
  "Нужна проверка": styles.verdictWatch,
};

function fmtScore(v: number | null | undefined): string {
  return v != null && Number.isFinite(v) ? v.toFixed(1) : DASH;
}

function fmtPrice(v: number | null | undefined): string {
  return v != null && Number.isFinite(v)
    ? `${Math.round(v).toLocaleString("ru")} ₽`
    : DASH;
}

function fmtPipeline(v: number | null | undefined): string {
  return v != null && Number.isFinite(v)
    ? `${Math.round(v).toLocaleString("ru")} кв.`
    : DASH;
}

const METRIC_ROWS: MetricRow[] = [
  {
    key: "verdict",
    label: "Вердикт МКД",
    winner: "none",
    value: () => null,
    render: (c) =>
      c.verdictLabel ? (
        <span className={VERDICT_CLASS[c.verdictLabel]}>{c.verdictLabel}</span>
      ) : (
        DASH
      ),
  },
  {
    key: "score",
    label: "Инвест-балл",
    winner: "high",
    value: (c) => c.score ?? null,
    render: (c) => (
      <>
        {fmtScore(c.score)}
        {c.scoreLabel ? (
          <span className={styles.cellMuted}> · {c.scoreLabel}</span>
        ) : null}
      </>
    ),
  },
  {
    key: "velocity",
    label: "Скорость продаж",
    winner: "high",
    // velocity_score=0 при velocity_data_available=false — это «нет данных»,
    // а не реальный 0 → исключаем из подсветки и рендерим «—».
    value: (c) =>
      c.velocityDataAvailable === false ? null : (c.velocityScore ?? null),
    render: (c) =>
      c.velocityDataAvailable !== false &&
      c.velocityScore != null &&
      Number.isFinite(c.velocityScore)
        ? `${Math.round(c.velocityScore * 100)}%`
        : DASH,
  },
  {
    key: "price",
    label: "Медиана ₽ / м²",
    // Дешевле ≠ лучше для девелопера — валентность неоднозначна, не подсвечиваем.
    winner: "none",
    value: (c) => c.medianPricePerM2 ?? null,
    render: (c) => fmtPrice(c.medianPricePerM2),
  },
  {
    key: "pipeline",
    label: "Пайплайн 24 мес",
    // Больше конкурирующего предложения → хуже. Меньше выигрывает.
    winner: "low",
    value: (c) => c.pipeline24mo ?? null,
    render: (c) => fmtPipeline(c.pipeline24mo),
  },
  {
    key: "confidence",
    label: "Достоверность",
    winner: "high",
    value: (c) => c.confidenceValue ?? null,
    render: (c) =>
      c.confidenceLabel ? (
        <>
          {c.confidenceValue != null
            ? `${Math.round(c.confidenceValue * 100)}% · `
            : ""}
          {CONFIDENCE_RU[c.confidenceLabel]}
        </>
      ) : (
        DASH
      ),
  },
];

/** Cads that win a comparable row (ties win jointly); empty when not comparable. */
function winnersFor(row: MetricRow, columns: CompareColumn[]): Set<string> {
  if (row.winner === "none") return new Set();
  const vals = columns
    .map((c) => ({ cad: c.cad, v: row.value(c) }))
    .filter((x): x is { cad: string; v: number } => x.v != null);
  if (vals.length < 2) return new Set();
  const best =
    row.winner === "high"
      ? Math.max(...vals.map((x) => x.v))
      : Math.min(...vals.map((x) => x.v));
  return new Set(vals.filter((x) => x.v === best).map((x) => x.cad));
}

// Short tail of a cad for compact column headers («…0204016:10»).
function shortCad(cad: string): string {
  const parts = cad.split(":");
  return parts.length > 2 ? `…${parts.slice(2).join(":")}` : cad;
}

// ── Component ─────────────────────────────────────────────────────────────────

export function PticaCompare({ cad }: Props) {
  // Extra cads (beyond the pinned current one), hydrated from localStorage after
  // mount (SSR-safe: first client render matches the server's empty list).
  const [extras, setExtras] = useState<string[]>([]);
  const [hydrated, setHydrated] = useState(false);
  const [input, setInput] = useState("");
  const [inputError, setInputError] = useState<string | null>(null);
  const [columns, setColumns] = useState<Record<string, CompareColumn>>({});

  useEffect(() => {
    // The current cad is always pinned first; the shortlist supplies the rest.
    setExtras(getShortlist().filter((c) => c !== cad));
    setHydrated(true);
  }, [cad]);

  // Persist the union (current pinned + extras) so it survives navigation, but
  // only after hydration so the initial empty state never clobbers a saved list.
  useEffect(() => {
    if (hydrated) saveShortlist([cad, ...extras]);
  }, [cad, extras, hydrated]);

  const handleResult = useCallback((col: CompareColumn) => {
    setColumns((prev) => {
      const existing = prev[col.cad];
      if (
        existing &&
        existing.status === col.status &&
        existing.errorMsg === col.errorMsg &&
        existing.districtName === col.districtName &&
        existing.verdictLabel === col.verdictLabel &&
        existing.score === col.score &&
        existing.scoreLabel === col.scoreLabel &&
        existing.velocityScore === col.velocityScore &&
        existing.velocityDataAvailable === col.velocityDataAvailable &&
        existing.medianPricePerM2 === col.medianPricePerM2 &&
        existing.pipeline24mo === col.pipeline24mo &&
        existing.confidenceLabel === col.confidenceLabel &&
        existing.confidenceValue === col.confidenceValue
      ) {
        return prev;
      }
      return { ...prev, [col.cad]: col };
    });
  }, []);

  // The pinned current cad counts toward COMPARE_MAX.
  const atMax = 1 + extras.length >= COMPARE_MAX;

  function addCad(e: React.FormEvent) {
    e.preventDefault();
    const next = input.trim();
    if (!isValidCad(next)) {
      setInputError("Неверный формат. Ожидается 66:41:0204016:10.");
      return;
    }
    if (next === cad || extras.includes(next)) {
      setInputError("Этот участок уже в сравнении.");
      return;
    }
    if (atMax) {
      setInputError(`Можно сравнить не более ${COMPARE_MAX} участков.`);
      return;
    }
    setExtras((prev) => [...prev, next]);
    setInput("");
    setInputError(null);
  }

  const removeCad = useCallback((target: string) => {
    setExtras((prev) => prev.filter((c) => c !== target));
    setColumns((prev) => {
      const nextCols = { ...prev };
      delete nextCols[target];
      return nextCols;
    });
  }, []);

  // Ordered cads: pinned current first, then the user-added extras.
  const allCads = useMemo(() => [cad, ...extras], [cad, extras]);

  const orderedColumns: CompareColumn[] = useMemo(
    () =>
      allCads.map((c) => columns[c] ?? { cad: c, status: "loading" as const }),
    [allCads, columns],
  );

  const winnerByRow = useMemo(
    () =>
      new Map<string, Set<string>>(
        METRIC_ROWS.map((row) => [row.key, winnersFor(row, orderedColumns)]),
      ),
    [orderedColumns],
  );

  return (
    <div className={styles.compareRoot}>
      <div className={styles.viewHead}>
        <h2>
          Сравнение участков
          <span className={styles.viewSub}>
            shortlist · до {COMPARE_MAX} объектов · COMPARE
          </span>
        </h2>
      </div>

      {/* Headless loaders — one per cad, lift analyze metrics up (real flow). */}
      {allCads.map((c) => (
        <ParcelCompareLoader key={c} cad={c} onResult={handleResult} />
      ))}

      {/* ── Add bar + chips ──────────────────────────────────────────────── */}
      <div className={styles.panel}>
        <form className={styles.compareBar} onSubmit={addCad}>
          <input
            type="text"
            value={input}
            onChange={(e) => {
              setInput(e.target.value);
              setInputError(null);
            }}
            placeholder="Добавить кадастровый номер · 66:41:…"
            aria-label="Кадастровый номер для сравнения"
            disabled={atMax}
            className={styles.compareInput}
          />
          <button
            type="submit"
            disabled={!input.trim() || atMax}
            className={styles.compareAddBtn}
          >
            Добавить
          </button>

          <div className={styles.chipRow}>
            <span className={`${styles.chip} ${styles.chipPinned}`}>
              <span className="mono">{cad}</span>
              <span className={styles.chipTag}>текущий</span>
            </span>
            {extras.map((c) => (
              <span key={c} className={styles.chip}>
                <span className="mono">{c}</span>
                <button
                  type="button"
                  onClick={() => removeCad(c)}
                  aria-label={`Убрать ${c} из сравнения`}
                  className={styles.chipRemove}
                >
                  <X size={11} strokeWidth={1.6} />
                </button>
              </span>
            ))}
          </div>
        </form>
        {inputError && (
          <p className={styles.compareInputError} role="alert">
            {inputError}
          </p>
        )}
      </div>

      {/* ── Comparison table ─────────────────────────────────────────────── */}
      <div className={`${styles.panel} ${styles.compareTableWrap}`}>
        {extras.length === 0 ? (
          <p className={styles.compareHint}>
            Добавьте ещё участки, чтобы поставить метрики рядом. Лучшее значение
            в строке подсвечивается; список сохраняется между сессиями.
          </p>
        ) : (
          <table className={styles.compareTable}>
            <thead>
              <tr>
                <th>Показатель</th>
                {orderedColumns.map((c) => (
                  <th key={c.cad} title={c.cad}>
                    <span className="mono">{shortCad(c.cad)}</span>
                    {c.districtName && c.districtName !== DASH ? (
                      <span className={styles.compareDistrict}>
                        {c.districtName}
                      </span>
                    ) : null}
                  </th>
                ))}
              </tr>
            </thead>
            <tbody>
              {METRIC_ROWS.map((row) => {
                const winners = winnerByRow.get(row.key) ?? new Set<string>();
                const isVerdict = row.key === "verdict";
                return (
                  <tr
                    key={row.key}
                    className={isVerdict ? styles.compareVerdictRow : undefined}
                  >
                    <td>{row.label}</td>
                    {orderedColumns.map((c) => {
                      const best = winners.has(c.cad);
                      return (
                        <td
                          key={c.cad}
                          className={best ? styles.compareBest : undefined}
                        >
                          {c.status === "loading" ? (
                            <span className={styles.cellMuted}>…</span>
                          ) : c.status === "error" ? (
                            <span
                              className={styles.cellError}
                              title={c.errorMsg ?? "Ошибка анализа"}
                            >
                              ошибка
                            </span>
                          ) : (
                            row.render(c)
                          )}
                        </td>
                      );
                    })}
                  </tr>
                );
              })}
            </tbody>
          </table>
        )}
      </div>
    </div>
  );
}
