"""Pydantic-схемы страницы «доля квартир дома в продаже» (мигр. 143).

Источник данных — view v_building_sale_share + базовая таблица listings.
Список домов вторичного рынка, где active_secondary / flat_count_effective >= threshold%.
"""

from __future__ import annotations

from datetime import date

from pydantic import BaseModel, ConfigDict


class BuildingSaleShare(BaseModel):
    """Один дом из v_building_sale_share для списка /buildings/sale-share."""

    model_config = ConfigDict(from_attributes=True)

    house_id: int
    address: str | None = None
    lat: float | None = None
    lon: float | None = None

    sale_share_pct: float | None = None
    # 2-й процент: distinct vtorichka за последние 45д (активные ИЛИ недавно
    # закрытые) / знаменатель. Дом проходит фильтр по ЛЮБОМУ из двух процентов.
    sale_share_pct_45d: float | None = None
    # implausible-матч (числитель > знаменателя по любому из %) — скорее всего
    # коллизия адреса при ГАР-матче; не прячем, а маркируем для UI.
    over_100: bool = False

    active_secondary: int | None = None
    # distinct vtorichka-листинги (активные ИЛИ закрытые) за окно 45д.
    listings_45d: int | None = None
    flat_count_effective: int | None = None
    gar_match_method: str | None = None

    median_price_rub: int | None = None
    median_price_per_m2: int | None = None
    avg_days_on_market: float | None = None

    year_built: int | None = None
    house_type: str | None = None
    total_floors: int | None = None
    series_name: str | None = None
    is_emergency: bool | None = None


class BuildingListing(BaseModel):
    """Активное вторичное объявление в доме (click-through drawer)."""

    model_config = ConfigDict(from_attributes=True)

    listing_id: int
    source: str
    source_url: str | None = None
    price_rub: int | None = None
    price_per_m2: int | None = None
    rooms: int | None = None
    area_m2: float | None = None
    floor: int | None = None
    total_floors: int | None = None
    days_on_market: int | None = None
    listing_date: date | None = None
    address: str | None = None
    # Площадки, где видна эта физическая квартира (дедуп кросс-площадочных дублей
    # по сигнатуре (rooms, round(area_m2), floor) — мигр.148). Представитель строки —
    # самый дешёвый листинг; sources перечисляет все источники той же квартиры.
    sources: list[str] | None = None


class HistogramBucket(BaseModel):
    """Корзина гистограммы распределения sale_share_pct."""

    bucket: str
    count: int


class SaleShareSummary(BaseModel):
    """Сводка для слайдера % + баннера покрытия (/buildings/sale-share/summary).

    buildings_with_denominator/coverage считают дома с непустым ЛЮБЫМ из двух %.
    max/p95 и histogram — отдельно для каждого процента (now + 45д).
    """

    total_secondary_buildings: int
    buildings_with_denominator: int
    coverage_pct: float
    max_pct: float | None = None
    p95_pct: float | None = None
    max_pct_45d: float | None = None
    p95_pct_45d: float | None = None
    histogram: list[HistogramBucket]
    histogram_45d: list[HistogramBucket]
