"""Фоновый поллер: по каждому порталу читает свежие входящие сообщения,
генерирует черновик и отправляет оператору карточку на согласование."""
from __future__ import annotations

import asyncio
import logging

import bitrix
import store
import approval
from claude_drafter import draft_reply
from config import settings

log = logging.getLogger("poller")


def SHOULD_HANDLE(dialog: dict) -> bool:
    """Фильтр обслуживаемых диалогов. По умолчанию — личные чаты (1-на-1).
    Расширь под свои нужды (например, по конкретным chat_id или группам)."""
    chat = dialog.get("chat") or {}
    # 'user' — личный диалог; 'chat' — групповой. Берём личные по умолчанию.
    return dialog.get("type") == "user" or chat.get("type") == "user"


def _dialog_id(dialog: dict) -> str | None:
    if dialog.get("type") == "user" and dialog.get("user"):
        return str(dialog["user"].get("id"))
    chat = dialog.get("chat") or {}
    if chat.get("id"):
        return f"chat{chat['id']}"
    return None


async def _process_portal(domain: str) -> None:
    portal = store.get_portal(domain)
    if not portal or not portal.get("operator_id"):
        return
    operator_id = portal["operator_id"]

    dialogs = await bitrix.recent_dialogs(domain)
    for d in dialogs:
        if not SHOULD_HANDLE(d):
            continue
        dialog_id = _dialog_id(d)
        if not dialog_id:
            continue

        msgs = await bitrix.dialog_messages(domain, dialog_id, settings.context_limit)
        if not msgs:
            continue
        # im.dialog.messages.get отдаёт новые сверху — упорядочим по возрастанию id.
        msgs_sorted = sorted(msgs, key=lambda m: int(m.get("id", 0)))
        last_seen = store.get_cursor(domain, dialog_id)
        newest_id = int(msgs_sorted[-1].get("id", 0))

        if newest_id <= last_seen:
            continue

        last_msg = msgs_sorted[-1]
        # Реагируем только если ПОСЛЕДНЕЕ сообщение — НЕ от оператора (т.е. входящее).
        if str(last_msg.get("author_id")) == str(operator_id):
            store.set_cursor(domain, dialog_id, newest_id)
            continue

        try:
            draft = await draft_reply(msgs_sorted, operator_id)
        except Exception as e:  # noqa: BLE001
            log.warning("draft failed for %s/%s: %s", domain, dialog_id, e)
            continue

        pending = store.create_pending(
            domain=domain,
            dialog_id=dialog_id,
            incoming=(last_msg.get("text") or "").strip()[:500],
            draft=draft,
        )
        # Карточка отправляется оператору в личку с ботом (DIALOG_ID = operator_id).
        await approval.present_draft(domain, operator_id, pending)
        store.set_cursor(domain, dialog_id, newest_id)


async def poll_loop() -> None:
    log.info("poller started, interval=%ss", settings.poll_interval)
    while True:
        for domain in store.all_portal_domains():
            try:
                await _process_portal(domain)
            except Exception as e:  # noqa: BLE001
                log.warning("portal %s poll error: %s", domain, e)
        await asyncio.sleep(settings.poll_interval)
