"""T2.3 digest 追加 repo 单测（ARCH §3.3 append-only / §5.5）。

纯内存 fake，无 DB：断言 append-only 语义（永不覆盖）、按 project_id 过滤、
返回不可变 frozen View。DB 级 append 行为同样由 `chapter_digests` 无唯一约束保证，
真实 DB 路径由 T2.7 E2E 覆盖。
"""

from __future__ import annotations

import uuid
from dataclasses import dataclass, field

import pytest
from pydantic import ValidationError
from ww_core.domain.digest_repo import DigestAppendRepo
from ww_core.domain.repositories import DigestView

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
    facts: dict[str, object]


@dataclass
class FakeDigestAppendRepo:
    """append-only 内存替身——镜像 SqlDigestAppendRepo 语义（每次 append 新增一行）。"""

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

    async def append(
        self, project_id: uuid.UUID, chapter_no: int, *, facts: dict[str, object]
    ) -> DigestView:
        self.rows.append(_Row(project_id=project_id, chapter_no=chapter_no, facts=dict(facts)))
        return DigestView(chapter_no=chapter_no, facts=dict(facts))


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


async def test_append_returns_frozen_view() -> None:
    repo: DigestAppendRepo = _repo()
    view = await repo.append(PROJECT, 1, facts={"entities": ["林动"]})
    assert isinstance(view, DigestView)
    assert view.chapter_no == 1
    assert view.facts == {"entities": ["林动"]}
    # frozen — 不可就地突变
    with pytest.raises(ValidationError):
        view.chapter_no = 9


async def test_append_is_append_only_never_overwrites() -> None:
    fake = _repo()
    repo: DigestAppendRepo = fake
    await repo.append(PROJECT, 1, facts={"v": "first"})
    await repo.append(PROJECT, 1, facts={"v": "second"})
    # 同章两次 append → 两行都在，旧的不被覆盖
    same_chapter = [r for r in fake.rows if r.chapter_no == 1]
    assert len(same_chapter) == 2
    assert [r.facts["v"] for r in same_chapter] == ["first", "second"]


async def test_append_does_not_mutate_input_facts() -> None:
    fake = _repo()
    repo: DigestAppendRepo = fake
    facts: dict[str, object] = {"entities": ["林动"]}
    await repo.append(PROJECT, 1, facts=facts)
    facts["entities"] = ["改了"]  # 改调用方的 dict 不应影响已存行
    assert fake.rows[0].facts == {"entities": ["林动"]}
