"""Pydantic-модели для входных параметров и выходных результатов инструментов."""

from datetime import date
from typing import Literal

from pydantic import BaseModel, Field


class PaginationParams(BaseModel):
    """Базовая пагинация."""

    limit: int = Field(
        default=20,
        ge=1,
        le=100,
        description=(
            "Количество результатов на странице. "
            "Если total > limit, используй offset+=limit для следующей страницы."
        ),
    )
    offset: int = Field(
        default=0,
        ge=0,
        description="Смещение для пагинации. Следующая страница: offset += limit.",
    )


class MetadataFilters(BaseModel):
    """Фильтры по метаданным, общие для всех инструментов поиска.

    Все фильтры объединяются через AND. Если результатов мало — попробуй убрать
    1–2 фильтра (обычно district или metro_station).
    """

    deal_type: Literal["sale", "rent_long", "rent_short"] | None = Field(
        default=None,
        description=(
            "Тип сделки: sale=продажа, rent_long=долгосрочная аренда, "
            "rent_short=посуточная"
        ),
    )
    city: str | None = Field(
        default=None,
        description=(
            "Город на украинском языке. Поиск по подстроке (ILIKE) — "
            "достаточно части названия, например 'Київ' или 'Львів'. Регистр не важен."
        ),
    )
    district: str | None = Field(
        default=None,
        description=(
            "Район на украинском языке. Поиск по подстроке (ILIKE). "
            "Примеры: 'Печерський', 'Шевченківський', 'Галицький'."
        ),
    )
    micro_district: str | None = Field(
        default=None,
        description="Микрорайон. Поиск по подстроке (ILIKE).",
    )
    street: str | None = Field(
        default=None,
        description="Улица. Поиск по подстроке (ILIKE). Пример: 'вул. Шевченка'.",
    )
    rooms_count: int | None = Field(
        default=None,
        ge=0,
        description="Количество комнат. 0 = студия, 1 = 1-комнатная и т.д.",
    )
    bedrooms_count: int | None = Field(
        default=None,
        ge=0,
        description="Количество спален.",
    )
    bathrooms_count: int | None = Field(
        default=None,
        ge=0,
        description="Количество ванных комнат.",
    )
    min_price: float | None = Field(
        default=None,
        ge=0,
        description=(
            "Минимальная цена. Обязательно указывай currency вместе с min_price/max_price, "
            "иначе фильтр применится ко всем валютам одновременно."
        ),
    )
    max_price: float | None = Field(
        default=None,
        ge=0,
        description="Максимальная цена. Указывай вместе с currency.",
    )
    currency: Literal["USD", "EUR", "UAH"] | None = Field(
        default=None,
        description="Валюта цены. Обязательна при использовании min_price/max_price.",
    )
    min_total_area: float | None = Field(
        default=None,
        ge=0,
        description="Минимальная общая площадь, м².",
    )
    max_total_area: float | None = Field(
        default=None,
        ge=0,
        description="Максимальная общая площадь, м².",
    )
    building_type: Literal["brick", "panel", "monolith", "gas_block", "wood"] | None = Field(
        default=None,
        description=(
            "Тип постройки: brick=кирпичный, panel=панельный, monolith=монолитный, "
            "gas_block=газоблок, wood=деревянный."
        ),
    )
    floor: int | None = Field(
        default=None,
        ge=0,
        description="Этаж квартиры. 0 обычно означает цоколь/подвал (если есть в базе).",
    )
    floors_total: int | None = Field(
        default=None,
        ge=0,
        description="Общее количество этажей в здании.",
    )
    listing_status: Literal["active", "sold", "rented", "removed", "archived"] | None = Field(
        default=None,
        description="Статус объявления. По умолчанию сервер не фильтрует — включая все статусы.",
    )
    metro_station: str | None = Field(
        default=None,
        description=(
            "Станция метро на украинском. Поиск по подстроке (ILIKE). "
            "Примеры: 'Арсенальна', 'Театральна', 'Площа Ринок'. "
            "Регистр не важен."
        ),
    )
    metro_distance_type: Literal["walk", "transport"] | None = Field(
        default=None,
        description="Удалённость от метро: walk=пешком, transport=на транспорте.",
    )
    metro_distance_min: int | None = Field(
        default=None,
        ge=0,
        description="Время до метро в минутах.",
    )
    layout: Literal["studio", "separate", "adjacent"] | None = Field(
        default=None,
        description="Тип планировки: studio=студия, separate=раздельная, adjacent=смежная.",
    )
    renovation_status: (
        Literal["cosmetic", "euro", "designer", "none", "construction"] | None
    ) = Field(
        default=None,
        description="Состояние ремонта.",
    )
    bathroom_type: Literal["combined", "separate", "multiple"] | None = Field(
        default=None,
        description="Тип санузла: combined=совмещённый, separate=раздельный, multiple=несколько.",
    )
    parking_type: Literal["ground", "underground", "none", "garage"] | None = Field(
        default=None,
        description="Тип парковки.",
    )
    heating_type: Literal["central", "autonomous", "floor", "none"] | None = Field(
        default=None,
        description=(
            "Тип отопления: central=центральное, autonomous=автономное, "
            "floor=подогрев полов, none=нет."
        ),
    )
    window_view: Literal["yard", "street", "park", "water", "forest"] | None = Field(
        default=None,
        description="Вид из окон.",
    )
    has_balcony: bool | None = Field(
        default=None,
        description="Наличие балкона.",
    )
    has_loggia: bool | None = Field(
        default=None,
        description="Наличие лоджии.",
    )


