"""Persistence data models.

Pydantic models for all persisted entities. These map to database tables.
"""

from __future__ import annotations

from enum import Enum
from typing import TYPE_CHECKING, Any, Literal

from pydantic import BaseModel, ConfigDict, Field

if TYPE_CHECKING:
    from datetime import datetime


class DocType(str, Enum):
    PATHWAY = "pathway"
    AGENT = "agent"
    ARTIFACT = "artifact"
    TEXT = "text"
    ASSET = "asset"


class RunStatus(str, Enum):
    PENDING = "pending"
    RUNNING = "running"
    COMPLETED = "completed"
    FAILED = "failed"


# =============================================================================
# Core Entities
# =============================================================================


class Workspace(BaseModel):
    model_config = ConfigDict(extra="forbid")

    id: str
    name: str
    metadata: dict[str, Any] = Field(default_factory=dict)
    created_at: datetime | None = None
    updated_at: datetime | None = None
    deleted_at: datetime | None = None


class Project(BaseModel):
    model_config = ConfigDict(extra="forbid")

    id: str
    workspace_id: str
    name: str
    root_folder_id: str
    graph_doc_id: str | None = None
    manifest_doc_id: str | None = None
    metadata: dict[str, Any] = Field(default_factory=dict)
    created_at: datetime | None = None
    updated_at: datetime | None = None
    deleted_at: datetime | None = None


class Folder(BaseModel):
    model_config = ConfigDict(extra="forbid")

    id: str
    workspace_id: str
    project_id: str | None = None
    name: str
    parent_id: str | None = None
    metadata: dict[str, Any] = Field(default_factory=dict)
    created_at: datetime | None = None
    updated_at: datetime | None = None
    deleted_at: datetime | None = None


class Document(BaseModel):
    model_config = ConfigDict(extra="forbid")

    id: str
    type: DocType
    name: str
    workspace_id: str | None = None
    project_id: str | None = None
    parent_id: str | None = None
    head_rev: str | None = None
    metadata: dict[str, Any] = Field(default_factory=dict)
    created_at: datetime | None = None
    updated_at: datetime | None = None
    deleted_at: datetime | None = None


class Revision(BaseModel):
    model_config = ConfigDict(extra="forbid")

    doc_id: str
    rev_id: str
    content: dict[str, Any]
    created_at: datetime | None = None


# =============================================================================
# Tree Projection (for UI)
# =============================================================================


class TreeDocumentNode(BaseModel):
    model_config = ConfigDict(extra="forbid")

    type: Literal["document"] = "document"
    id: str
    name: str
    doc_type: DocType
    head_rev: str | None = None
    metadata: dict[str, Any] = Field(default_factory=dict)


class TreeChildren(BaseModel):
    model_config = ConfigDict(extra="forbid")

    folders: list[TreeFolderNode] = Field(default_factory=list)
    documents: list[TreeDocumentNode] = Field(default_factory=list)


class TreeFolderNode(BaseModel):
    model_config = ConfigDict(extra="forbid")

    type: Literal["folder"] = "folder"
    id: str
    name: str
    metadata: dict[str, Any] = Field(default_factory=dict)
    children: TreeChildren


class WorkspaceTree(BaseModel):
    model_config = ConfigDict(extra="forbid")

    workspace: Workspace
    tree: TreeChildren


# =============================================================================
# Patches (for document editing)
# =============================================================================


class SnapshotPatch(BaseModel):
    """Replace the entire document content snapshot."""

    model_config = ConfigDict(extra="forbid")
    type: Literal["snapshot"] = "snapshot"
    content: dict[str, Any]


class PathwayGraphOp(BaseModel):
    """One semantic graph op."""

    model_config = ConfigDict(extra="allow")
    op: str


class PathwayGraphOpsPatch(BaseModel):
    """Apply semantic graph ops to a pathway document."""

    model_config = ConfigDict(extra="forbid")
    type: Literal["pathway_graph_ops"] = "pathway_graph_ops"
    ops: list[PathwayGraphOp] = Field(default_factory=list)


Patch = SnapshotPatch | PathwayGraphOpsPatch


class ApplyPatchResult(BaseModel):
    model_config = ConfigDict(extra="forbid")

    doc_id: str
    base_rev: str | None = None
    new_rev: str
    head_rev: str
    content: dict[str, Any]


# =============================================================================
# Runs / Execution Logs
# =============================================================================


class RunSummary(BaseModel):
    model_config = ConfigDict(extra="forbid")

    run_id: str
    workspace_id: str | None = None
    doc_id: str | None = None
    status: RunStatus = RunStatus.PENDING
    started_at_ms: int | None = None
    finished_at_ms: int | None = None
    error: str | None = None
    updated_at: str | None = None  # Set by store


class RunEvent(BaseModel):
    model_config = ConfigDict(extra="forbid")

    run_id: str
    seq: int
    ts_ms: int
    type: str
    node_id: str | None = None
    payload: dict[str, Any] = Field(default_factory=dict)


__all__ = [
    "ApplyPatchResult",
    # Enums
    "DocType",
    "Document",
    "Folder",
    # Patches
    "Patch",
    "PathwayGraphOp",
    "PathwayGraphOpsPatch",
    "Project",
    "Revision",
    "RunEvent",
    "RunStatus",
    # Runs
    "RunSummary",
    "SnapshotPatch",
    "TreeChildren",
    # Tree
    "TreeDocumentNode",
    "TreeFolderNode",
    # Core models
    "Workspace",
    "WorkspaceTree",
]
