"""
cortex/hippo/models.py — Pydantic models and dataclasses for Hippo LCM.

External-facing types use Pydantic BaseModel (validation, serialization).
Internal types use dataclass (lighter weight, matches Cortex convention).
"""

from __future__ import annotations

from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import Any, Literal, Optional, Union

from pydantic import BaseModel, Field


# ═══════════════════════════════════════════════════════════════════════════
# Enums
# ═══════════════════════════════════════════════════════════════════════════


class MessageRole(str, Enum):
    USER = "user"
    ASSISTANT = "assistant"
    SYSTEM = "system"
    TOOL = "tool"


class SummaryKind(str, Enum):
    LEAF = "leaf"
    CONDENSED = "condensed"


class ContextItemType(str, Enum):
    MESSAGE = "message"
    SUMMARY = "summary"
    CORNERSTONE = "cornerstone"


# ═══════════════════════════════════════════════════════════════════════════
# Database Records (dataclasses — internal)
# ═══════════════════════════════════════════════════════════════════════════


@dataclass
class ConversationRecord:
    conversation_id: int
    owner_id: str
    session_id: str
    title: Optional[str] = None
    bootstrapped_at: Optional[str] = None
    created_at: str = ""
    updated_at: str = ""
    session_key: Optional[str] = None


@dataclass
class MessageRecord:
    message_id: int
    conversation_id: int
    owner_id: str
    seq: int
    role: str
    content: str
    token_count: int = 0
    created_at: str = ""


@dataclass
class SummaryRecord:
    summary_id: str
    conversation_id: int
    owner_id: str
    kind: str
    depth: int
    content: str
    token_count: int = 0
    file_ids: str = "[]"
    earliest_at: Optional[str] = None
    latest_at: Optional[str] = None
    descendant_count: int = 0
    descendant_token_count: int = 0
    source_message_token_count: int = 0
    quality_score: Optional[float] = None
    embedding: Optional[bytes] = None
    signal_metadata: Optional[str] = None
    created_at: str = ""


@dataclass
class ContextItemRecord:
    conversation_id: int
    ordinal: int
    item_type: str
    message_id: Optional[int] = None
    summary_id: Optional[str] = None
    created_at: str = ""


# ═══════════════════════════════════════════════════════════════════════════
# API Models (Pydantic — external-facing)
# ═══════════════════════════════════════════════════════════════════════════


class ContextItem(BaseModel):
    """A single item in the assembled context."""
    kind: ContextItemType
    role: str = "user"
    content: Union[str, list[Any]] = ""
    token_count: int = 0
    source_id: Optional[str] = None  # message_id or summary_id or cornerstone_id
    metadata: dict[str, Any] = Field(default_factory=dict)
    reconstructed: Optional[dict[str, Any]] = Field(default=None, exclude=True)  # runtime-only, not serialized


class AssembleResult(BaseModel):
    """Result of context assembly."""
    items: list[ContextItem] = Field(default_factory=list)
    messages: list[dict[str, Any]] = Field(default_factory=list)
    total_tokens: int = 0
    cornerstone_tokens: int = 0
    summary_tokens: int = 0
    message_tokens: int = 0
    truncated: bool = False
    system_prompt_addition: Optional[str] = None


class IngestResult(BaseModel):
    """Result of ingesting a message."""
    message_id: int
    conversation_id: int
    token_count: int
    seq: int


class CompactionDecision(BaseModel):
    """Whether compaction should run."""
    should_compact: bool = False
    current_tokens: int = 0
    threshold_tokens: int = 0
    reason: str = ""


class CompactionResult(BaseModel):
    """Result of a compaction sweep."""
    summaries_created: int = 0
    messages_compacted: int = 0
    tokens_before: int = 0
    tokens_after: int = 0
    rounds: int = 0


class ConversationStats(BaseModel):
    """Statistics for a conversation."""
    conversation_id: int
    message_count: int = 0
    summary_count: int = 0
    max_depth: int = 0
    total_message_tokens: int = 0
    total_summary_tokens: int = 0
    context_item_count: int = 0
    last_compaction_at: Optional[str] = None
    cache_hit_rate: float = 0.0
    circuit_breaker_state: str = "ok"
    summary_depth_distribution: dict[int, int] = Field(default_factory=dict)
