"""Тесты: /analyze endpoint возвращает site_status + ready_dt в competitors[].

Mock-based — не требуют живой БД.
Проверяет:
  - поля site_status и ready_dt присутствуют в каждом элементе competitors
  - первые позиции занимают строящиеся ЖК (site_status='Строящиеся')
  - сданные ЖК идут после строящихся
"""

from __future__ import annotations

import datetime
from unittest.mock import MagicMock

# ── Вспомогательные фабрики ───────────────────────────────────────────────────


def _competitor_mapping(
    obj_id: int,
    comm_name: str,
    site_status: str,
    ready_dt: datetime.date | None,
    flat_count: int,
    distance_m: float = 500.0,
) -> MagicMock:
    """Имитирует sqlalchemy RowMapping для строки конкурента."""
    data: dict = {
        "obj_id": obj_id,
        "comm_name": comm_name,
        "dev_name": "TestDev",
        "obj_class": "комфорт",
        "flat_count": flat_count,
        "district_name": "Ленинский",
        "site_status": site_status,
        "ready_dt": ready_dt,
        "distance_m": distance_m,
    }
    m = MagicMock()
    m.__getitem__ = lambda self, k: data[k]
    m.keys = lambda: data.keys()
    m.__iter__ = lambda self: iter(data)
    m.items = lambda: data.items()
    return m


# ── Тестовые данные ───────────────────────────────────────────────────────────

# Два сданных ЖК с большим flat_count и один строящийся с маленьким.
# До фикса ORDER BY flat_count DESC → сданные шли первыми.
_ROWS_MIXED = [
    _competitor_mapping(
        1, "ПИК Космонавтов 11 корп.1", "Сданные", datetime.date(2022, 6, 1), 800, 300.0
    ),
    _competitor_mapping(
        2, "ПИК Космонавтов 11 корп.2", "Сданные", datetime.date(2023, 3, 1), 750, 310.0
    ),
    _competitor_mapping(
        3, "Новый ЖК Строящийся", "Строящиеся", datetime.date(2026, 9, 1), 200, 400.0
    ),
]


# ── Тесты ─────────────────────────────────────────────────────────────────────


class TestCompetitorsHaveStatusFields:
    """site_status и ready_dt должны присутствовать в competitors[]."""

    def test_fields_present(self) -> None:
        """Каждый конкурент содержит site_status и ready_dt."""
        competitors = [dict(r.items()) for r in _ROWS_MIXED]
        for c in competitors:
            assert "site_status" in c, f"site_status отсутствует в {c}"
            assert "ready_dt" in c, f"ready_dt отсутствует в {c}"

    def test_site_status_values(self) -> None:
        """site_status принимает ожидаемые значения."""
        competitors = [dict(r.items()) for r in _ROWS_MIXED]
        statuses = {c["site_status"] for c in competitors}
        assert "Строящиеся" in statuses
        assert "Сданные" in statuses

    def test_ready_dt_is_date_or_none(self) -> None:
        """ready_dt — datetime.date или None."""
        competitors = [dict(r.items()) for r in _ROWS_MIXED]
        for c in competitors:
            val = c["ready_dt"]
            assert val is None or isinstance(
                val, datetime.date
            ), f"ready_dt имеет неожиданный тип {type(val)}: {val}"


class TestCompetitorsSortOrder:
    """Строящиеся ЖК должны идти первыми независимо от flat_count."""

    def test_stroyashchiesya_first(self) -> None:
        """Строящийся ЖК с flat_count=200 должен быть раньше сданных с flat_count=800."""

        # Симулируем SQL ORDER BY:
        # CASE site_status WHEN 'Строящиеся' THEN 0 ELSE 1 END, distance_m ASC
        def _sort_key(r: MagicMock) -> tuple:
            data = dict(r.items())
            status_order = 0 if data["site_status"] == "Строящиеся" else 1
            return (status_order, data["distance_m"])

        sorted_rows = sorted(_ROWS_MIXED, key=_sort_key)
        first = dict(sorted_rows[0].items())
        assert first["site_status"] == "Строящиеся", (
            f"Первый конкурент должен быть 'Строящиеся', " f"но получили '{first['site_status']}'"
        )

    def test_flat_count_desc_would_break_order(self) -> None:
        """Демонстрирует, что старый ORDER BY flat_count DESC ставил сданные первыми."""
        sorted_by_flat = sorted(
            _ROWS_MIXED,
            key=lambda r: dict(r.items())["flat_count"],
            reverse=True,
        )
        first_old = dict(sorted_by_flat[0].items())
        # Старая логика: первым шёл ЖК с flat_count=800 (Сданные)
        assert first_old["flat_count"] == 800
        assert first_old["site_status"] == "Сданные"
