from __future__ import annotations
from sqloquent.asyncql import (
    AsyncSqlModel, AsyncRelatedModel, AsyncRelatedCollection, AsyncQueryBuilderProtocol
)
from .EntryType import EntryType
import packify


_None = packify.pack(None)


class ArchivedEntry(AsyncSqlModel):
    connection_info: str = ''
    table: str = 'archived_entries'
    id_column: str = 'id'
    columns: tuple[str] = (
        'id', 'type', 'amount', 'nonce', 'account_id', 'details', 'description',
        'timestamp',
    )
    id: str
    type: str
    amount: int
    nonce: bytes
    account_id: str
    details: bytes
    description: str|None
    timestamp: str|None
    account: AsyncRelatedModel
    transactions: AsyncRelatedCollection

    def __hash__(self) -> int:
        data = self.encode_value(self._encode(self.data))
        return hash(bytes(data, 'utf-8'))

    # override automatic properties
    @property
    def type(self) -> EntryType:
        """The EntryType of the Entry."""
        return EntryType(self.data['type'])
    @type.setter
    def type(self, val: EntryType):
        if type(val) is not EntryType:
            return
        self.data['type'] = val.value

    @property
    def details(self) -> packify.SerializableType:
        """A packify.SerializableType stored in the database as a blob."""
        return packify.unpack(self.data.get('details', _None))
    @details.setter
    def details(self, val: packify.SerializableType):
        self.data['details'] = packify.pack(val)

    @staticmethod
    def _encode(data: dict|None) -> dict|None:
        if type(data) is not dict:
            return data
        if type(data.get('type', None)) is EntryType:
            data['type'] = data['type'].value
        if type(data.get('details', {})) is not bytes:
            data['details'] = packify.pack(data.get('details', None))
        return data

    @staticmethod
    def _parse(data: dict|None) -> dict|None:
        if type(data) is dict and type(data['amount']) is str:
            data['amount'] = int(data['amount'])
        return data

    @classmethod
    async def insert(cls, data: dict, /, *, suppress_events: bool = False,
            parallel_events: bool = False,
        ) -> ArchivedEntry | None:
        """Ensure data is encoded before inserting."""
        result = await super().insert(
            cls._encode(data), suppress_events=suppress_events,
            parallel_events=parallel_events,
        )
        return result

    @classmethod
    async def insert_many(cls, items: list[dict], /, *, suppress_events: bool = False,
            parallel_events: bool = False,
        ) -> int:
        """Ensure data is encoded before inserting."""
        items = [cls._encode(data) for data in items]
        return await super().insert_many(
            items, suppress_events=suppress_events,
            parallel_events=parallel_events,
        )

    @classmethod
    def query(cls, conditions: dict = None) -> AsyncQueryBuilderProtocol:
        """Ensure conditions are encoded properly before querying."""
        return super().query(cls._encode(conditions))
