"""领域视图 + Repository 协议（ARCH §3.5）。

记忆服务依赖这些 **Protocol** 而非具体实现（依赖倒置）：单测注入内存 fake，
运行时注入 SQLAlchemy 实现（见 ww_core.memory.sql_repositories）。

视图是与 ORM 解耦的只读 Pydantic 快照——记忆逻辑不碰 SQLAlchemy 行，
保证确定性选择可纯函数化、可单测。所有读取统一按 project_id 过滤（数据隔离）。
"""

from __future__ import annotations

import uuid
from dataclasses import dataclass
from typing import Any, Protocol

from pydantic import BaseModel, Field

from ww_core.domain.review_repo import ReviewRepo

# ---- 只读视图（snake_case，frozen 防止意外突变）----


class OutlineView(BaseModel):
    model_config = {"frozen": True}

    volume: int
    chapter_no: int
    beats: dict[str, Any] = Field(default_factory=dict)
    foreshadow_windows: list[dict[str, Any]] = Field(default_factory=list)


class CharacterView(BaseModel):
    model_config = {"frozen": True}

    name: str
    role: str | None = None
    traits: dict[str, Any] | None = None
    appearance: str | None = None
    motive: str | None = None
    backstory: str | None = None
    arc: dict[str, Any] | None = None
    speech_tics: dict[str, Any] | None = None
    tags: list[Any] = Field(default_factory=list)
    relations: list[Any] = Field(default_factory=list)
    first_chapter: int | None = None
    latest_state: str | None = None


class WorldEntityView(BaseModel):
    model_config = {"frozen": True}

    type: str
    name: str
    rules: dict[str, Any] | None = None
    first_chapter: int | None = None
    latest_state: str | None = None


class DigestView(BaseModel):
    model_config = {"frozen": True}

    chapter_no: int
    facts: dict[str, Any] = Field(default_factory=dict)


class ForeshadowView(BaseModel):
    model_config = {"frozen": True}

    code: str
    title: str
    status: str
    planted_at: int | None = None
    expected_close_from: int | None = None
    expected_close_to: int | None = None
    content: str | None = None


class StyleView(BaseModel):
    model_config = {"frozen": True}

    dimensions: dict[str, Any] = Field(default_factory=dict)


class RuleView(BaseModel):
    model_config = {"frozen": True}

    level: str
    content: str


class ProjectSpecView(BaseModel):
    """项目书级蓝本只读快照（premise/logline/theme/title）——稳定、定型，入缓存前缀。

    这是「作品蓝本」：哪怕世界观/角色/大纲全空，它也保证写章 prompt 非空、有方向
    （修复空 prompt 导致的 400 "message must not be empty"）。
    """

    model_config = {"frozen": True}

    title: str
    logline: str | None = None
    premise: str | None = None
    theme: str | None = None
    # 题材（每书稳定项）——供 assemble 注入 genre-aware craft 片段（灵感 F1）。
    genre: str | None = None
    # 立项书级常量（灵感④）：基调/结局取向/叙事视角——每书稳定 → 入 stable_core 缓存前缀。
    tone: str | None = None
    ending_type: str | None = None
    narrative_pov: str | None = None


# ---- Repository 协议（async；统一按 project_id 过滤）----


class OutlineRepo(Protocol):
    async def get(self, project_id: uuid.UUID, chapter_no: int) -> OutlineView | None: ...

    async def list_for_project(self, project_id: uuid.UUID) -> list[OutlineView]: ...


class CharacterRepo(Protocol):
    async def list_for_project(self, project_id: uuid.UUID) -> list[CharacterView]: ...


class WorldEntityRepo(Protocol):
    async def list_for_project(self, project_id: uuid.UUID) -> list[WorldEntityView]: ...


class DigestRepo(Protocol):
    async def recent(self, project_id: uuid.UUID, k: int) -> list[DigestView]: ...


class ForeshadowRepo(Protocol):
    async def list_for_codes(
        self, project_id: uuid.UUID, codes: list[str]
    ) -> list[ForeshadowView]: ...


class StyleRepo(Protocol):
    async def latest(self, project_id: uuid.UUID) -> StyleView | None: ...


class RulesRepo(Protocol):
    async def all_for_project(self, project_id: uuid.UUID) -> list[RuleView]: ...


class ProjectSpecRepo(Protocol):
    async def spec(self, project_id: uuid.UUID) -> ProjectSpecView | None: ...


@dataclass(frozen=True)
class MemoryRepos:
    """记忆服务所需的 9 个 repo 的依赖捆绑（注入点）。

    `review` 末位默认 None：只有冲突裁决反哺（灵感②）需要它；不注入时 assemble
    优雅降级（不反哺），故既有构造点无需感知（守风险#6）。
    """

    outline: OutlineRepo
    character: CharacterRepo
    world_entity: WorldEntityRepo
    digest: DigestRepo
    foreshadow: ForeshadowRepo
    style: StyleRepo
    rules: RulesRepo
    project: ProjectSpecRepo
    review: ReviewRepo | None = None
