from dataclasses import dataclass, field
from typing import Any, Optional
from .schema import FieldSchema, ContentMediaType

__all__ = ["HandleDef", "InputHandleDef", "OutputHandleDef"]

@dataclass(frozen=True, kw_only=True)
class DataDict:
    
    def get(self, key: str, default: Any = None) -> Any:
        """obj.get('key', default)"""
        return getattr(self, key, default)
    
    def __getitem__(self, key: str) -> Any:
        """obj['key']"""
        if hasattr(self, key):
            return getattr(self, key)
        raise KeyError(f"'{key}' not found")
    
    def __contains__(self, key: str) -> bool:
        """'key' in obj"""
        return hasattr(self, key)

@dataclass(frozen=True, kw_only=True)
class HandleDef(DataDict):
    """The base handle for def which is output and input handle's common part.
    """
    handle: str
    """The name of the handle. it should be unique in handle list."""

    """The description of the handle. It is used to display in the UI."""
    description: Optional[str] = None

    json_schema: Optional[FieldSchema] = None
    """The schema of the handle. It is used to validate the handle's content."""

    kind: Optional[str] = None
    """A alias of the handle's type name. It is used to display in the UI and connect to the other handle match"""

    is_additional: Optional[bool] = None
    """If the handle is an additional handle, this field is not original field, it is generated by oocana. If True, the handle is an additional handle, otherwise it is a normal handle."""

    _raw_json_schema: Optional[dict] = field(init=False, repr=False, default=None)
    """The raw json schema of the handle. Only dict can express some fields do not exist, so keep it for compatibility."""

    def __init__(self, **kwargs):
        for key, value in kwargs.items():
            object.__setattr__(self, key, value)
        if "handle" not in kwargs:
            raise ValueError("missing attr key: 'handle'")
        json_schema = self.json_schema
        if json_schema is not None and not isinstance(json_schema, FieldSchema):
            object.__setattr__(self, "_raw_json_schema", json_schema)
            object.__setattr__(self, "json_schema", FieldSchema.generate_schema(json_schema))

    def check_handle_type(self, type: ContentMediaType) -> bool:
        if self.handle is None:
            return False
        if self.json_schema is None:
            return False
        if self.json_schema.contentMediaType is None:
            return False
        return self.json_schema.contentMediaType == type

    def json_schema_to_dict(self) -> Optional[dict]:
        """Return the raw JSON schema as a dictionary, or None if no schema exists."""
        return self._raw_json_schema if self._raw_json_schema else None

    def is_var_handle(self) -> bool:
        return self.check_handle_type("oomol/var")
    
    def is_secret_handle(self) -> bool:
        return self.check_handle_type("oomol/secret")

    def is_bin_handle(self) -> bool:
        return self.check_handle_type("oomol/bin")
    
    def is_credential_handle(self) -> bool:
        return self.check_handle_type("oomol/credential")
    
    def is_additional_handle(self) -> bool:
        """Check if the handle is an additional handle."""
        return hasattr(self, "is_additional") and self.is_additional is True

@dataclass(frozen=True, kw_only=True)
class InputHandleDef(HandleDef):

    value: Optional[Any] = None
    """default value for input handle, can be None.
    """

    nullable: Optional[bool] = None
    """If the handle value can be None. If True, the handle can be None, otherwise it must have a value."""

    _deserialize_from_cache = False
    """If True, the handle will be serialized for cache. If False, the handle will not be serialized for cache. only work for var handle and the var is pandas DataFrame."""

    _has_value: bool = field(init=False, repr=False, default=False)
    """If True, the handle has a value. If False, the handle does not have a value."""

    def is_serializable_var(self) -> bool:
        """Check if the input handle is a serializable variable."""
        return self.is_var_handle() and self._deserialize_from_cache is True
    
    def has_value(self) -> bool:
        """Check if the input handle has a value."""
        return self._has_value

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        if "value" in kwargs:
            object.__setattr__(self, "_has_value", True)
        else:
            object.__setattr__(self, "_has_value", False)


@dataclass(frozen=True, kw_only=True)
class OutputHandleDef(HandleDef):

    _serialize_for_cache: Optional[bool] = None
    """If True, the handle will be serialized for cache. If False, the handle will not be serialized for cache."""

    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def need_serialize_var_for_cache(self) -> bool:
        return self.is_var_handle() and self._serialize_for_cache is True