"""avito_shared.py — общие константы/хелперы для avito*.py модулей.

Единая точка правды для:
  - `RUS_MONTHS` — рус. месяц (родительный падеж) → номер, для парсинга дат вида
    "9 октября 2025" (было продублировано в avito.py/avito_detail.py/avito_houses.py).
  - `_unix_to_date` — unix-epoch (в СЕКУНДАХ) → `date`, в таймзоне Europe/Moscow.

TZ-обоснование (issue #2129): было 3 независимые копии конвертации unix→date с
РАЗНЫМИ timezone — MSK (avito.py, документировано issue #726), UTC (avito_houses.py),
naive/system-local (avito_imv.py). Один и тот же epoch давал разные `date` в
SERP / houses / IMV путях для одного и того же объявления.

Почему MSK — канонический выбор (не UTC, не local):
  - Avito — российская площадка, отображает даты объявлений (`item-date` на SERP,
    `publishDate`, история цен `startPriceDate`/`lastPriceDate`/`removedDate`) в
    Europe/Moscow. Объявление, поднятое в 00:00–03:00 MSK, при наивной UTC-конвертации
    получило бы дату на день раньше, чем видно на самом Avito (обоснование #726).
  - avito_houses.py и avito_imv.py используются на одних и тех же данных
    (`housePlacementHistory` widget / IMV `placementHistory` — идентичные поля
    startPriceDate/lastPriceDate/removedDate, см. fixtures/avito_imv_getdata.json) —
    единственный смысл держать их согласованными с уже принятым MSK-решением из #726.
  - Проверено на живой fixture: startPriceDate/lastPriceDate — всегда полночь UTC
    (00:00:00), поэтому MSK vs UTC не меняет date для них. `removedDate`/`publishDate`
    несут реальное время суток — там UTC/naive-local и MSK ДЕЙСТВИТЕЛЬНО расходятся
    (пример из fixture: removedDate=1756077885 → UTC 2025-08-24, MSK 2025-08-25).
    Это единственный реальный сдвиг данных от этого фикса: `avito_imv_evaluations` /
    `house_placement_history`, поля `removed_date`/`publish_date`, у ~12.5% строк
    (события в 21:00–23:59 UTC/local) дата сдвинется на +1 день при следующем прогоне
    скрейпера. Схему/уже сохранённые строки не трогаем — это единственно правильное
    представление на будущее.
  - Fixed-offset `timezone(timedelta(hours=3))`, а не `zoneinfo("Europe/Moscow")` —
    не зависит от системной tzdata в контейнере; Москва не переходит на DST с 2014,
    так что fixed offset корректен всегда.
"""

from __future__ import annotations

import logging
from datetime import date, datetime, timedelta, timezone

logger = logging.getLogger(__name__)

# Europe/Moscow, UTC+3, без перехода на летнее время (с 2014).
MSK = timezone(timedelta(hours=3))

# Русские месяцы (родительный падеж) для парсинга дат вида "9 октября 2025".
RUS_MONTHS: dict[str, int] = {
    "января": 1,
    "февраля": 2,
    "марта": 3,
    "апреля": 4,
    "мая": 5,
    "июня": 6,
    "июля": 7,
    "августа": 8,
    "сентября": 9,
    "октября": 10,
    "ноября": 11,
    "декабря": 12,
}


def _unix_to_date(ts: int | float | None) -> date | None:
    """Конвертирует unix-epoch (в СЕКУНДАХ) в `date`, в таймзоне Europe/Moscow (MSK).

    Возвращает `None` для `None`/`0` (Avito использует 0 как sentinel «нет даты») и
    для значений, которые не конвертируются в валидную дату.

    ВАЖНО: ожидает epoch в СЕКУНДАХ. Avito SERP-поле `sortTimeStamp` отдаёт epoch в
    МИЛЛИСЕКУНДАХ — вызывающий код обязан поделить на 1000 до вызова (см.
    `avito.py::_build_sort_timestamp_map`).
    """
    if not ts:
        return None
    try:
        return datetime.fromtimestamp(float(ts), tz=MSK).date()
    except (OSError, OverflowError, ValueError):
        logger.warning("Не удалось конвертировать unix timestamp: %r", ts)
        return None
