"""
TIBET integration for RapidRAG - Token-based Intent, Behavior, Evidence & Trust.

Provides cryptographic provenance for all RAG operations.
"""

import hashlib
import json
from datetime import datetime
from typing import Optional, Dict, Any, List
from dataclasses import dataclass, field, asdict


@dataclass
class TIBETToken:
    """
    TIBET provenance token with Dutch semantics.

    ERIN: What's IN the action (content)
    ERAAN: What's attached (dependencies, references)
    EROMHEEN: Context around it (environment, state)
    ERACHTER: Intent behind it (why this action)
    """
    token_id: str
    token_type: str  # "ingest", "search", "query", "delete"
    timestamp: str
    actor: str

    # Dutch provenance semantics
    erin: Any  # Content of the action
    eraan: List[str] = field(default_factory=list)  # References/dependencies
    eromheen: Dict[str, Any] = field(default_factory=dict)  # Context
    erachter: str = ""  # Intent/reason

    # Chain
    parent_id: Optional[str] = None

    # Integrity
    content_hash: str = ""
    signature: str = ""

    def __post_init__(self):
        if not self.content_hash:
            self.content_hash = self._compute_hash()

    def _compute_hash(self) -> str:
        """Compute content hash for integrity."""
        data = {
            "type": self.token_type,
            "timestamp": self.timestamp,
            "actor": self.actor,
            "erin": self.erin,
            "eraan": self.eraan,
            "eromheen": self.eromheen,
            "erachter": self.erachter,
            "parent_id": self.parent_id
        }
        content = json.dumps(data, sort_keys=True, default=str)
        return hashlib.sha256(content.encode()).hexdigest()

    def to_dict(self) -> Dict[str, Any]:
        """Convert to dictionary."""
        return asdict(self)

    def to_json(self) -> str:
        """Convert to JSON string."""
        return json.dumps(self.to_dict(), default=str)


class TIBETProvider:
    """
    TIBET token provider for RapidRAG.

    Tracks provenance for all operations:
    - Document ingestion (who added what, when, why)
    - Searches (who searched, what query, what found)
    - RAG queries (full chain: search -> context -> LLM -> answer)
    """

    def __init__(
        self,
        actor: str = "rapid-rag",
        store_tokens: bool = True,
        token_callback: Optional[callable] = None
    ):
        """
        Initialize TIBET provider.

        Args:
            actor: Default actor ID for operations
            store_tokens: Keep tokens in memory
            token_callback: Optional callback for each token (for external storage)
        """
        self.actor = actor
        self.store_tokens = store_tokens
        self.token_callback = token_callback
        self._tokens: List[TIBETToken] = []
        self._token_counter = 0

    def _generate_id(self) -> str:
        """Generate unique token ID."""
        self._token_counter += 1
        timestamp = datetime.now().strftime("%Y%m%d%H%M%S%f")
        return f"tibet_{timestamp}_{self._token_counter:04d}"

    def create_token(
        self,
        token_type: str,
        erin: Any,
        eraan: Optional[List[str]] = None,
        eromheen: Optional[Dict[str, Any]] = None,
        erachter: str = "",
        actor: Optional[str] = None,
        parent_id: Optional[str] = None
    ) -> TIBETToken:
        """
        Create a new TIBET token.

        Args:
            token_type: Type of operation
            erin: Content of the action
            eraan: References/dependencies
            eromheen: Context
            erachter: Intent/reason
            actor: Override default actor
            parent_id: Parent token for chaining

        Returns:
            TIBETToken with provenance
        """
        token = TIBETToken(
            token_id=self._generate_id(),
            token_type=token_type,
            timestamp=datetime.now().isoformat(),
            actor=actor or self.actor,
            erin=erin,
            eraan=eraan or [],
            eromheen=eromheen or {},
            erachter=erachter,
            parent_id=parent_id
        )

        if self.store_tokens:
            self._tokens.append(token)

        if self.token_callback:
            self.token_callback(token)

        return token

    def token_ingest(
        self,
        doc_ids: List[str],
        source: str,
        chunk_count: int,
        erachter: str = "Document ingestion"
    ) -> TIBETToken:
        """Create token for document ingestion."""
        return self.create_token(
            token_type="ingest",
            erin={
                "doc_ids": doc_ids,
                "source": source,
                "chunk_count": chunk_count
            },
            eraan=[source],
            eromheen={
                "operation": "add_document",
                "timestamp": datetime.now().isoformat()
            },
            erachter=erachter
        )

    def token_search(
        self,
        query: str,
        results: List[Dict],
        n_requested: int,
        erachter: str = "Semantic search"
    ) -> TIBETToken:
        """Create token for search operation."""
        return self.create_token(
            token_type="search",
            erin={
                "query": query,
                "n_requested": n_requested,
                "n_returned": len(results),
                "result_ids": [r.get("id") for r in results]
            },
            eraan=[r.get("id") for r in results],
            eromheen={
                "operation": "search",
                "scores": [r.get("score") for r in results]
            },
            erachter=erachter
        )

    def token_query(
        self,
        question: str,
        search_token_id: str,
        answer: str,
        model: str,
        sources: List[Dict],
        erachter: str = "RAG query"
    ) -> TIBETToken:
        """Create token for RAG query (search + LLM)."""
        return self.create_token(
            token_type="query",
            erin={
                "question": question,
                "answer_preview": answer[:500] if len(answer) > 500 else answer,
                "model": model,
                "source_count": len(sources)
            },
            eraan=[s.get("id") for s in sources],
            eromheen={
                "operation": "rag_query",
                "model": model,
                "context_tokens": sum(len(s.get("content", "")) for s in sources)
            },
            erachter=erachter,
            parent_id=search_token_id
        )

    def token_delete(
        self,
        doc_ids: List[str],
        erachter: str = "Document deletion"
    ) -> TIBETToken:
        """Create token for deletion."""
        return self.create_token(
            token_type="delete",
            erin={"deleted_ids": doc_ids},
            eraan=doc_ids,
            eromheen={"operation": "delete"},
            erachter=erachter
        )

    def get_chain(self, token_id: str) -> List[TIBETToken]:
        """Get full provenance chain for a token."""
        chain = []
        current_id = token_id

        while current_id:
            token = self.get_token(current_id)
            if token:
                chain.append(token)
                current_id = token.parent_id
            else:
                break

        return chain

    def get_token(self, token_id: str) -> Optional[TIBETToken]:
        """Get token by ID."""
        for token in self._tokens:
            if token.token_id == token_id:
                return token
        return None

    def get_tokens(
        self,
        token_type: Optional[str] = None,
        actor: Optional[str] = None,
        limit: int = 100
    ) -> List[TIBETToken]:
        """Get tokens with optional filtering."""
        tokens = self._tokens

        if token_type:
            tokens = [t for t in tokens if t.token_type == token_type]
        if actor:
            tokens = [t for t in tokens if t.actor == actor]

        return tokens[-limit:]

    def export_tokens(self) -> List[Dict]:
        """Export all tokens as dicts."""
        return [t.to_dict() for t in self._tokens]

    def clear(self):
        """Clear all stored tokens."""
        self._tokens = []
