"""
cortex/core/belief_history.py — Belief change history from reconciliation (#1280).

Exposes the superseded_by/supersedes chain through the retrieval API,
allowing callers to trace how beliefs about a subject have evolved over time.

Example usage::

    history = await get_belief_history(storage, subject="user", predicate="lives_in", owner_id="default")
    # Returns: [
    #   {"claim_id": "c3", "value": "Bangkok", "status": "active", "created_at": "2024-03-15", "superseded_by": None},
    #   {"claim_id": "c2", "value": "Tokyo", "status": "superseded", "created_at": "2024-01-10", "superseded_by": "c3"},
    #   {"claim_id": "c1", "value": "San Francisco", "status": "superseded", "created_at": "2023-06-01", "superseded_by": "c2"},
    # ]
"""
from __future__ import annotations

import logging
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional

logger = logging.getLogger(__name__)


@dataclass
class BeliefChange:
    """A single belief state in the change history chain."""
    claim_id: str
    subject: str
    predicate: str
    value: str
    status: str  # 'active', 'superseded', 'deprecated', etc.
    created_at: Optional[str] = None
    valid_from: Optional[str] = None
    valid_to: Optional[str] = None
    superseded_by: Optional[str] = None
    confidence: float = 0.0
    temporal_marker: Optional[str] = None

    def to_dict(self) -> Dict[str, Any]:
        return {
            "claim_id": self.claim_id,
            "subject": self.subject,
            "predicate": self.predicate,
            "value": self.value,
            "status": self.status,
            "created_at": self.created_at,
            "valid_from": self.valid_from,
            "valid_to": self.valid_to,
            "superseded_by": self.superseded_by,
            "confidence": self.confidence,
            "temporal_marker": self.temporal_marker,
        }


@dataclass
class BeliefHistory:
    """Complete belief change history for a subject-predicate pair."""
    subject: str
    predicate: str
    changes: List[BeliefChange] = field(default_factory=list)
    current_value: Optional[str] = None

    @property
    def change_count(self) -> int:
        return len(self.changes)

    def to_dict(self) -> Dict[str, Any]:
        return {
            "subject": self.subject,
            "predicate": self.predicate,
            "current_value": self.current_value,
            "change_count": self.change_count,
            "changes": [c.to_dict() for c in self.changes],
        }


async def _execute_fetchall(storage: Any, owner_id: str, sql: str, params: tuple) -> list:
    """Execute a SELECT query and return all rows.

    Supports both:
    - Async SQLiteAdapter (production): uses ``storage.execute_fetchall()``
    - Sync sqlite3 connections (tests / legacy): uses ``_get_db()`` or ``conn``

    The production adapter's ``execute_fetchall`` ignores ``owner_id`` (connection
    is already scoped); the sync path uses ``_get_db(owner_id)`` if available.
    """
    # Async path: production SQLiteAdapter (has async execute_fetchall)
    if hasattr(storage, "execute_fetchall") and callable(storage.execute_fetchall):
        result = storage.execute_fetchall(sql, params)
        # Await if it's a coroutine (async adapter), return directly if sync mock
        import inspect
        if inspect.isawaitable(result):
            return await result
        return result

    # Sync path: test doubles with _get_db() or .conn
    db = storage._get_db(owner_id) if hasattr(storage, "_get_db") else storage.conn
    cursor = db.execute(sql, params)
    return cursor.fetchall()


async def get_belief_history(
    storage: Any,
    subject: str,
    predicate: str,
    owner_id: str = "default",
    include_deprecated: bool = True,
) -> BeliefHistory:
    """Retrieve the full belief change history for a subject-predicate pair.

    Traces the superseded_by chain to reconstruct how a belief evolved.

    Args:
        storage: StorageAdapter instance (async SQLiteAdapter or sync test double).
        subject: Subject entity name (e.g. 'user').
        predicate: Predicate name (e.g. 'lives_in').
        owner_id: Memory namespace.
        include_deprecated: Include deprecated/soft-deleted claims.

    Returns:
        BeliefHistory with chronologically ordered changes (newest first).
    """
    history = BeliefHistory(subject=subject, predicate=predicate)

    allowed_statuses = ("active", "superseded", "deprecated") if include_deprecated else ("active", "superseded")
    placeholders = ", ".join("?" for _ in allowed_statuses)

    rows = await _execute_fetchall(
        storage,
        owner_id,
        f"""
        SELECT id, subject, predicate, object_value, status,
               created_at, valid_from, valid_to, superseded_by,
               confidence, temporal_marker
        FROM semantic_claim
        WHERE owner_id = ? AND subject = ? AND predicate = ?
          AND status IN ({placeholders})
        ORDER BY created_at DESC
        """,
        (owner_id, subject, predicate, *allowed_statuses),
    )

    for row in rows:
        change = BeliefChange(
            claim_id=row[0],
            subject=row[1],
            predicate=row[2],
            value=row[3],
            status=row[4],
            created_at=row[5],
            valid_from=row[6],
            valid_to=row[7],
            superseded_by=row[8],
            confidence=float(row[9]) if row[9] else 0.0,
            temporal_marker=row[10],
        )
        history.changes.append(change)

    # Set current value from first active claim
    active = [c for c in history.changes if c.status == "active"]
    if active:
        history.current_value = active[0].value

    return history


async def get_belief_changes_for_entity(
    storage: Any,
    subject: str,
    owner_id: str = "default",
    limit: int = 50,
) -> List[BeliefHistory]:
    """Retrieve belief change histories for all predicates of a given subject.

    Args:
        storage: StorageAdapter instance (async SQLiteAdapter or sync test double).
        subject: Subject entity name.
        owner_id: Memory namespace.
        limit: Maximum number of distinct predicates to return.

    Returns:
        List of BeliefHistory objects, one per predicate that has changes.
    """
    histories: List[BeliefHistory] = []

    rows = await _execute_fetchall(
        storage,
        owner_id,
        """
        SELECT DISTINCT predicate
        FROM semantic_claim
        WHERE owner_id = ? AND subject = ? AND status IN ('active', 'superseded')
        ORDER BY predicate
        LIMIT ?
        """,
        (owner_id, subject, limit),
    )
    predicates = [row[0] for row in rows]

    for predicate in predicates:
        hist = await get_belief_history(storage, subject, predicate, owner_id)
        if hist.change_count > 1:  # Only include predicates with actual changes
            histories.append(hist)

    return histories
