"""T2.3 章节 accepted 版本晋升单测（ARCH §3.3 多版本行 / §5.5 step 1）。

纯内存 fake，无 DB：断言 max(version)+1 计算、草稿行保留、按 project_id 隔离、
返回不可变 frozen View。`(project_id, chapter_no, version)` 唯一约束的 **DB 级**强制
由 `chapters` 模型 UniqueConstraint 定义（见 packages/db/ww_db/models.py），真实 DB
路径由 T2.7 E2E 覆盖；此处只测 repo 计算下一版次的逻辑。
"""

from __future__ import annotations

import uuid
from dataclasses import FrozenInstanceError, dataclass, field

import pytest
from ww_core.domain.chapter_repo import ChapterView

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


@dataclass
class _Row:
    project_id: uuid.UUID
    chapter_no: int
    volume: int
    content: str
    status: str
    version: int


@dataclass
class FakePromoteRepo:
    """内存替身——镜像 SqlChapterRepo 晋升语义（max+1 新行，草稿保留）。"""

    rows: list[_Row] = field(default_factory=list)

    def _versions(self, project_id: uuid.UUID, chapter_no: int) -> list[int]:
        return [
            r.version
            for r in self.rows
            if r.project_id == project_id and r.chapter_no == chapter_no
        ]

    async def max_version(self, project_id: uuid.UUID, chapter_no: int) -> int:
        versions = self._versions(project_id, chapter_no)
        return max(versions) if versions else 0

    async def promote_to_accepted(
        self, project_id: uuid.UUID, chapter_no: int, *, content: str, volume: int = 1
    ) -> ChapterView:
        next_version = (await self.max_version(project_id, chapter_no)) + 1
        row = _Row(
            project_id=project_id,
            chapter_no=chapter_no,
            volume=volume,
            content=content,
            status="accepted",
            version=next_version,
        )
        self.rows.append(row)
        return ChapterView(
            project_id=row.project_id,
            chapter_no=row.chapter_no,
            volume=row.volume,
            content=row.content,
            status=row.status,
            version=row.version,
        )

    async def latest_accepted(self, project_id: uuid.UUID, chapter_no: int) -> ChapterView | None:
        accepted = [
            r
            for r in self.rows
            if r.project_id == project_id and r.chapter_no == chapter_no and r.status == "accepted"
        ]
        if not accepted:
            return None
        row = max(accepted, key=lambda r: r.version)
        return ChapterView(
            project_id=row.project_id,
            chapter_no=row.chapter_no,
            volume=row.volume,
            content=row.content,
            status=row.status,
            version=row.version,
        )


def _repo_with_draft() -> FakePromoteRepo:
    repo = FakePromoteRepo()
    repo.rows.append(
        _Row(
            project_id=PROJECT,
            chapter_no=1,
            volume=1,
            content="draft text",
            status="draft",
            version=1,
        )
    )
    return repo


async def test_promote_uses_max_version_plus_one() -> None:
    fake = _repo_with_draft()  # 已有 version=1 草稿
    view = await fake.promote_to_accepted(PROJECT, 1, content="final")
    assert view.version == 2  # max(1)+1
    assert view.status == "accepted"
    assert view.content == "final"


async def test_promote_keeps_draft_row() -> None:
    fake = _repo_with_draft()
    await fake.promote_to_accepted(PROJECT, 1, content="final")
    draft = [r for r in fake.rows if r.status == "draft"]
    assert len(draft) == 1  # 草稿行保留
    assert draft[0].content == "draft text"


async def test_promote_second_accept_increments_again() -> None:
    fake = _repo_with_draft()
    await fake.promote_to_accepted(PROJECT, 1, content="v2")
    again = await fake.promote_to_accepted(PROJECT, 1, content="v3")
    assert again.version == 3  # 改稿再验收 → max(2)+1


async def test_promote_first_version_when_no_rows() -> None:
    fake = FakePromoteRepo()  # 空，无草稿
    view = await fake.promote_to_accepted(PROJECT, 9, content="x")
    assert view.version == 1  # 0+1


async def test_max_version_isolated_by_project() -> None:
    fake = _repo_with_draft()  # PROJECT/ch1 有 version=1
    # OTHER 项目同章号互不影响
    assert await fake.max_version(OTHER, 1) == 0
    view = await fake.promote_to_accepted(OTHER, 1, content="other")
    assert view.version == 1


async def test_latest_accepted_returns_highest_version() -> None:
    fake = _repo_with_draft()
    await fake.promote_to_accepted(PROJECT, 1, content="v2")
    await fake.promote_to_accepted(PROJECT, 1, content="v3")
    latest = await fake.latest_accepted(PROJECT, 1)
    assert latest is not None
    assert latest.version == 3
    assert latest.content == "v3"


async def test_latest_accepted_none_when_only_draft() -> None:
    fake = _repo_with_draft()
    assert await fake.latest_accepted(PROJECT, 1) is None


async def test_chapter_view_is_frozen() -> None:
    fake = FakePromoteRepo()
    view = await fake.promote_to_accepted(PROJECT, 1, content="x")
    with pytest.raises(FrozenInstanceError):
        view.version = 99  # type: ignore[misc]
