"""T2.3 审稿留痕 repo 单测（ARCH §5.4/§5.5）。

纯内存 fake，无 DB：断言 record 写入字段、list 历史新→旧、set_decisions 裁决留痕，
均返回不可变 frozen View，按 project_id 隔离。
"""

from __future__ import annotations

import uuid
from dataclasses import dataclass, field

import pytest
from pydantic import ValidationError
from ww_core.domain.review_repo import ReviewRepo, ReviewView

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


@dataclass
class FakeReviewRepo:
    """内存替身——镜像 SqlReviewRepo 语义（append 每次审稿；裁决就地补 decisions）。"""

    rows: list[ReviewView] = field(default_factory=list)
    _seq: int = 0

    async def record(
        self,
        project_id: uuid.UUID,
        chapter_no: int,
        *,
        chapter_version: int | None = None,
        conflicts: list[dict[str, object]],
        foreshadow_sug: list[dict[str, object]] | None = None,
        style: dict[str, object] | None = None,
        pace: dict[str, object] | None = None,
        characterization: dict[str, object] | None = None,
        health_score: int | None = None,
    ) -> ReviewView:
        self._seq += 1
        view = ReviewView(
            id=uuid.UUID(int=self._seq),
            project_id=project_id,
            chapter_no=chapter_no,
            chapter_version=chapter_version,
            conflicts=list(conflicts),
            foreshadow_sug=list(foreshadow_sug or []),
            style=style,
            pace=pace,
            characterization=characterization,
            health_score=health_score,
            decisions=None,
        )
        self.rows.append(view)
        return view

    async def list_for_chapter(self, project_id: uuid.UUID, chapter_no: int) -> list[ReviewView]:
        matched = [
            r for r in self.rows if r.project_id == project_id and r.chapter_no == chapter_no
        ]
        return list(reversed(matched))  # 新→旧

    async def set_decisions(
        self, review_id: uuid.UUID, *, decisions: dict[str, object]
    ) -> ReviewView:
        idx, old = next((i, r) for i, r in enumerate(self.rows) if r.id == review_id)
        updated = old.model_copy(update={"decisions": dict(decisions)})
        self.rows[idx] = updated
        return updated


def _repo() -> FakeReviewRepo:
    return FakeReviewRepo()


async def test_record_persists_review_fields() -> None:
    repo: ReviewRepo = _repo()
    view = await repo.record(
        PROJECT,
        5,
        chapter_version=2,
        conflicts=[{"type": "性格漂移", "where": "p3"}],
        health_score=80,
    )
    assert isinstance(view, ReviewView)
    assert view.chapter_no == 5
    assert view.chapter_version == 2
    assert view.conflicts == [{"type": "性格漂移", "where": "p3"}]
    assert view.health_score == 80
    assert view.decisions is None


async def test_record_defaults_empty_collections() -> None:
    repo: ReviewRepo = _repo()
    view = await repo.record(PROJECT, 1, conflicts=[])
    assert view.foreshadow_sug == []
    assert view.style is None
    assert view.pace is None
    assert view.chapter_version is None


async def test_list_for_chapter_newest_first() -> None:
    repo: ReviewRepo = _repo()
    await repo.record(PROJECT, 7, conflicts=[{"n": 1}])
    await repo.record(PROJECT, 7, conflicts=[{"n": 2}])
    history = await repo.list_for_chapter(PROJECT, 7)
    assert [h.conflicts[0]["n"] for h in history] == [2, 1]  # 新→旧


async def test_list_for_chapter_isolated_by_project() -> None:
    repo: ReviewRepo = _repo()
    await repo.record(PROJECT, 1, conflicts=[])
    await repo.record(OTHER, 1, conflicts=[])
    history = await repo.list_for_chapter(PROJECT, 1)
    assert len(history) == 1
    assert all(h.project_id == PROJECT for h in history)


async def test_set_decisions_records_adjudication() -> None:
    repo: ReviewRepo = _repo()
    rec = await repo.record(PROJECT, 3, conflicts=[{"type": "设定违例"}])
    decisions = {"c0": "采纳", "c1": "忽略"}
    updated = await repo.set_decisions(rec.id, decisions=decisions)
    assert updated.id == rec.id
    assert updated.decisions == decisions


async def test_view_is_frozen() -> None:
    repo: ReviewRepo = _repo()
    view = await repo.record(PROJECT, 1, conflicts=[])
    with pytest.raises(ValidationError):
        view.health_score = 50
