"""
API to the Preseem Data Model.
The API is dynamically built from the GRPC model, by creating a class under the
PreseemModel object for each GRPC module, with static methods in that class.

This model maintains a map of all objects by uuid to pb and py objs.
The python object is kept in sync with changes on the server.
Local changes to the python objects are ignored; update methods work as
per the model API, where fields are passed directly to the method which
enact changes on the server.  This implementation anticipates being able
to subscribe to changes in the future to keep the local cache up to date;
in the meantime, users can do Get or List operations to update the local
objects.

Python objects can only be created by the client as a result of a create,
list or get operation.
"""
from importlib import import_module
import os.path
import pkgutil
from ._apiutil import load_api, op, op_sync

import preseem_protobuf.model


class PreseemModel:
    """API to the Preseem Data Model."""

    def __init__(self, client, cache=False):
        """Initialize the model with a GRPC client."""
        self._client = client
        self.cache_enabled = cache  # See STM-9547
        self._types = {}

    def _load(self, wrapper):
        """Load the model and create Python wrappers for things."""
        pkgpath = os.path.dirname(preseem_protobuf.model.__file__)
        for x in pkgutil.iter_modules([pkgpath]):
            if x.name.endswith('_pb2_grpc'):
                name = x.name[:-9]  # trim "_pb2_grpc" from the name
                if name == 'common':
                    continue

                # Load the python modules for this model object type
                pmod = import_module(f'preseem_protobuf.model.{name}_pb2')
                gmod = import_module(f'preseem_protobuf.model.{name}_pb2_grpc')
                load_api(self, wrapper, pmod, gmod)

    async def init(self):
        """Dynamically load model protobuf modules."""
        await self._client.connect()
        self._load(op)

    def load(self):
        """Dynamically load model protobuf modules. (non-asyncio)"""
        self._client._connect()
        self._load(op_sync)
