"""Предполётная проверка источников контент-планёрки.

Планёрка читает источники молча: файла нет — просто спланирует неделю без
него и не скажет об этом ни слова. Тихая потеря данных хуже падения, потому
что план выглядит нормальным. Эта проверка делает потерю громкой.

Запуск: python3 preflight.py <корень волта> [ГГГГ-ММ-ДД] [--no-growth]
Печатает найденные проблемы, по одной в строке. Пусто — всё на месте.

`--no-growth` — для утреннего прогона. Прирост и упоминания собирает только
воскресная планёрка, поэтому к пятнице файлу штатно четыре дня, и без флага
утро ругалось бы на него каждый день до конца недели. Ежедневный алерт, на
который нечего ответить, — самый быстрый способ научить не читать алерты.
"""
from __future__ import annotations

import time
from pathlib import Path

# Архивы обновляются ежедневно; двое суток — уже сбой, а не задержка.
MAX_ARCHIVE_AGE_DAYS = 2

# Прирост и упоминания собирает growth.py на самой планёрке (вс и ср), поэтому
# порог другой: пять суток — это уже пропущенный прогон, а не нормальная пауза.
# Важнее срока сам факт: у TGStat история подписчиков живёт ~10 дней, и каждый
# несобранный прогон — это дни, которые больше не восстановить.
MAX_GROWTH_AGE_DAYS = 5
GROWTH_FILE = "{channel} {source} прирост и упоминания канала.md"

CHANNEL = "projects/channel"
TRENDWATCHING = CHANNEL + "/trendwatching"

# Отчёты ниши именуются датой дня планёрки — если генератор и Glob в prompt.md
# разъедутся, планёрка молча останется без внешнего контекста.
NICHE_TRACKS = ("ai", "биз")

# Архивы без даты в имени: перезаписываются каждым прогоном пуллеров.
ARCHIVES = (
    ("TGStat", "{channel} {source} TGStat архив постов канала.md"),
    ("Instagram", "{channel} {source} Instagram архив постов.md"),
    ("Threads", "{channel} {source} Threads архив постов.md"),
)


def _post_count(text: str):
    """Число из строки «Всего постов: N». None — строки нет."""
    marker = "Всего постов:"
    if marker not in text:
        return None
    tail = text.split(marker, 1)[1].lstrip()
    digits = ""
    for ch in tail:
        if ch.isdigit():
            digits += ch
        else:
            break
    return int(digits) if digits else None


def _niche_path(vault: Path, track: str, today: str) -> Path:
    return (Path(vault) / TRENDWATCHING /
            "{{channel}} {{source}} инстаграм-ниша {} – {}.md".format(track, today))


def check_sources(vault, today: str, with_growth: bool = True) -> list:
    vault = Path(vault)
    problems = []

    for name, filename in ARCHIVES:
        path = vault / CHANNEL / filename
        if not path.exists():
            problems.append(
                "нет архива {} — площадка выпадет из разбора".format(name))
            continue
        # Пустой архив опаснее отсутствующего: планёрка прочтёт его как
        # «постов не было» и честно напишет, что неделя была молчаливой.
        count = _post_count(path.read_text(encoding="utf-8"))
        if count == 0:
            problems.append(
                "архив {} пуст («Всего постов: 0») — похоже, пуллер "
                "перезаписал его мусором".format(name))

        age_days = (time.time() - path.stat().st_mtime) / 86400
        if age_days > MAX_ARCHIVE_AGE_DAYS:
            problems.append(
                "архив {} не обновлялся {} дн. — пуллер не отработал, "
                "цифры будут прошлыми".format(name, int(age_days)))

    if with_growth:
        growth = vault / CHANNEL / GROWTH_FILE
        if not growth.exists():
            problems.append(
                "нет файла прироста и упоминаний — планёрка не скажет, откуда "
                "пришли новые подписчики")
        else:
            age_days = (time.time() - growth.stat().st_mtime) / 86400
            if age_days > MAX_GROWTH_AGE_DAYS:
                problems.append(
                    "прирост и упоминания не обновлялись {} дн. — сбор падает, "
                    "а история подписчиков в TGStat живёт всего ~10 дней".format(
                        int(age_days)))

    for track in NICHE_TRACKS:
        path = _niche_path(vault, track, today)
        if not path.exists():
            problems.append(
                "нет отчёта «инстаграм-ниша {}» за {} — планёрка спланирует "
                "инстаграм вслепую".format(track, today))
    return problems


def main(argv=None) -> int:
    import sys
    from datetime import date

    argv = list(argv if argv is not None else sys.argv[1:])
    with_growth = True
    if "--no-growth" in argv:
        argv.remove("--no-growth")
        with_growth = False
    if not argv:
        print("укажи корень волта: python3 preflight.py <путь> [ГГГГ-ММ-ДД] [--no-growth]",
              file=sys.stderr)
        return 2
    today = argv[1] if len(argv) > 1 else date.today().isoformat()

    for problem in check_sources(argv[0], today, with_growth=with_growth):
        print(problem)
    # Ненулевой код — сигнал вызывающему скрипту, а не повод отменять планёрку:
    # неполный план лучше, чем никакого.
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
