"""Feed 匹配引擎：新文献入库 → 匹配用户订阅 → 生成个性化 Feed"""

import uuid

from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession

from app.models.interaction import UserDismissedTag, UserFeed, UserSubscription
from app.models.literature import GlobalLiteratureTag, GlobalTag

# 用户负偏好阈值：同一标签累计被忽略 N 次，Feed 引擎不再推送该标签下的文章
DISMISS_THRESHOLD = 3


async def _get_dismissed_tag_ids(db: AsyncSession, user_ids: list[uuid.UUID]) -> dict[uuid.UUID, set[str]]:
    """批量查询用户 dismissed tag IDs（超过阈值的 tag_id 集合），返回 {user_id: {tag_id_str}}"""
    rows = await db.execute(
        select(UserDismissedTag.user_id, UserDismissedTag.tag_id)
        .where(
            UserDismissedTag.user_id.in_(user_ids),
            UserDismissedTag.dismiss_count >= DISMISS_THRESHOLD,
        )
    )
    result: dict[uuid.UUID, set[str]] = {}
    for uid_val, tag_id in rows:
        result.setdefault(uid_val, set()).add(str(tag_id))
    return result


async def generate_feeds_for_literature(
    db: AsyncSession,
    literature_id: uuid.UUID,
) -> int:
    """为一篇文献匹配所有订阅用户，插入 user_feed。返回插入数。"""
    tag_result = await db.execute(
        select(GlobalLiteratureTag.tag_id, GlobalLiteratureTag.is_major)
        .join(GlobalTag, GlobalTag.id == GlobalLiteratureTag.tag_id)
        .where(
            GlobalLiteratureTag.literature_id == literature_id,
            GlobalTag.is_active == True,
        )
    )
    lit_tags = [(t, m) for t, m in tag_result]
    if not lit_tags:
        return 0

    lit_tag_ids = {t[0] for t in lit_tags}
    lit_major_ids = {t[0] for t in lit_tags if t[1]}

    result = await db.execute(
        select(UserSubscription.user_id, UserSubscription.tag_id, UserSubscription.match_mode)
        .where(UserSubscription.tag_id.in_(lit_tag_ids), UserSubscription.is_active == True)
    )
    user_data = result.all()
    if not user_data:
        return 0

    # 按用户聚合
    user_matches: dict[uuid.UUID, list[dict]] = {}
    for uid, tag_id, mode in user_data:
        user_matches.setdefault(uid, []).append({
            "tag_id": str(tag_id), "is_major": tag_id in lit_major_ids, "match_mode": mode,
        })

    user_ids = list(user_matches.keys())

    # ── 批量查询负偏好标签（跳过已忽略达阈值的标签命中） ──
    dismissed_map = await _get_dismissed_tag_ids(db, user_ids)

    # ── 批量预查订阅总数（eliminate N+1） ──
    total_q = await db.execute(
        select(UserSubscription.user_id, func.count(UserSubscription.id).label('total'))
        .where(UserSubscription.user_id.in_(user_ids), UserSubscription.is_active == True)
        .group_by(UserSubscription.user_id)
    )
    user_total_counts = {row.user_id: row.total for row in total_q}

    # ── 批量预查已存在的 Feed（eliminate N+1） ──
    ex_result = await db.execute(
        select(UserFeed.user_id, UserFeed.literature_id)
        .where(UserFeed.literature_id == literature_id, UserFeed.user_id.in_(user_ids))
    )
    existing_feeds = {(row.user_id, row.literature_id) for row in ex_result}

    count = 0
    for uid, matches in user_matches.items():
        # 跳过被忽略标签的文章
        dismissed_tags = dismissed_map.get(uid, set())
        if any(m["tag_id"] in dismissed_tags for m in matches):
            continue
        # 取所有命中标签中最严格的匹配模式
        mode_rank = {"strict": 0, "standard": 1, "loose": 2}
        mode = min(matches, key=lambda m: mode_rank.get(m["match_mode"], 1))["match_mode"]

        if mode == "strict":
            total_count = user_total_counts.get(uid, 0)
            if len(matches) < total_count:
                continue

        major_cnt = sum(1 for m in matches if m["is_major"])
        if mode == "standard":
            # 标准模式下：有major命中→必读，3+标签命中→推荐，否则相关
            if major_cnt >= 1:
                priority = "must_read" if len(matches) >= 3 else "recommended"
            elif len(matches) >= 2:
                priority = "recommended"
            else:
                priority = "related"
        elif mode == "loose":
            # 宽松：任一命中→推荐，3+命中→必读
            priority = "must_read" if len(matches) >= 3 else "recommended"
        else:
            # strict 已经在上面的过滤中处理了
            priority = "must_read" if major_cnt >= 2 else "recommended"

        # 使用预查的 set 去重
        if (uid, literature_id) in existing_feeds:
            continue

        db.add(UserFeed(
            user_id=uid, literature_id=literature_id,
            matched_tags=[{"tag_id": m["tag_id"], "is_major": m["is_major"]} for m in matches],
            priority=priority,
        ))
        count += 1

    return count