class SearchSimilarInput(BaseModel):
    """Входные параметры для векторного поиска.

    Используй, когда пользователь описывает желания, атмосферу или качества
    («уютная квартира с ремонтом у метро»), а не конкретные ключевые слова.
    """

    query: str = Field(
        ...,
        description=(
            "Текстовый запрос на украинском языке для поиска по смыслу. "
            "Формулируй конкретно, без разговорных стоп-слов. "
            "✓ Хорошо: '2-кімнатна квартира біля метро з ремонтом'. "
            "✗ Плохо: 'дай квартиру недорого' (стоп-слова)."
        ),
    )
    filters: MetadataFilters = Field(default_factory=MetadataFilters)
    pagination: PaginationParams = Field(default_factory=PaginationParams)
    # min_similarity зашит в код всегда 0.7 (широкий поиск)


class SearchMetadataInput(BaseModel):
    """Входные параметры для поиска по метаданным/FTS.

    Используй, когда пользователь называет конкретные ключевые слова:
    район, станция метро, улица, особенности ('Печерський район метро Арсенальна').
    """

    query: str = Field(
        ...,
        description=(
            "Текстовый запрос на украинском языке для полнотекстового поиска. "
            "Подходит для конкретных названий и ключевых слов. "
            "✓ Хорошо: 'Печерський район Арсенальна метро'. "
            "✗ Плохо: 'хочу квартиру' (слишком общее, используй search_similar)."
        ),
    )
    filters: MetadataFilters = Field(default_factory=MetadataFilters)
    pagination: PaginationParams = Field(default_factory=PaginationParams)
    sort_by: Literal[
        "relevance",
        "price_asc",
        "price_desc",
        "date_desc",
        "area_desc",
    ] = Field(
        default="relevance",
        description=(
            "Сортировка результатов: relevance=по релевантности FTS (умолч.), "
            "price_asc=цена по возрастанию, price_desc=цена по убыванию, "
            "date_desc=новые сверху, area_desc=большая площадь сверху."
        ),
    )


class GetListingInput(BaseModel):
    """Входные параметры для получения объявления по ID."""

    listing_id: int = Field(..., ge=1, description="ID объявления (положительное целое число).")


class ListingResult(BaseModel):
    """Результат поиска — одно объявление."""

    id: int
    title: str
    description: str | None = None
    generated_description: str | None = None
    price: float | None = None
    currency: str | None = None
    original_price: float | None = None
    original_currency: str | None = None
    price_per_sqm: float | None = None
    deal_type: str | None = None
    city: str | None = None
    district: str | None = None
    micro_district: str | None = None
    street: str | None = None
    house_number: str | None = None
    address_raw: str | None = None
    rooms_count: int | None = None
    bedrooms_count: int | None = None
    bathrooms_count: int | None = None
    total_area: float | None = None
    living_area: float | None = None
    kitchen_area: float | None = None
    land_area: float | None = None
    floor: int | None = None
    floors_total: int | None = None
    building_type: str | None = None
    building_year: int | None = None
    renovation_status: str | None = None
    ceiling_height: float | None = None
    material: str | None = None
    balcony_count: int | None = None
    loggia_count: int | None = None
    has_balcony: bool | None = None
    has_loggia: bool | None = None
    bathroom_type: str | None = None
    elevator_count: int | None = None
    has_freight_elevator: bool | None = None
    parking_type: str | None = None
    heating_type: str | None = None
    internet: bool | None = None
    security: bool | None = None
    layout: str | None = None
    window_view: str | None = None
    windows_direction: str | None = None
    metro_station: str | None = None
    metro_distance_type: str | None = None
    metro_distance_min: int | None = None
    latitude: float | None = None
    longitude: float | None = None
    url_source: str | None = None
    publish_date: date | None = None
    images_count: int | None = None
    contact_phone: str | None = None
    contact_name: str | None = None
    contact_email: str | None = None
    is_agent: bool | None = None
    agency_name: str | None = None
    listing_status: str | None = None
    listing_quality_score: int | None = None
    reliability_rating: int | None = None
    sentiment_score: float | None = None
    archived_at: date | None = None
    created_at: date | None = None
    updated_at: date | None = None
    similarity_score: float | None = Field(
        default=None,
        description="Косинусная близость [0..1] для search_similar_listings. "
        "1.0 = максимально похоже, 0.0 = не похоже.",
    )
    rank_score: float | None = Field(
        default=None,
        description="Релевантность FTS (ts_rank_cd) для search_by_metadata. "
        "Чем выше, тем релевантнее.",
    )


class SearchResult(BaseModel):
    """Результат поиска — список объявлений + мета.

    Если total > limit и offset + limit < total — есть ещё результаты.
    Для следующей страницы: offset += limit.
    """

    total: int = Field(description="Общее количество найденных записей (без LIMIT).")
    limit: int
    offset: int
    listings: list[ListingResult]
