-- 110_scrape_schedules_seed_geocode_missing_listings.sql
-- Seed row для nightly geocode backfill listings.lat/lon (все источники, все города).
--
-- Root cause (обнаружен 2026-06-16):
--   Scrapers (avito, yandex, n1) сохраняют поисковые карточки с lat=lon=NULL
--   в расчёте на то что «geocode-missing cron подтянет».
--   OS crontab геокодирует ТОЛЬКО rosreestr_deals — для listings такого cron нет.
--   На prod: 6959 avito + 590 yandex + 125 n1 = 7674 listings с null-geom при
--   ненулевом address; geocode_tried_at IS NULL (ни разу не пробовали с 2026-05-23).
--   Dry-run показал 85% geocode success (51/60) — wiring нотночного scheduler'а
--   материально поднимает геопокрытие.
--
-- Решение:
--   Вместо нового cron — wire существующую geocode_missing_listings в in-app scheduler.
--   Функция address-dedup (1 geocode call на уникальный адрес → UPDATE all listings).
--   Loop-safe: addresses помечаются geocode_tried_at=NOW() при failure → backoff 7 дней,
--   нет бесконечного переотбора.
--
-- Окно 06:00-09:00 UTC (09:00-12:00 МСК):
--   - После city sweeps (~03-04 UTC), которые depositing новые listings с null-geom.
--   - После deals OS-geocode cron (05:30 / 05:45 UTC) — снижаем Nominatim contention.
--   - До open-hours peaks на Nominatim (дневные задачи других систем).
--
-- default_params:
--   batch_size  — addresses за один вызов geocode_missing_listings (200 ≈ 3.5 мин Nominatim).
--   budget_sec  — wall-clock budget одного прогона; 1800 = 30 мин (fits в 3h window).
--
-- ЗАВИСИМОСТИ: 052_scrape_schedules.sql (таблица + UNIQUE(source)).
-- Idempotent: ON CONFLICT (source) DO NOTHING — безопасно запускать повторно.

BEGIN;

INSERT INTO scrape_schedules (
    source,
    enabled,
    window_start_hour,
    window_end_hour,
    next_run_at,
    default_params
)
VALUES
(
    'geocode_missing_listings',
    true,                                        -- ACTIVE: нет внешней зависимости (anti-bot),
                                                 -- только Nominatim rate-limited geocoder
    6,
    9,
    ((CURRENT_DATE + INTERVAL '1 day') + make_interval(hours => 6)) AT TIME ZONE 'UTC',
    '{"batch_size": 200, "budget_sec": 1800}'::jsonb
)
ON CONFLICT (source) DO NOTHING;

COMMENT ON TABLE scrape_schedules IS
    'In-app scheduler config (заменяет cron-script setup). '
    'Sources: avito_city_sweep, yandex_city_sweep (dormant, #561), '
    'cian_history_backfill, rosreestr_dkp_import, listing_source_snapshot (#570), '
    'asking_to_sold_ratio_refresh (#648), refresh_search_matview (#769), '
    'yandex_address_backfill (#855, EKB pilot), '
    'sber_index_pull (#887, monthly СберИндекс city-level price index), '
    'rosreestr_quarter_poll (#888, monthly Rosreestr new-quarter availability check), '
    'cian_city_sweep (dormant, #973), '
    'yandex_newbuilding_sweep (dormant, #974), '
    'geocode_missing_listings (#1: listings geom backfill, all sources).';

COMMIT;