async def batch_generate_feeds(
    db: AsyncSession,
    literature_ids: list[uuid.UUID],
) -> int:
    """批量生成 feeds：一次查询获取所有文献标签 + 所有订阅匹配，减少 N+1"""
    if not literature_ids:
        return 0

    # 1. 批量获取所有文献的标签
    tag_result = await db.execute(
        select(GlobalLiteratureTag.literature_id, GlobalLiteratureTag.tag_id, GlobalLiteratureTag.is_major)
        .where(GlobalLiteratureTag.literature_id.in_(literature_ids))
    )
    lit_tags: dict[uuid.UUID, dict[uuid.UUID, bool]] = {}
    all_tag_ids: set[uuid.UUID] = set()
    for lit_id, tag_id, is_major in tag_result:
        lit_tags.setdefault(lit_id, {})[tag_id] = is_major
        all_tag_ids.add(tag_id)
    if not all_tag_ids:
        return 0

    present_ids = [lid for lid in literature_ids if lid in lit_tags]
    if not present_ids:
        return 0

    # 2. 批量获取订阅这些标签的用户
    sub_result = await db.execute(
        select(UserSubscription.user_id, UserSubscription.tag_id, UserSubscription.match_mode)
        .where(UserSubscription.tag_id.in_(all_tag_ids), UserSubscription.is_active == True)
    )
    user_subs: dict[uuid.UUID, dict[uuid.UUID, str]] = {}
    for user_id, tag_id, mode in sub_result:
        user_subs.setdefault(user_id, {})[tag_id] = mode
    if not user_subs:
        return 0

    # 3. 每个用户取最严格的匹配模式
    mode_rank = {"strict": 0, "standard": 1, "loose": 2}
    user_mode: dict[uuid.UUID, str] = {}
    for uid, tags in user_subs.items():
        user_mode[uid] = min(tags.values(), key=lambda m: mode_rank.get(m, 1))

    # 4. 批量查询负偏好标签
    all_user_ids = list(user_subs.keys())
    dismissed_map = await _get_dismissed_tag_ids(db, all_user_ids)

    # 5. 订阅总数（strict 模式需要）
    total_q = await db.execute(
        select(UserSubscription.user_id, func.count(UserSubscription.id).label("total"))
        .where(UserSubscription.user_id.in_(all_user_ids), UserSubscription.is_active == True)
        .group_by(UserSubscription.user_id)
    )
    user_total = {r.user_id: r.total for r in total_q}

    # 6. 批量查询已存在的 Feed
    ex = await db.execute(
        select(UserFeed.user_id, UserFeed.literature_id)
        .where(UserFeed.literature_id.in_(present_ids))
    )
    existing = {(r.user_id, r.literature_id) for r in ex}

    # 6. 内存匹配
    new_feeds: list[UserFeed] = []
    for lit_id in present_ids:
        tag_map = lit_tags[lit_id]
        lit_tag_set = set(tag_map)
        lit_major = {t for t, m in tag_map.items() if m}

        for uid, subs in user_subs.items():
            matched = lit_tag_set & set(subs)
            if not matched:
                continue

            # 跳过被忽略标签的文章
            dismissed_tags = dismissed_map.get(uid, set())
            if any(str(t) in dismissed_tags for t in matched):
                continue

            mode = user_mode[uid]
            if mode == "strict" and len(matched) < user_total.get(uid, 0):
                continue

            major_cnt = len(matched & lit_major)
            if mode == "standard":
                if major_cnt >= 1:
                    priority = "must_read" if len(matched) >= 3 else "recommended"
                elif len(matched) >= 2:
                    priority = "recommended"
                else:
                    priority = "related"
            elif mode == "loose":
                priority = "must_read" if len(matched) >= 3 else "recommended"
            else:
                priority = "must_read" if major_cnt >= 2 else "recommended"

            if (uid, lit_id) in existing:
                continue

            new_feeds.append(UserFeed(
                user_id=uid, literature_id=lit_id,
                matched_tags=[{"tag_id": str(t), "is_major": t in lit_major} for t in matched],
                priority=priority,
            ))

    if new_feeds:
        db.add_all(new_feeds)
    return len(new_feeds)


async def generate_all_feeds(db: AsyncSession) -> int:
    """全量：全局文献库 → 所有订阅 → 生成 Feed。seed 脚本专用。"""
    from app.models.literature import GlobalLiterature
    result = await db.execute(select(GlobalLiterature.id))
    all_ids = [r for (r,) in result.all()]  # r is a Row, unpack
    total = 0
    for lid in all_ids:
        total += await generate_feeds_for_literature(db, lid)
    await db.commit()
    return total
