"""
Bridge Facts Module for GSD-RLM Memory System.

Provides BridgeLevel enum and BridgeFact dataclass for multi-level
persistent context storage (MEM-09).

Level hierarchy:
- L0_SESSION: Facts scoped to a single agent session
- L1_PHASE: Facts scoped to a planning phase
- L2_PROJECT: Facts scoped to the entire project
- L3_WORKSPACE: Facts scoped across all projects (user-level)
"""

from dataclasses import dataclass, field
from datetime import datetime, timezone
from enum import Enum
from typing import Any, Dict, List, Optional
import uuid


def _utcnow_iso() -> str:
    """Get current UTC time as ISO string (timezone-aware)."""
    return datetime.now(timezone.utc).isoformat()


class BridgeLevel(Enum):
    """
    Scope levels for memory bridge facts.

    Defines the persistence and visibility scope of stored facts.
    Lower levels have shorter lifetimes but higher specificity.
    """

    L0_SESSION = "L0_SESSION"  # Session-scoped, temporary
    L1_PHASE = "L1_PHASE"  # Phase-scoped, persists with phase
    L2_PROJECT = "L2_PROJECT"  # Project-scoped, persists with project
    L3_WORKSPACE = "L3_WORKSPACE"  # Workspace-scoped, user-level persistence

    @property
    def priority(self) -> int:
        """Get numeric priority for level (higher = broader scope)."""
        return {
            BridgeLevel.L0_SESSION: 0,
            BridgeLevel.L1_PHASE: 1,
            BridgeLevel.L2_PROJECT: 2,
            BridgeLevel.L3_WORKSPACE: 3,
        }[self]

    @classmethod
    def from_string(cls, value: str) -> "BridgeLevel":
        """
        Convert string to BridgeLevel.

        Args:
            value: String representation (e.g., "L0_SESSION").

        Returns:
            Corresponding BridgeLevel enum value.

        Raises:
            ValueError: If string doesn't match any level.
        """
        try:
            return cls(value.upper())
        except ValueError:
            valid = [l.value for l in cls]
            raise ValueError(f"Invalid BridgeLevel '{value}'. Valid: {valid}")


@dataclass
class BridgeFact:
    """
    A fact stored in the Memory Bridge at a specific scope level.

    Facts are key-value pairs with metadata that can be stored at
    different scope levels (session, phase, project, workspace).

    Attributes:
        fact_id: Unique identifier for this fact.
        level: Scope level where this fact is stored.
        scope_id: Identifier for the specific scope (session_id, phase_id, etc.).
        key: Fact key (e.g., "user_preference_python_version").
        value: Fact value (can be string, dict, list, etc.).
        source: Where this fact originated (e.g., "user_input", "inferred").
        confidence: Confidence level for this fact (0.0 to 1.0).
        ttl: Time-to-live in seconds (None = permanent).
        created_at: ISO timestamp when fact was created.
        embedding: Optional embedding vector for semantic search.
    """

    fact_id: str
    level: BridgeLevel
    scope_id: str
    key: str
    value: Any
    source: str = "unknown"
    confidence: float = 1.0
    ttl: Optional[int] = None
    created_at: str = field(default_factory=_utcnow_iso)
    embedding: Optional[List[float]] = None

    def __post_init__(self) -> None:
        """Validate fact after initialization."""
        if not 0.0 <= self.confidence <= 1.0:
            raise ValueError(
                f"Confidence must be between 0.0 and 1.0, got {self.confidence}"
            )

        if not self.fact_id:
            raise ValueError("fact_id cannot be empty")

        if not self.key:
            raise ValueError("key cannot be empty")

        if not self.scope_id:
            raise ValueError("scope_id cannot be empty")

    @classmethod
    def create(
        cls,
        level: BridgeLevel,
        scope_id: str,
        key: str,
        value: Any,
        source: str = "unknown",
        confidence: float = 1.0,
        ttl: Optional[int] = None,
        embedding: Optional[List[float]] = None,
    ) -> "BridgeFact":
        """
        Factory method to create a new BridgeFact with auto-generated ID.

        Args:
            level: Scope level for the fact.
            scope_id: Identifier for the scope.
            key: Fact key.
            value: Fact value.
            source: Origin of the fact.
            confidence: Confidence level (0.0-1.0).
            ttl: Time-to-live in seconds.
            embedding: Optional embedding vector.

        Returns:
            New BridgeFact instance.
        """
        fact_id = f"fact_{uuid.uuid4().hex[:12]}"
        return cls(
            fact_id=fact_id,
            level=level,
            scope_id=scope_id,
            key=key,
            value=value,
            source=source,
            confidence=confidence,
            ttl=ttl,
            embedding=embedding,
        )

    def to_dict(self) -> Dict[str, Any]:
        """
        Serialize fact to dictionary for JSON storage.

        Returns:
            Dictionary representation of the fact.
        """
        return {
            "fact_id": self.fact_id,
            "level": self.level.value,
            "scope_id": self.scope_id,
            "key": self.key,
            "value": self.value,
            "source": self.source,
            "confidence": self.confidence,
            "ttl": self.ttl,
            "created_at": self.created_at,
            "embedding": self.embedding,
        }

    @classmethod
    def from_dict(cls, data: Dict[str, Any]) -> "BridgeFact":
        """
        Deserialize fact from dictionary.

        Args:
            data: Dictionary containing fact data.

        Returns:
            BridgeFact instance.

        Raises:
            KeyError: If required fields are missing.
            ValueError: If level is invalid.
        """
        return cls(
            fact_id=data["fact_id"],
            level=BridgeLevel.from_string(data["level"]),
            scope_id=data["scope_id"],
            key=data["key"],
            value=data["value"],
            source=data.get("source", "unknown"),
            confidence=data.get("confidence", 1.0),
            ttl=data.get("ttl"),
            created_at=data.get("created_at", _utcnow_iso()),
            embedding=data.get("embedding"),
        )

    def is_expired(self) -> bool:
        """
        Check if fact has expired based on TTL.

        Returns:
            True if fact has expired, False otherwise.
        """
        if self.ttl is None:
            return False

        try:
            created = datetime.fromisoformat(self.created_at)
            # Handle both timezone-aware and naive datetimes
            now = datetime.now(timezone.utc)
            if created.tzinfo is None:
                # Assume naive datetime is UTC
                created = created.replace(tzinfo=timezone.utc)
            elapsed = (now - created).total_seconds()
            return elapsed > self.ttl
        except (ValueError, TypeError):
            return False

    def __str__(self) -> str:
        """Human-readable representation."""
        return f"BridgeFact({self.key}={self.value!r} @ {self.level.value})"

    def __repr__(self) -> str:
        """Developer representation."""
        return (
            f"BridgeFact(fact_id={self.fact_id!r}, level={self.level}, "
            f"scope_id={self.scope_id!r}, key={self.key!r})"
        )
