#!/usr/bin/env python3
"""Очередь на индексацию: какие статьи поиск ещё не взял и сколько они ждут.

Собрана по образцу PR Factory («Отправить на переобход»). Там список статей
вне поиска с двумя колонками: сколько дней ждёт и когда последний раз
отправляли. Человеку делать ничего не нужно - отправку ведёт скрипт, список
нужен, чтобы видеть очередь и замечать, когда она перестаёт двигаться.

Откуда что берём:
  статьи портала       - карта сайта (сайт/public/sitemap.xml)
  кто уже в поиске     - выгрузка Вебмастера, поле «адреса_в_поиске»
  сколько ждёт         - дата публикации из реестра текстов
  когда отправляли     - завод/вебмастер/переобход-журнал.json

Порядок: после выгрузки Вебмастера и реестра, до обновить-фолбэк.py.
Запуск: python3 панель/данные/обновить-очередь-индексации.py
"""

import json
import re
from datetime import date, datetime
from pathlib import Path

ПАПКА = Path(__file__).resolve().parent
КОРЕНЬ = ПАПКА.parent.parent
ОТЧЁТ = ПАПКА / "report.json"
КАРТА = КОРЕНЬ / "сайт" / "public" / "sitemap.xml"
РЕЕСТР = КОРЕНЬ / "завод" / "реестр-текстов.json"
ИСТОРИЯ = КОРЕНЬ / "завод" / "вебмастер" / "история"
ЖУРНАЛ = КОРЕНЬ / "завод" / "вебмастер" / "переобход-журнал.json"


def склонение(н, один, два, много):
    о, е = abs(н) % 100, abs(н) % 10
    if 11 <= о <= 14:
        return много
    return один if е == 1 else два if 2 <= е <= 4 else много


def адреса_в_поиске():
    """Последний снимок Вебмастера. None - поля ещё нет в снимке."""
    снимки = sorted(ИСТОРИЯ.glob("*.json")) if ИСТОРИЯ.exists() else []
    for путь in reversed(снимки):
        try:
            д = json.loads(путь.read_text(encoding="utf-8"))
        except (OSError, ValueError):
            continue
        если = д.get("адреса_в_поиске")
        if если is not None:
            return {а.rstrip("/") for а in если}
    return None


def из_карты():
    if not КАРТА.exists():
        return []
    return re.findall(r"<loc>(.*?)</loc>", КАРТА.read_text(encoding="utf-8"))


def даты_публикаций():
    """{слаг: дата выхода} по реестру текстов - только статьи портала."""
    если = {}
    if not РЕЕСТР.exists():
        return если
    д = json.loads(РЕЕСТР.read_text(encoding="utf-8"))
    for т in д["тексты"]:
        if т.get("вид") == "внешняя площадка":
            continue
        # У статей портала дата в статусе записана как ГГГГ-ММ-ДД
        # («опубликовано на портале 2026-09-11»), у площадочных - как
        # ДД.ММ.ГГГГ. Разбираем оба формата.
        текст = str(т.get("статус", ""))
        м = re.search(r"(\d{4})-(\d{2})-(\d{2})", текст)
        д_ = None
        if м:
            д_ = date(int(м.group(1)), int(м.group(2)), int(м.group(3)))
        else:
            м = re.search(r"(\d{2})\.(\d{2})\.(\d{4})", текст)
            if м:
                д_ = date(int(м.group(3)), int(м.group(2)), int(м.group(1)))
        слаг = (т.get("слаг") or "").strip("/")
        if д_ and слаг:
            если[слаг] = д_
    return если


def последние_отправки():
    """{адрес: когда отправляли в последний раз}."""
    если = {}
    if not ЖУРНАЛ.exists():
        return если
    try:
        записи = json.loads(ЖУРНАЛ.read_text(encoding="utf-8"))
    except (OSError, ValueError):
        return если
    for з in записи:
        когда = з.get("когда")
        for а in з.get("адреса") or []:
            # В журнале лежат пути вида «/statya/…/», в карте сайта - полные
            # адреса. Ключом берём путь, по нему и сверяем.
            путь = "/" + str(а).split("frantlas.ru/", 1)[-1].lstrip("/")
            путь = путь.rstrip("/") + "/"
            если[путь] = max(если.get(путь, ""), когда or "")
    return если


def главная():
    в_поиске = адреса_в_поиске()
    отчёт = json.loads(ОТЧЁТ.read_text(encoding="utf-8"))
    seo = отчёт.setdefault("seo", {})

    if в_поиске is None:
        seo["очередь_индексации"] = {
            "статус": "нет данных",
            "почему": ("в снимках Вебмастера нет поля «адреса_в_поиске»: "
                       "запустите python3 завод/вебмастер/выгрузка.py"),
        }
        ОТЧЁТ.write_text(json.dumps(отчёт, ensure_ascii=False, indent=1) + "\n",
                         encoding="utf-8")
        print("очередь индексации: списка страниц в поиске ещё нет")
        return 0

    даты = даты_публикаций()
    отправки = последние_отправки()
    сегодня = date.today()

    строки = []
    for адрес in из_карты():
        короткий = adr = адрес.rstrip("/")
        if короткий in в_поиске:
            continue
        слаг = короткий.rsplit("/", 1)[-1]
        вышла = даты.get(слаг)
        путь = "/" + короткий.split("frantlas.ru/", 1)[-1] if "frantlas.ru/" in короткий else короткий
        строки.append({
            "адрес": адрес,
            "путь": путь.rstrip("/") + "/",
            "слаг": слаг,
            "вышла": вышла.isoformat() if вышла else None,
            "ждёт_дней": (сегодня - вышла).days if вышла else None,
            "отправляли": отправки.get(путь.rstrip("/") + "/"),
        })
    # Сверху те, кто ждёт дольше: очередь на переобход идёт в этом же порядке.
    строки.sort(key=lambda с: -(с["ждёт_дней"] or 0))

    seo["очередь_индексации"] = {
        "статус": "есть",
        "заголовок": "Ждут индексации",
        # Повтор отправки ведёт завод/indexnow/переобход-изменённых.py
        # (крон 20:00): страницы, которые не отправляли дольше трёх дней.
        "подпись": ("Страниц ещё нет в Яндексе. В 20:00 скрипт отправляет их на "
                    "переобход раз в три дня. Отправка старше четырёх дней - очередь встала."),
        "строки": строки,
        "сколько": len(строки),
        "в_поиске": len(в_поиске),
    }
    ОТЧЁТ.write_text(json.dumps(отчёт, ensure_ascii=False, indent=1) + "\n",
                     encoding="utf-8")
    ждут = [с for с in строки if (с["ждёт_дней"] or 0) > 7]
    print(f"очередь индексации: {len(строки)} "
          f"{склонение(len(строки), 'страница', 'страницы', 'страниц')} вне поиска, "
          f"из них {len(ждут)} ждёт дольше недели")
    return 0


if __name__ == "__main__":
    raise SystemExit(главная())
