"""
Восстановление SMS-очереди после ошибки
cannot import name 'extract_clean_phones' из spam_sms.

1) Из bot.log — объявления, где телефоны уже нашли, но упали на импорте.
2) Из autoinfo — записи с телефонами без успешной SMS в sent_phones.

Запуск в контейнере:
  python scripts/requeue_after_clean_phones_error.py
"""

from __future__ import annotations

import asyncio
import logging
import os
import re
import sys
from pathlib import Path

# /app — корень приложения в Docker
ROOT = Path(__file__).resolve().parents[1]
if str(ROOT) not in sys.path:
    sys.path.insert(0, str(ROOT))

logging.basicConfig(level=logging.INFO, format="%(levelname)s %(message)s")
_log = logging.getLogger("requeue")


def _pick_user_id() -> int:
    from config import ADMIN_USER_IDS, ALLOWED_USER_IDS

    if ADMIN_USER_IDS:
        return int(sorted(ADMIN_USER_IDS)[0])
    if ALLOWED_USER_IDS:
        return int(sorted(ALLOWED_USER_IDS)[0])
    raw = (os.getenv("ADMIN_USER_ID") or "").split(",")[0].strip()
    return int(raw or "0")


def _parse_failed_from_log(log_path: Path) -> dict[str, str]:
    """ad_id -> raw phones string из строки «Итого найдено…» перед ошибкой импорта."""
    if not log_path.is_file():
        _log.warning("Лог не найден: %s", log_path)
        return {}

    text = log_path.read_text(encoding="utf-8", errors="ignore")
    found_pat = re.compile(
        r"Итого найдено телефонов для (\S+): (\d+) — (.+)"
    )
    out: dict[str, str] = {}
    for m in found_pat.finditer(text):
        ad_id = m.group(1)
        phones_raw = m.group(3).strip()
        if phones_raw in ("пусто", "[]"):
            continue
        chunk = text[m.end() : m.end() + 3000]
        if "extract_clean_phones" in chunk and "cannot import" in chunk:
            out[ad_id] = phones_raw
    return out


async def _main() -> int:
    from spam_sms import extract_clean_phones
    from app.database.crud_pending_sms import count_pending_sms, enqueue_pending_sms
    from app.database.crud_static import is_phone_sent
    from app.database.models import AutoInfo
    from config import effective_sms_resend_after_days
    from sqlalchemy import select
    from app.database.engine import async_session

    user_id = _pick_user_id()
    if not user_id:
        _log.error("Не удалось определить user_id для очереди")
        return 1

    resend_days = effective_sms_resend_after_days()
    _log.info("user_id=%s resend_days=%s", user_id, resend_days)

    log_paths = [
        Path("/app/logs/bot.log"),
        ROOT / "logs" / "bot.log",
    ]
    from_log: dict[str, str] = {}
    for p in log_paths:
        from_log.update(_parse_failed_from_log(p))
    _log.info("Из логов кандидатов (ad_id): %s", len(from_log))

    # ad_id -> (phones list, name, link) — только из лога с ошибкой импорта
    candidates: dict[str, tuple[list, str | None, str | None]] = {}

    for ad_id, raw in from_log.items():
        phones_src: list = [raw]
        if raw.startswith("[") and raw.endswith("]"):
            try:
                import ast

                parsed = ast.literal_eval(raw)
                if isinstance(parsed, (list, tuple)):
                    phones_src = list(parsed)
            except Exception:
                phones_src = [raw]
        candidates[str(ad_id)] = (phones_src, None, None)

    # Подтянуть name/link из autoinfo, если запись уже есть
    if candidates:
        async with async_session() as session:
            result = await session.execute(
                select(AutoInfo).where(AutoInfo.ad_id.in_(list(candidates.keys())))
            )
            for row in result.scalars().all():
                ad_id = str(row.ad_id)
                src, _, _ = candidates[ad_id]
                if not src and row.all_phones:
                    src = row.all_phones
                candidates[ad_id] = (src, row.name, row.link)

    _log.info("К восстановлению из лога: %s", len(candidates))

    # Дополнительно: autoinfo за сегодня с телефонами без успешной SMS
    # (на случай если ошибка была на другом пути импорта после сохранения)
    from datetime import datetime, timedelta

    since = datetime.utcnow() - timedelta(hours=24)
    async with async_session() as session:
        result = await session.execute(
            select(AutoInfo).where(AutoInfo.created_at >= since)
        )
        recent = list(result.scalars().all())
    for row in recent:
        ad_id = str(row.ad_id)
        if ad_id in candidates:
            continue
        if not row.all_phones:
            continue
        candidates[ad_id] = (row.all_phones, row.name, row.link)

    _log.info("Всего кандидатов (лог + autoinfo 24ч): %s", len(candidates))

    queued = 0
    skipped_sent = 0
    skipped_empty = 0
    skipped_dup = 0

    for ad_id, (phones_src, name, link) in candidates.items():
        clean = extract_clean_phones(phones_src)
        if not clean:
            skipped_empty += 1
            continue
        phone = clean[0]
        if await is_phone_sent(phone, resend_days):
            skipped_sent += 1
            continue
        row = await enqueue_pending_sms(
            user_id=user_id,
            phone_number=phone,
            ad_id=ad_id,
            ad_name=name or f"ad {ad_id}",
            link=link,
        )
        if row is None:
            skipped_dup += 1
        else:
            queued += 1
            _log.info("queued %s -> %s (%s)", ad_id, phone, name)

    pending_total = await count_pending_sms()
    _log.info(
        "Готово: queued=%s skipped_sent=%s skipped_empty=%s skipped_dup=%s pending_total=%s",
        queued,
        skipped_sent,
        skipped_empty,
        skipped_dup,
        pending_total,
    )
    return 0


if __name__ == "__main__":
    raise SystemExit(asyncio.run(_main()))
