"""P1-5：`SqlForeshadowLedgerRepo.scan_overdue` 谓词下推 + 批量 UPDATE ... RETURNING。

旧实现拉全项目伏笔行到内存再 Python 过滤/逐条 UPDATE；新实现把判据下推为单条
`UPDATE ... WHERE ... RETURNING`（命中行一次置 OVERDUE）。判据须等价于
`foreshadow_state.is_overdue`：`expected_close_to IS NOT NULL AND expected_close_to <
current AND status NOT IN (CLOSED, OVERDUE)`。

真实批量更新行为由 tests/ 下 E2E（真 PG）覆盖；此处以不连库方式断言编译出的 SQL
确实是带正确谓词 + RETURNING 的 UPDATE（谓词下推已正确接线），保持单测确定性。
"""

from __future__ import annotations

import uuid
from typing import Any

import pytest
from sqlalchemy.dialects import postgresql
from ww_core.domain.foreshadow_repo import SqlForeshadowLedgerRepo

PROJECT = uuid.UUID("00000000-0000-0000-0000-000000000001")


class _CapturingResult:
    def scalars(self) -> _CapturingResult:
        return self

    def all(self) -> list[Any]:
        return []


class _CapturingSession:
    """fake session：捕获 execute 的 statement，返回空 RETURNING 结果（不连库）。"""

    def __init__(self) -> None:
        self.statements: list[Any] = []
        self.expired = False

    async def execute(self, statement: Any) -> _CapturingResult:
        self.statements.append(statement)
        return _CapturingResult()

    def expire_all(self) -> None:
        self.expired = True


@pytest.mark.asyncio
async def test_scan_overdue_pushes_predicate_into_update_returning() -> None:
    session = _CapturingSession()
    repo = SqlForeshadowLedgerRepo(session)  # type: ignore[arg-type]  # fake 同形

    changed = await repo.scan_overdue(PROJECT, current_chapter=15)

    # 空结果 → 无命中行；且发了一条语句、并 expire_all 同步 ORM 身份。
    assert changed == []
    assert session.expired is True
    assert len(session.statements) == 1

    compiled = str(
        session.statements[0].compile(dialect=postgresql.dialect())  # type: ignore[no-untyped-call]
    ).upper()
    # 谓词下推 + 批量更新 + RETURNING（不再全表扫到内存逐行）。
    assert "UPDATE" in compiled
    assert "RETURNING" in compiled
    assert "EXPECTED_CLOSE_TO" in compiled
    # CLOSED / OVERDUE 被排除（NOT IN）——命中即真正状态翻转的行。
    assert "NOT IN" in compiled
