"""Celery task: пересчёт district-level индексов локаций → `location` (#948 Part B, §8.2).

Прогоняет refresh_locations (app/services/site_finder/locations.py): итерирует 8
админ-районов ЕКБ, для каждого считает индексы инфра/конкуренция/спрос/будущее-
предложение (reuse per-district forecast-функций, #1129-кэшированы) и апсертит в
`location`. ON CONFLICT (district_name) делает прогон идемпотентным.

Расписание — еженедельно (district-индексы меняются медленно: рынок поглощает сток
неделями, будущее предложение — слой supply-layers, который сам обновляется
еженедельно по понедельникам). Регистрируется в beat_schedule.py ПОСЛЕ supply-layers
(Mon 06:00) — чтобы future_supply-индекс считался по СВЕЖЕМУ складу предложения.

Детерминированно, без LLM. SAVEPOINT per-row внутри refresh_locations (backend.md):
сбойный район не валит остальные.

Mirror conventions (cbr_macro_sync / supply_layers_refresh): SessionLocal() +
try/finally close, logger (не print), задача техническая — не управляется через
job_settings, добавляется в beat_schedule hardcoded-блок.
"""

from __future__ import annotations

import logging
from typing import Any

from app.core.db import SessionLocal
from app.services.site_finder.locations import refresh_locations
from app.workers.celery_app import celery_app

logger = logging.getLogger(__name__)


@celery_app.task(
    bind=True,
    name="tasks.location_refresh.location_refresh",
    max_retries=2,
)
def location_refresh(self: Any, region: str | None = None) -> dict[str, Any]:
    """Пересчитать + upsert-нуть district-level индексы по всем районам в `location`.

    Идемпотентно (ON CONFLICT по district_name). Graceful: сбойный район
    откатывается изолированно (SAVEPOINT), не роняя прогон.

    Args:
        region: метка региона в колонку location.region (None = ЕКБ/Свердл.).

    Returns:
        Счётчики refresh_locations: {"districts": N, "upserted": M, "failed": K}.
    """
    db = SessionLocal()
    try:
        result = refresh_locations(db, region=region)
        logger.info(
            "location_refresh: districts=%s upserted=%s failed=%s",
            result["districts"],
            result["upserted"],
            result["failed"],
        )
        return result
    finally:
        db.close()
