"""定期回查未标引文献：PubMed in-process/publisher → MeSH 标引后自动补打标签

策略：
1. 查询 citation_status NOT IN ('medline') 且超过 7 天未复查的文献
2. 批量调用 PubMed EFetch 获取最新数据
3. 如有新 MeSH → 更新 mesh_headings + tag_article()
"""

import asyncio
import logging
from datetime import datetime, timedelta

from sqlalchemy import select, and_, not_
from sqlalchemy.ext.asyncio import AsyncSession

from app.compat import UTC
from app.db import async_session
from app.models.literature import GlobalLiterature, GlobalLiteratureTag
from app.services.pubmed_api import fetch_articles, API_KEY
from app.services.tag_service import tag_article

logger = logging.getLogger(__name__)

# 每批查询数（PubMed EFetch 限制：无 key 3 req/s，200/req）
BATCH_SIZE = 200
# 限速间隔（秒）
RATE_LIMIT = 0.34 if not API_KEY else 0.11


async def find_unindexed_batch(db: AsyncSession, max_age_days: int = 7,
                               batch_size: int = BATCH_SIZE) -> list[GlobalLiterature]:
    """查找需要回查的未标引文献

    条件：
    - citation_status != 'medline'（含 NULL，即从未标引）
    - updated_at <= max_age_days 前（避免频繁查刚查过的）
    """
    cutoff = datetime.now(UTC) - timedelta(days=max_age_days)
    result = await db.execute(
        select(GlobalLiterature).where(and_(
            GlobalLiterature.mesh_headings.isnot(None),
            not_(GlobalLiterature.citation_status.in_(["medline"])),
            GlobalLiterature.updated_at <= cutoff,
        )).order_by(GlobalLiterature.updated_at).limit(batch_size)
    )
    return list(result.scalars().all())


async def recheck_batch(lits: list[GlobalLiterature]) -> int:
    """对一批文献调用 EFetch 检查是否有新 MeSH，返回更新数"""
    if not lits:
        return 0

    pmids = [lit.pmid for lit in lits]
    logger.info("回查 %d 篇文献: PMID %d..%d", len(pmids), min(pmids), max(pmids))

    articles = await fetch_articles(pmids)
    if not articles:
        return 0

    # 构建 PMID → article 映射
    article_map = {a["pmid"]: a for a in articles}

    updated = 0
    async with async_session() as db:
        for lit in lits:
            article = article_map.get(lit.pmid)
            if not article:
                continue

            new_mesh = article.get("mesh_headings", [])
            if not new_mesh:
                continue

            # 检查 MeSH 是否有变化
            old_mesh = lit.mesh_headings or []
            old_uis = {m.get("ui") for m in old_mesh if m.get("ui")}
            new_uis = {m.get("ui") for m in new_mesh if m.get("ui")}
            if new_uis == old_uis:
                continue

            # 有新 MeSH → 更新
            lit.mesh_headings = new_mesh
            lit.citation_status = article.get("citation_status", lit.citation_status)
            if article.get("date_completed"):
                lit.date_completed = article["date_completed"]

            # 重新打标
            tagged = await tag_article(db, lit.id, new_mesh)
            if tagged:
                updated += 1

        await db.commit()

    return updated


async def run_retagger(max_articles: int = 0) -> dict:
    """运行回查流程，返回统计"""
    stats = {"checked": 0, "updated": 0, "errors": 0}

    async with async_session() as db:
        while True:
            batch = await find_unindexed_batch(db)
            if not batch:
                break

            try:
                n = await recheck_batch(batch)
                stats["updated"] += n
                stats["checked"] += len(batch)
                logger.info("回查进度: %d checked, %d updated", stats["checked"], stats["updated"])
            except Exception:
                logger.exception("回查批次失败 (%d 篇)", len(batch))
                stats["errors"] += 1

            if max_articles and stats["checked"] >= max_articles:
                break

            await asyncio.sleep(RATE_LIMIT)

    return stats
