/**
 * source-registry — единый источник правды по источникам данных «Меры» (trade-in).
 *
 * КОНТРАКТ ЧЕСТНОСТИ (#2211, класс #1968 — «долг честности отрастает без инвариантов»):
 *   1. Здесь НЕТ и не должно появляться хардкод-массива «источников, которые якобы
 *      участвовали в оценке». Любой ростер/чип/лейбл/знаменатель в UI выводится ЛИБО
 *      из фактического `estimate.sources_used` (что реально дало данные), ЛИБО из
 *      `SOURCES` ниже (справочник известных источников для лейблов/цветов).
 *   2. Знаменатели вида «N/6», «N/5» НЕ хардкодятся числом. Если нужен знаменатель
 *      источников — берётся из `LIVE_SOURCE_COUNT` (derived), а не из литерала.
 *   3. Пустой `sources_used` → честный текст «источники не зафиксированы», НЕ подстановка
 *      фейковых чипов avito/cian/yandex/rosreestr.
 *
 * Канон набора — по backend `estimator.py::_canonical_sources`:
 *   sources_used = {листинговые источники, реально давшие аналоги}
 *                ∪ {'rosreestr' — сделки ДКП}
 *                ∪ {оценочные: 'avito_imv' | 'yandex_valuation' | 'cian_valuation'}.
 *   n1 полностью выключен (#2204): удалён из справочника. Исторические строки из БД с
 *   `source='n1'` могут прийти в placement history — они безопасно проходят через
 *   fallback `sourceLabel`/`sourceDot` (лейбл = сырой id, точка = нейтральный `dom`),
 *   код не падает на неизвестном источнике.
 */

export type SourceKind = "listing" | "deals" | "valuation";

export interface SourceMeta {
  /** Идентификатор как в `estimate.sources_used` / `analog.source`. */
  readonly id: string;
  /** Краткий человекочитаемый лейбл для чипов. */
  readonly label: string;
  /** CSS-класс цветной точки — `.src-dot.<dot>` в trade-in.css. */
  readonly dot: string;
  /** Тип вклада: объявления / сделки / внешняя оценка. */
  readonly kind: SourceKind;
}

/**
 * Справочник известных (активных) источников. `as const satisfies` фиксирует форму на
 * уровне типов: массив readonly, добавить «фейковый» источник или сломать поле не даст TS.
 * Cоответствие CSS-классам `.src-dot.*` (trade-in.css) проверено вручную — для
 * rosreestr валиден класс `rosreestr` (не мифический `ros`, у которого нет стиля).
 */
export const SOURCES = [
  { id: "avito", label: "Avito", dot: "avito", kind: "listing" },
  { id: "cian", label: "Циан", dot: "cian", kind: "listing" },
  { id: "yandex", label: "Я.Недвижимость", dot: "yandex", kind: "listing" },
  { id: "domklik", label: "ДомКлик", dot: "dom", kind: "listing" },
  { id: "rosreestr", label: "Росреестр", dot: "rosreestr", kind: "deals" },
  { id: "etagi", label: "Этажи", dot: "etagi", kind: "deals" },
  { id: "avito_imv", label: "Avito Оценка", dot: "avito", kind: "valuation" },
  { id: "yandex_valuation", label: "Я.Оценка", dot: "yandex", kind: "valuation" },
  { id: "cian_valuation", label: "Циан Оценка", dot: "cian", kind: "valuation" },
] as const satisfies readonly SourceMeta[];

const BY_ID: ReadonlyMap<string, SourceMeta> = new Map(SOURCES.map((s) => [s.id, s]));

/** Листинговые источники (объявления) — для ростера прогресса сбора. */
export const LIVE_LISTING_SOURCES: readonly SourceMeta[] = SOURCES.filter(
  (s) => s.kind === "listing",
);

/**
 * Знаменатель источников: листинговые + сделки (rosreestr / etagi).
 * Derived из реестра — заменяет хардкод «/6» / «/5» в компонентах.
 */
export const LIVE_SOURCE_COUNT: number = SOURCES.filter(
  (s) => s.kind === "listing" || s.kind === "deals",
).length;

/** Известен ли источник справочнику. */
export function isKnownSource(id: string): boolean {
  return BY_ID.has(id);
}

/** Лейбл источника; неизвестный id возвращается как есть (честно, без выдумки). */
export function sourceLabel(id: string): string {
  return BY_ID.get(id)?.label ?? id;
}

/** CSS-класс точки; неизвестный id → нейтральный `dom`. */
export function sourceDot(id: string): string {
  return BY_ID.get(id)?.dot ?? "dom";
}
