from enum import StrEnum
from pydantic import BaseModel
from rid_lib import RID
from rid_lib.ext import Manifest
from rid_lib.ext.bundle import Bundle
from rid_lib.types.koi_net_node import KoiNetNode
from ..protocol.event import Event, EventType


type KnowledgeEventType = EventType | None

class KnowledgeSource(StrEnum):
    Internal = "INTERNAL"
    External = "EXTERNAL"

class KnowledgeObject(BaseModel):
    """A normalized knowledge representation for internal processing.
    
    Capable of representing an RID, manifest, bundle, or event. Contains three additional fields use for decision making in the knowledge processing pipeline. 
    
    The source indicates whether this object was generated by this node, or sourced from another node in the network. 
    
    The normalized event type indicates how the knowledge object is viewed from the perspective of this node, and what cache actions will take place. `NEW`, `UPDATE` -> cache write, `FORGET` -> cache delete, `None` -> no cache action.
    
    The network targets indicate other nodes in the network this knowledge object will be sent to. The event sent to them will be constructed from this knowledge object's RID, manifest, contents, and normalized event type.
    
    Constructors are provided to create a knowledge object from an RID, manifest, bundle, or event.
    """
    rid: RID
    manifest: Manifest | None = None
    contents: dict | None = None
    event_type: KnowledgeEventType = None
    normalized_event_type: KnowledgeEventType = None
    source: KnowledgeSource
    network_targets: set[KoiNetNode] = set()
    
    def __repr__(self):
        return f"<KObj '{self.rid}' event type: '{self.event_type}' -> '{self.normalized_event_type}', source: '{self.source}'>"
    
    @classmethod
    def from_rid(
        cls, 
        rid: RID, 
        event_type: KnowledgeEventType = None, 
        source: KnowledgeSource = KnowledgeSource.Internal
    ) -> "KnowledgeObject":
        return cls(
            rid=rid,
            event_type=event_type,
            source=source
        )
        
    @classmethod
    def from_manifest(
        cls, 
        manifest: Manifest, 
        event_type: KnowledgeEventType = None, 
        source: KnowledgeSource = KnowledgeSource.Internal
    ) -> "KnowledgeObject":
        return cls(
            rid=manifest.rid,
            manifest=manifest,
            event_type=event_type,
            source=source
        )
        
    @classmethod
    def from_bundle(
        cls, 
        bundle: Bundle, 
        event_type: KnowledgeEventType = None, 
        source: KnowledgeSource = KnowledgeSource.Internal
    ) -> "KnowledgeObject":
        return cls(
            rid=bundle.rid,
            manifest=bundle.manifest,
            contents=bundle.contents,
            event_type=event_type,
            source=source
        )
        
    @classmethod
    def from_event(
        cls,
        event: Event,
        source: KnowledgeSource = KnowledgeSource.Internal
    ) -> "KnowledgeObject":
        return cls(
            rid=event.rid,
            manifest=event.manifest,
            contents=event.contents,
            event_type=event.event_type,
            source=source
        )
    
    @property
    def bundle(self):
        if self.manifest is None or self.contents is None:
            return
        
        return Bundle(
            manifest=self.manifest,
            contents=self.contents
        )
    
    @property
    def normalized_event(self):
        if self.normalized_event_type is None:
            raise ValueError("Internal event's normalized event type is None, cannot convert to Event")
        
        elif self.normalized_event_type == EventType.FORGET:
            return Event(
                rid=self.rid,
                event_type=EventType.FORGET
            )
        
        else:    
            return Event(
                rid=self.rid,
                event_type=self.normalized_event_type,
                manifest=self.manifest,
                contents=self.contents
            )