"""Smoke-тесты Celery-обвязки точек выхода газосети (#2119 Фаза B2, PR-4).

- sync_gas_outlet_points зарегистрирована (module в include-списке celery_app);
- beat содержит gas-outlet-points-sync-weekly (еженедельно, вторник);
- таск дважды дёргает лоадер: load_gas_outlet_points → geocode_gas_outlets(apply=True),
  каждый шаг graceful (сбой одного не роняет второй) — mock без сети/БД.
"""

from __future__ import annotations

from unittest.mock import patch


def test_task_registered_in_celery_include() -> None:
    """Модуль connection_capacity_sync в include-списке celery_app."""
    from app.workers.celery_app import celery_app

    assert "app.workers.tasks.connection_capacity_sync" in celery_app.conf.include


def test_beat_schedule_includes_gas_outlet_sync_weekly_tuesday() -> None:
    """gas-outlet-points-sync-weekly зарегистрирована еженедельно во вторник."""
    from app.workers import beat_schedule

    # build_beat_schedule читает DB → мокаем _build_beat_schedule_from_db,
    # чтобы тест не зависел от живого Postgres.
    with patch.object(beat_schedule, "_build_beat_schedule_from_db", return_value={}):
        schedule = beat_schedule.build_beat_schedule()

    assert (
        "gas-outlet-points-sync-weekly" in schedule
    ), f"gas-outlet-points-sync-weekly отсутствует в beat: {sorted(schedule.keys())}"
    entry = schedule["gas-outlet-points-sync-weekly"]
    assert entry["task"] == "tasks.connection_capacity_sync.sync_gas_outlet_points"
    # crontab day_of_week = вторник (2).
    assert 2 in entry["schedule"].day_of_week


def test_sync_gas_outlet_points_load_then_geocode() -> None:
    """Таск: load_gas_outlet_points → geocode_gas_outlets(apply=True), оба результата в ответе."""
    from app.workers.tasks import connection_capacity_sync as ccs

    load_result = {"rows": 1037, "inserted": 10, "updated": 1027}
    geo_result = {"candidates": 5, "geocoded": 3, "applied": True}
    with (
        patch(
            "app.services.site_finder.ekgas_outlet_loader.load_gas_outlet_points",
            return_value=load_result,
        ),
        patch(
            "app.services.site_finder.ekgas_outlet_loader.geocode_gas_outlets",
            return_value=geo_result,
        ) as mock_geo,
        patch.object(ccs, "_log_breadcrumb"),
    ):
        out = ccs.sync_gas_outlet_points()

    assert out["load"] == load_result
    assert out["geocode"] == geo_result
    # геокод вызван С apply=True (реальная запись geom на проде).
    assert mock_geo.call_args.kwargs.get("apply") is True


def test_sync_gas_outlet_points_geocode_failure_graceful() -> None:
    """Сбой geocode-шага не роняет таск: load-результат сохраняется, geocode → error-словарь."""
    from app.workers.tasks import connection_capacity_sync as ccs

    load_result = {"rows": 1037, "inserted": 0, "updated": 1037}
    with (
        patch(
            "app.services.site_finder.ekgas_outlet_loader.load_gas_outlet_points",
            return_value=load_result,
        ),
        patch(
            "app.services.site_finder.ekgas_outlet_loader.geocode_gas_outlets",
            side_effect=RuntimeError("DaData quota exhausted"),
        ),
        patch.object(ccs, "_log_breadcrumb"),
    ):
        out = ccs.sync_gas_outlet_points()

    assert out["load"] == load_result  # шаг 1 не откатывается сбоем шага 2
    assert "error" in out["geocode"]
    assert "DaData" in out["geocode"]["error"]


def test_sync_gas_outlet_points_load_failure_still_attempts_geocode() -> None:
    """Сбой load-шага не мешает попытке geocode (шаги независимы, каждый graceful)."""
    from app.workers.tasks import connection_capacity_sync as ccs

    geo_result = {"candidates": 0, "geocoded": 0, "applied": True}
    with (
        patch(
            "app.services.site_finder.ekgas_outlet_loader.load_gas_outlet_points",
            side_effect=RuntimeError("ekgas catalog 503"),
        ),
        patch(
            "app.services.site_finder.ekgas_outlet_loader.geocode_gas_outlets",
            return_value=geo_result,
        ),
        patch.object(ccs, "_log_breadcrumb"),
    ):
        out = ccs.sync_gas_outlet_points()

    assert "error" in out["load"]
    assert out["geocode"] == geo_result
