"""_____________________________________________________________________

:PROJECT: LARAsuite

*lara_django_samples high_level_interface *

:details: lara_django_samples high_level_interface.
         - generated with 'lara-django-dev generate_high_level_interface lara_django_samples
:authors: mark doerr <mark.doerr@uni-greifswald.de>

.. note:: -
.. todo:: - 
________________________________________________________________________
"""

import json
import asyncio
import logging
import grpc
from typing import Union

from google.protobuf.json_format import MessageToDict

import lara_django_base_grpc.v1.lara_django_base_pb2 as lara_django_base_pb2
import lara_django_base_grpc.v1.lara_django_base_pb2_grpc as lara_django_base_pb2_grpc

import lara_django_samples_grpc.v1.lara_django_samples_pb2 as lara_django_samples_pb2
import lara_django_samples_grpc.v1.lara_django_samples_pb2_grpc as lara_django_samples_pb2_grpc

import  lara_django_base_grpc.lara_django_base_data_model as base_dm
import  lara_django_base_grpc.lara_django_base_interface as base_if
from  lara_django_base_grpc.abstract_interface import LARAInterface, export_file, import_file, id_cache, update_image

import  lara_django_samples_grpc.lara_django_samples_data_model as samples_dm 



#_________________  ExtraData  ______________________


class ExtraDataInterface:
    def __init__(self, channel: grpc.Channel = None, default_namespace_uri : str = None) -> None:
        self.default_namespace_uri = None
        self.default_namespace_id = None

        if default_namespace_uri is not None:
            self.default_namespace_uri = default_namespace_uri
        
        self.namespace_if = base_if.NamespaceInterface(channel)

        # self.extradata_class_client = (
        #     lara_django_samples_pb2_grpc.ExtraDataclassByIRIControllerStub(channel)
        # )
        
        self.extradata_client = lara_django_samples_pb2_grpc.ExtraDataControllerStub(channel)

        # self.extradata_full_client = lara_django_samples_pb2_grpc.ExtraDataFullControllerStub(
        #     channel
        # )

    @import_file(relation_resolver=lambda x: x)
    async def create(self,  extradata:  list[samples_dm.ExtraData] = None,  ids_only: bool = False, namespace_uri: str = None) -> Union[list[str], list[samples_dm.ExtraData]]:
        # TODO: only retrieve default_namespace_id if not already retrieved and namespace_uri is None
        self.namespace_id = None
        # extradataclass_id = None
        if namespace_uri is None and self.default_namespace_uri is not None:
            namespace_uri = self.default_namespace_uri
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        try:
            self.namespace_id = await self.namespace_if.retrieve_id(namespace_uri=namespace_uri)
        except Exception as e:
            logging.error(f"Error retrieving namespace_id: {e}")
        
        # some sample code for retrieving a class IRI and adding namespace and class to the model
        # if extradata_class_iri is None:
        #     raise ValueError("No sample class IRI is given.")
        # try:
        #     res = await self.extradata_class_client.Retrieve(
        #         lara_django_samples_pb2.ExtraDataclassRetrieveRequest(iri=extradata_class_iri)
        #     )
        #     extradataclass_id = res.extradataclass_id 
        # except Exception as e:
        #     logging.error(f"Error retrieving extradataclass_id: {e}")
        # for extradata in extradatas:
        #     extradata.namespace = self.namespace_id
        #     extradata.extradata_class = extradataclass_id

        try:
            create_coroutines = ( self.extradata_client.Create(lara_django_samples_pb2.ExtraDataRequest(**extradata.model_dump())) for extradata in extradata )
            res = await asyncio.gather(*create_coroutines)
            if ids_only:
                return [item.extradata_id for item in res]
            else:
                return res

        except Exception as e:
            logging.error(f"Error creating extradata: {e}")

    @export_file
    async def retrieve(
        self, 
        extradata_id: str = None,
        extradata_name: str = None,
        #extradata_name_full: str = None,
        filter_dict: dict = None,
        raw : bool = False
        ) -> list[samples_dm.ExtraData]:
        filter_list = []
        results_list = []
        if  extradata_id is not None:
            try:
                res =  await self.extradata_client.Retrieve(
                    lara_django_samples_pb2.ExtraDataRetrieveRequest(extradata_id=extradata_id) )
                if raw:
                    results_list.append(res)
                else:
                    results_list.append( samples_dm.ExtraData(**MessageToDict(res, preserving_proto_field_name=True)) )
            except Exception as e:
                logging.error(f"Error retrieving extradata_id: {e}")

        if extradata_name is not None:
            filter_list.append({"name": extradata_name})
        # if extradata_name_full is not None:
        #     filter_list.append({"name_full": extradata_name_full})
        if filter_dict is not None:
            filter_list.append(filter_dict)

        for filter_dict in filter_list:
            metadata = (("filters", (json.dumps(filter_dict))),)
            try:
                res = await self.extradata_client.List(
                        lara_django_samples_pb2.ExtraDataListRequest(), metadata=metadata
                      )
                if raw:
                      results_list += res.results
                else:
                    # a queue could be used here to speed up the retrieval
                    results = res.results
                    results_list += [ samples_dm.ExtraData(**MessageToDict(res, preserving_proto_field_name=True)) for res in results ]
            except Exception as e:
                logging.error(f"Error retrieving extradata_name: {e}")

        return results_list

    #@id_cache
    async def retrieve_id(self, 
                          extradata_name: str = None,
                          extradata_name_full: str = None,
                          iri: str = None,) -> str:
        if extradata_name is not None:
            filter_dict = {"name": extradata_name}
        elif extradata_name_full is not None:
            filter_dict = {"name_full": extradata_name_full}
        elif iri is not None:
            filter_dict = {"iri": iri}
            metadata = (("filters", (json.dumps(filter_dict))),)
            try:
                res = await self.extradata_client.List(
                    lara_django_samples_pb2.ExtraDataListRequest(), metadata=metadata
                )
                results = res.results
            except Exception as e:
                logging.error(f"Error retrieving extradata_name: {e}")
            if len(results) > 0 and len(results) < 2:
                return results[0].extradata_id
            else:
                raise ValueError(f"Error retrieving extradata_id - no or too many results found.")
            
        
    async def list_items(self, filter_dict: dict = None, ids_only: bool = False) -> list[str]:
        if filter_dict is None:
            res = await self.extradata_client.List(lara_django_samples_pb2.ExtraDataListRequest())
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            res = await self.extradata_client.List(
                lara_django_samples_pb2.ExtraDataListRequest(), metadata=metadata
            )
        if res is not None and ids_only:
            return [item.extradata_id for item in res.results]
        else:
            return res.results
            

    async def list_full(self, filter_dict: dict = None) :
        if self.extradata_full_client is None:
            raise ValueError("No full client available.")
        if filter_dict is None:
            return await self.extradata_full_client.List(
                lara_django_samples_pb2.ExtraDataFullListRequest()
            )
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            return await self.extradata_full_client.List(
                lara_django_samples_pb2.ExtraDataFullListRequest(), metadata=metadata
            )

    @import_file(relation_resolver=lambda x: x)
    # @update_image
    # @update_file
    async def update(self, extradata : samples_dm.ExtraData = None) -> None:
        try:
            await self.extradata_client.Update(
                lara_django_samples_pb2.ExtraDataRequest(**extradata.model_dump()
            ))
        except Exception as e:
            logging.error(f"Error updating extradata_id: {e}")

    async def delete(self, id_list: list[str] = None, filter_dict: dict = None) -> None:
        if filter_dict is not None:
            id_list = await self.list_items(filter_dict=filter_dict, ids_only=True)
    
        try:
            delete_coroutines = ( self.extradata_client.Destroy(lara_django_samples_pb2.ExtraDataDestroyRequest(extradata_id=id)) for id in id_list )
            await asyncio.gather(*delete_coroutines)
            
        except Exception as e:
            logging.error(f"Error deleting extradata_id: {e}")

#_________________  SampleLayout  ______________________


class SampleLayoutInterface:
    def __init__(self, channel: grpc.Channel = None, default_namespace_uri : str = None) -> None:
        self.default_namespace_uri = None
        self.default_namespace_id = None

        if default_namespace_uri is not None:
            self.default_namespace_uri = default_namespace_uri
        
        self.namespace_if = base_if.NamespaceInterface(channel)

        # self.samplelayout_class_client = (
        #     lara_django_samples_pb2_grpc.SampleLayoutClassByIRIControllerStub(channel)
        # )
        
        self.samplelayout_client = lara_django_samples_pb2_grpc.SampleLayoutControllerStub(channel)

        # self.samplelayout_full_client = lara_django_samples_pb2_grpc.SampleLayoutFullControllerStub(
        #     channel
        # )

    @import_file(relation_resolver=lambda x: x)
    async def create(self,  samplelayouts:  list[samples_dm.SampleLayout] = None,  ids_only: bool = False, namespace_uri: str = None) -> Union[list[str], list[samples_dm.SampleLayout]]:
        # TODO: only retrieve default_namespace_id if not already retrieved and namespace_uri is None
        self.namespace_id = None
        # samplelayoutclass_id = None
        if namespace_uri is None and self.default_namespace_uri is not None:
            namespace_uri = self.default_namespace_uri
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        try:
            self.namespace_id = await self.namespace_if.retrieve_id(namespace_uri=namespace_uri)
        except Exception as e:
            logging.error(f"Error retrieving namespace_id: {e}")
        
        # some sample code for retrieving a class IRI and adding namespace and class to the model
        # if samplelayout_class_iri is None:
        #     raise ValueError("No sample class IRI is given.")
        # try:
        #     res = await self.samplelayout_class_client.Retrieve(
        #         lara_django_samples_pb2.SampleLayoutClassRetrieveRequest(iri=samplelayout_class_iri)
        #     )
        #     samplelayoutclass_id = res.samplelayoutclass_id 
        # except Exception as e:
        #     logging.error(f"Error retrieving samplelayoutclass_id: {e}")
        # for samplelayout in samplelayouts:
        #     samplelayout.namespace = self.namespace_id
        #     samplelayout.samplelayout_class = samplelayoutclass_id

        try:
            create_coroutines = ( self.samplelayout_client.Create(lara_django_samples_pb2.SampleLayoutRequest(**samplelayout.model_dump())) for samplelayout in samplelayouts )
            res = await asyncio.gather(*create_coroutines)
            if ids_only:
                return [item.samplelayout_id for item in res]
            else:
                return res

        except Exception as e:
            logging.error(f"Error creating samplelayout: {e}")

    @export_file
    async def retrieve(
        self, 
        samplelayout_id: str = None,
        samplelayout_name: str = None,
        #samplelayout_name_full: str = None,
        filter_dict: dict = None,
        raw : bool = False
        ) -> list[samples_dm.SampleLayout]:
        filter_list = []
        results_list = []
        if  samplelayout_id is not None:
            try:
                res =  await self.samplelayout_client.Retrieve(
                    lara_django_samples_pb2.SampleLayoutRetrieveRequest(samplelayout_id=samplelayout_id) )
                if raw:
                    results_list.append(res)
                else:
                    results_list.append( samples_dm.SampleLayout(**MessageToDict(res, preserving_proto_field_name=True)) )
            except Exception as e:
                logging.error(f"Error retrieving samplelayout_id: {e}")

        if samplelayout_name is not None:
            filter_list.append({"name": samplelayout_name})
        # if samplelayout_name_full is not None:
        #     filter_list.append({"name_full": samplelayout_name_full})
        if filter_dict is not None:
            filter_list.append(filter_dict)

        for filter_dict in filter_list:
            metadata = (("filters", (json.dumps(filter_dict))),)
            try:
                res = await self.samplelayout_client.List(
                        lara_django_samples_pb2.SampleLayoutListRequest(), metadata=metadata
                      )
                if raw:
                      results_list += res.results
                else:
                    # a queue could be used here to speed up the retrieval
                    results = res.results
                    results_list += [ samples_dm.SampleLayout(**MessageToDict(res, preserving_proto_field_name=True)) for res in results ]
            except Exception as e:
                logging.error(f"Error retrieving samplelayout_name: {e}")

        return results_list

    #@id_cache
    async def retrieve_id(self, 
                          samplelayout_name: str = None,
                          samplelayout_name_full: str = None,
                          iri: str = None,) -> str:
        if samplelayout_name is not None:
            filter_dict = {"name": samplelayout_name}
        elif samplelayout_name_full is not None:
            filter_dict = {"name_full": samplelayout_name_full}
        elif iri is not None:
            filter_dict = {"iri": iri}
            metadata = (("filters", (json.dumps(filter_dict))),)
            try:
                res = await self.samplelayout_client.List(
                    lara_django_samples_pb2.SampleLayoutListRequest(), metadata=metadata
                )
                results = res.results
            except Exception as e:
                logging.error(f"Error retrieving samplelayout_name: {e}")
            if len(results) > 0 and len(results) < 2:
                return results[0].samplelayout_id
            else:
                raise ValueError(f"Error retrieving samplelayout_id - no or too many results found.")
            
        
    async def list_items(self, filter_dict: dict = None, ids_only: bool = False) -> list[str]:
        if filter_dict is None:
            res = await self.samplelayout_client.List(lara_django_samples_pb2.SampleLayoutListRequest())
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            res = await self.samplelayout_client.List(
                lara_django_samples_pb2.SampleLayoutListRequest(), metadata=metadata
            )
        if res is not None and ids_only:
            return [item.samplelayout_id for item in res.results]
        else:
            return res.results
            

    async def list_full(self, filter_dict: dict = None) :
        if self.samplelayout_full_client is None:
            raise ValueError("No full client available.")
        if filter_dict is None:
            return await self.samplelayout_full_client.List(
                lara_django_samples_pb2.SampleLayoutFullListRequest()
            )
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            return await self.samplelayout_full_client.List(
                lara_django_samples_pb2.SampleLayoutFullListRequest(), metadata=metadata
            )

    @import_file(relation_resolver=lambda x: x)
    # @update_image
    # @update_file
    async def update(self, samplelayout : samples_dm.SampleLayout = None) -> None:
        try:
            await self.samplelayout_client.Update(
                lara_django_samples_pb2.SampleLayoutRequest(**samplelayout.model_dump()
            ))
        except Exception as e:
            logging.error(f"Error updating samplelayout_id: {e}")

    async def delete(self, id_list: list[str] = None, filter_dict: dict = None) -> None:
        if filter_dict is not None:
            id_list = await self.list_items(filter_dict=filter_dict, ids_only=True)
    
        try:
            delete_coroutines = ( self.samplelayout_client.Destroy(lara_django_samples_pb2.SampleLayoutDestroyRequest(samplelayout_id=id)) for id in id_list )
            await asyncio.gather(*delete_coroutines)
            
        except Exception as e:
            logging.error(f"Error deleting samplelayout_id: {e}")

#_________________  SampleClass  ______________________


class SampleClassInterface:
    def __init__(self, channel: grpc.Channel = None, default_namespace_uri : str = None) -> None:
        self.default_namespace_uri = None
        self.default_namespace_id = None

        if default_namespace_uri is not None:
            self.default_namespace_uri = default_namespace_uri
        
        self.namespace_if = base_if.NamespaceInterface(channel)

        # self.sampleclass_class_client = (
        #     lara_django_samples_pb2_grpc.SampleClassclassByIRIControllerStub(channel)
        # )
        
        self.sampleclass_client = lara_django_samples_pb2_grpc.SampleClassControllerStub(channel)

        # self.sampleclass_full_client = lara_django_samples_pb2_grpc.SampleClassFullControllerStub(
        #     channel
        # )

    @import_file(relation_resolver=lambda x: x)
    async def create(self,  sampleclasses:  list[samples_dm.SampleClass] = None,  ids_only: bool = False, namespace_uri: str = None) -> Union[list[str], list[samples_dm.SampleClass]]:
        # TODO: only retrieve default_namespace_id if not already retrieved and namespace_uri is None
        self.namespace_id = None
        # sampleclassclass_id = None
        if namespace_uri is None and self.default_namespace_uri is not None:
            namespace_uri = self.default_namespace_uri
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        try:
            self.namespace_id = await self.namespace_if.retrieve_id(namespace_uri=namespace_uri)
        except Exception as e:
            logging.error(f"Error retrieving namespace_id: {e}")
        
        # some sample code for retrieving a class IRI and adding namespace and class to the model
        # if sampleclass_class_iri is None:
        #     raise ValueError("No sample class IRI is given.")
        # try:
        #     res = await self.sampleclass_class_client.Retrieve(
        #         lara_django_samples_pb2.SampleClassclassRetrieveRequest(iri=sampleclass_class_iri)
        #     )
        #     sampleclassclass_id = res.sampleclassclass_id 
        # except Exception as e:
        #     logging.error(f"Error retrieving sampleclassclass_id: {e}")
        # for sampleclass in sampleclasss:
        #     sampleclass.namespace = self.namespace_id
        #     sampleclass.sampleclass_class = sampleclassclass_id

        try:
            create_coroutines = ( self.sampleclass_client.Create(lara_django_samples_pb2.SampleClassRequest(**sampleclass.model_dump())) for sampleclass in sampleclasses )
            res = await asyncio.gather(*create_coroutines)
            if ids_only:
                return [item.sampleclass_id for item in res]
            else:
                return res

        except Exception as e:
            logging.error(f"Error creating sampleclass: {e}")

    @export_file
    async def retrieve(
        self, 
        sampleclass_id: str = None,
        sampleclass_name: str = None,
        #sampleclass_name_full: str = None,
        filter_dict: dict = None,
        raw : bool = False
        ) -> list[samples_dm.SampleClass]:
        filter_list = []
        results_list = []
        if  sampleclass_id is not None:
            try:
                res =  await self.sampleclass_client.Retrieve(
                    lara_django_samples_pb2.SampleClassRetrieveRequest(sampleclass_id=sampleclass_id) )
                if raw:
                    results_list.append(res)
                else:
                    results_list.append( samples_dm.SampleClass(**MessageToDict(res, preserving_proto_field_name=True)) )
            except Exception as e:
                logging.error(f"Error retrieving sampleclass_id: {e}")

        if sampleclass_name is not None:
            filter_list.append({"name": sampleclass_name})
        # if sampleclass_name_full is not None:
        #     filter_list.append({"name_full": sampleclass_name_full})
        if filter_dict is not None:
            filter_list.append(filter_dict)

        for filter_dict in filter_list:
            metadata = (("filters", (json.dumps(filter_dict))),)
            try:
                res = await self.sampleclass_client.List(
                        lara_django_samples_pb2.SampleClassListRequest(), metadata=metadata
                      )
                if raw:
                      results_list += res.results
                else:
                    # a queue could be used here to speed up the retrieval
                    results = res.results
                    results_list += [ samples_dm.SampleClass(**MessageToDict(res, preserving_proto_field_name=True)) for res in results ]
            except Exception as e:
                logging.error(f"Error retrieving sampleclass_name: {e}")

        return results_list

    #@id_cache
    async def retrieve_id(self, 
                          sampleclass_name: str = None,
                          sampleclass_name_full: str = None,
                          iri: str = None,) -> str:
        if sampleclass_name is not None:
            filter_dict = {"name": sampleclass_name}
        elif sampleclass_name_full is not None:
            filter_dict = {"name_full": sampleclass_name_full}
        elif iri is not None:
            filter_dict = {"iri": iri}
            metadata = (("filters", (json.dumps(filter_dict))),)
            try:
                res = await self.sampleclass_client.List(
                    lara_django_samples_pb2.SampleClassListRequest(), metadata=metadata
                )
                results = res.results
            except Exception as e:
                logging.error(f"Error retrieving sampleclass_name: {e}")
            if len(results) > 0 and len(results) < 2:
                return results[0].sampleclass_id
            else:
                raise ValueError(f"Error retrieving sampleclass_id - no or too many results found.")
            
        
    async def list_items(self, filter_dict: dict = None, ids_only: bool = False) -> list[str]:
        if filter_dict is None:
            res = await self.sampleclass_client.List(lara_django_samples_pb2.SampleClassListRequest())
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            res = await self.sampleclass_client.List(
                lara_django_samples_pb2.SampleClassListRequest(), metadata=metadata
            )
        if res is not None and ids_only:
            return [item.sampleclass_id for item in res.results]
        else:
            return res.results
            

    async def list_full(self, filter_dict: dict = None) :
        if self.sampleclass_full_client is None:
            raise ValueError("No full client available.")
        if filter_dict is None:
            return await self.sampleclass_full_client.List(
                lara_django_samples_pb2.SampleClassFullListRequest()
            )
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            return await self.sampleclass_full_client.List(
                lara_django_samples_pb2.SampleClassFullListRequest(), metadata=metadata
            )

    @import_file(relation_resolver=lambda x: x)
    # @update_image
    # @update_file
    async def update(self, sampleclass : samples_dm.SampleClass = None) -> None:
        try:
            await self.sampleclass_client.Update(
                lara_django_samples_pb2.SampleClassRequest(**sampleclass.model_dump()
            ))
        except Exception as e:
            logging.error(f"Error updating sampleclass_id: {e}")

    async def delete(self, id_list: list[str] = None, filter_dict: dict = None) -> None:
        if filter_dict is not None:
            id_list = await self.list_items(filter_dict=filter_dict, ids_only=True)
    
        try:
            delete_coroutines = ( self.sampleclass_client.Destroy(lara_django_samples_pb2.SampleClassDestroyRequest(sampleclass_id=id)) for id in id_list )
            await asyncio.gather(*delete_coroutines)
            
        except Exception as e:
            logging.error(f"Error deleting sampleclass_id: {e}")

#_________________  Sample  ______________________


class SampleInterface:
    def __init__(self, channel: grpc.Channel = None, default_namespace_uri : str = None) -> None:
        self.default_namespace_uri = None
        self.default_namespace_id = None

        if default_namespace_uri is not None:
            self.default_namespace_uri = default_namespace_uri
        
        self.namespace_if = base_if.NamespaceInterface(channel)

        # self.sample_class_client = (
        #     lara_django_samples_pb2_grpc.SampleClassByIRIControllerStub(channel)
        # )
        
        self.sample_client = lara_django_samples_pb2_grpc.SampleControllerStub(channel)

        # self.sample_full_client = lara_django_samples_pb2_grpc.SampleFullControllerStub(
        #     channel
        # )

    @import_file(relation_resolver=lambda x: x)
    async def create(self,  samples:  list[samples_dm.Sample] = None,  ids_only: bool = False, namespace_uri: str = None) -> Union[list[str], list[samples_dm.Sample]]:
        # TODO: only retrieve default_namespace_id if not already retrieved and namespace_uri is None
        self.namespace_id = None
        # sampleclass_id = None
        if namespace_uri is None and self.default_namespace_uri is not None:
            namespace_uri = self.default_namespace_uri
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        try:
            self.namespace_id = await self.namespace_if.retrieve_id(namespace_uri=namespace_uri)
        except Exception as e:
            logging.error(f"Error retrieving namespace_id: {e}")
        
        # some sample code for retrieving a class IRI and adding namespace and class to the model
        # if sample_class_iri is None:
        #     raise ValueError("No sample class IRI is given.")
        # try:
        #     res = await self.sample_class_client.Retrieve(
        #         lara_django_samples_pb2.SampleClassRetrieveRequest(iri=sample_class_iri)
        #     )
        #     sampleclass_id = res.sampleclass_id 
        # except Exception as e:
        #     logging.error(f"Error retrieving sampleclass_id: {e}")
        # for sample in samples:
        #     sample.namespace = self.namespace_id
        #     sample.sample_class = sampleclass_id

        try:
            create_coroutines = ( self.sample_client.Create(lara_django_samples_pb2.SampleRequest(**sample.model_dump())) for sample in samples )
            res = await asyncio.gather(*create_coroutines)
            if ids_only:
                return [item.sample_id for item in res]
            else:
                return res

        except Exception as e:
            logging.error(f"Error creating sample: {e}")

    @export_file
    async def retrieve(
        self, 
        sample_id: str = None,
        sample_name: str = None,
        #sample_name_full: str = None,
        filter_dict: dict = None,
        raw : bool = False
        ) -> list[samples_dm.Sample]:
        filter_list = []
        results_list = []
        if  sample_id is not None:
            try:
                res =  await self.sample_client.Retrieve(
                    lara_django_samples_pb2.SampleRetrieveRequest(sample_id=sample_id) )
                if raw:
                    results_list.append(res)
                else:
                    results_list.append( samples_dm.Sample(**MessageToDict(res, preserving_proto_field_name=True)) )
            except Exception as e:
                logging.error(f"Error retrieving sample_id: {e}")

        if sample_name is not None:
            filter_list.append({"name": sample_name})
        # if sample_name_full is not None:
        #     filter_list.append({"name_full": sample_name_full})
        if filter_dict is not None:
            filter_list.append(filter_dict)

        for filter_dict in filter_list:
            metadata = (("filters", (json.dumps(filter_dict))),)
            try:
                res = await self.sample_client.List(
                        lara_django_samples_pb2.SampleListRequest(), metadata=metadata
                      )
                if raw:
                      results_list += res.results
                else:
                    # a queue could be used here to speed up the retrieval
                    results = res.results
                    results_list += [ samples_dm.Sample(**MessageToDict(res, preserving_proto_field_name=True)) for res in results ]
            except Exception as e:
                logging.error(f"Error retrieving sample_name: {e}")

        return results_list

    #@id_cache
    async def retrieve_id(self, 
                          sample_name: str = None,
                          sample_name_full: str = None,
                          iri: str = None,) -> str:
        if sample_name is not None:
            filter_dict = {"name": sample_name}
        elif sample_name_full is not None:
            filter_dict = {"name_full": sample_name_full}
        elif iri is not None:
            filter_dict = {"iri": iri}
            metadata = (("filters", (json.dumps(filter_dict))),)
            try:
                res = await self.sample_client.List(
                    lara_django_samples_pb2.SampleListRequest(), metadata=metadata
                )
                results = res.results
            except Exception as e:
                logging.error(f"Error retrieving sample_name: {e}")
            if len(results) > 0 and len(results) < 2:
                return results[0].sample_id
            else:
                raise ValueError(f"Error retrieving sample_id - no or too many results found.")
            
        
    async def list_items(self, filter_dict: dict = None, ids_only: bool = False) -> list[str]:
        if filter_dict is None:
            res = await self.sample_client.List(lara_django_samples_pb2.SampleListRequest())
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            res = await self.sample_client.List(
                lara_django_samples_pb2.SampleListRequest(), metadata=metadata
            )
        if res is not None and ids_only:
            return [item.sample_id for item in res.results]
        else:
            return res.results
            

    async def list_full(self, filter_dict: dict = None) :
        if self.sample_full_client is None:
            raise ValueError("No full client available.")
        if filter_dict is None:
            return await self.sample_full_client.List(
                lara_django_samples_pb2.SampleFullListRequest()
            )
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            return await self.sample_full_client.List(
                lara_django_samples_pb2.SampleFullListRequest(), metadata=metadata
            )

    @import_file(relation_resolver=lambda x: x)
    # @update_image
    # @update_file
    async def update(self, sample : samples_dm.Sample = None) -> None:
        try:
            await self.sample_client.Update(
                lara_django_samples_pb2.SampleRequest(**sample.model_dump()
            ))
        except Exception as e:
            logging.error(f"Error updating sample_id: {e}")

    async def delete(self, id_list: list[str] = None, filter_dict: dict = None) -> None:
        if filter_dict is not None:
            id_list = await self.list_items(filter_dict=filter_dict, ids_only=True)
    
        try:
            delete_coroutines = ( self.sample_client.Destroy(lara_django_samples_pb2.SampleDestroyRequest(sample_id=id)) for id in id_list )
            await asyncio.gather(*delete_coroutines)
            
        except Exception as e:
            logging.error(f"Error deleting sample_id: {e}")

#_________________  SampleSet  ______________________


class SampleSetInterface:
    def __init__(self, channel: grpc.Channel = None, default_namespace_uri : str = None) -> None:
        self.default_namespace_uri = None
        self.default_namespace_id = None

        if default_namespace_uri is not None:
            self.default_namespace_uri = default_namespace_uri
        
        self.namespace_if = base_if.NamespaceInterface(channel)

        # self.sampleset_class_client = (
        #     lara_django_samples_pb2_grpc.SampleSetclassByIRIControllerStub(channel)
        # )
        
        self.sampleset_client = lara_django_samples_pb2_grpc.SampleSetControllerStub(channel)

        # self.sampleset_full_client = lara_django_samples_pb2_grpc.SampleSetFullControllerStub(
        #     channel
        # )

    @import_file(relation_resolver=lambda x: x)
    async def create(self,  samplesets:  list[samples_dm.SampleSet] = None,  ids_only: bool = False, namespace_uri: str = None) -> Union[list[str], list[samples_dm.SampleSet]]:
        # TODO: only retrieve default_namespace_id if not already retrieved and namespace_uri is None
        self.namespace_id = None
        # samplesetclass_id = None
        if namespace_uri is None and self.default_namespace_uri is not None:
            namespace_uri = self.default_namespace_uri
        if namespace_uri is None:
            raise ValueError("No namespace URI is given.")
        try:
            self.namespace_id = await self.namespace_if.retrieve_id(namespace_uri=namespace_uri)
        except Exception as e:
            logging.error(f"Error retrieving namespace_id: {e}")
        
        # some sample code for retrieving a class IRI and adding namespace and class to the model
        # if sampleset_class_iri is None:
        #     raise ValueError("No sample class IRI is given.")
        # try:
        #     res = await self.sampleset_class_client.Retrieve(
        #         lara_django_samples_pb2.SampleSetclassRetrieveRequest(iri=sampleset_class_iri)
        #     )
        #     samplesetclass_id = res.samplesetclass_id 
        # except Exception as e:
        #     logging.error(f"Error retrieving samplesetclass_id: {e}")
        # for sampleset in samplesets:
        #     sampleset.namespace = self.namespace_id
        #     sampleset.sampleset_class = samplesetclass_id

        try:
            create_coroutines = ( self.sampleset_client.Create(lara_django_samples_pb2.SampleSetRequest(**sampleset.model_dump())) for sampleset in samplesets )
            res = await asyncio.gather(*create_coroutines)
            if ids_only:
                return [item.sampleset_id for item in res]
            else:
                return res

        except Exception as e:
            logging.error(f"Error creating sampleset: {e}")

    @export_file
    async def retrieve(
        self, 
        sampleset_id: str = None,
        sampleset_name: str = None,
        #sampleset_name_full: str = None,
        filter_dict: dict = None,
        raw : bool = False
        ) -> list[samples_dm.SampleSet]:
        filter_list = []
        results_list = []
        if  sampleset_id is not None:
            try:
                res =  await self.sampleset_client.Retrieve(
                    lara_django_samples_pb2.SampleSetRetrieveRequest(sampleset_id=sampleset_id) )
                if raw:
                    results_list.append(res)
                else:
                    results_list.append( samples_dm.SampleSet(**MessageToDict(res, preserving_proto_field_name=True)) )
            except Exception as e:
                logging.error(f"Error retrieving sampleset_id: {e}")

        if sampleset_name is not None:
            filter_list.append({"name": sampleset_name})
        # if sampleset_name_full is not None:
        #     filter_list.append({"name_full": sampleset_name_full})
        if filter_dict is not None:
            filter_list.append(filter_dict)

        for filter_dict in filter_list:
            metadata = (("filters", (json.dumps(filter_dict))),)
            try:
                res = await self.sampleset_client.List(
                        lara_django_samples_pb2.SampleSetListRequest(), metadata=metadata
                      )
                if raw:
                      results_list += res.results
                else:
                    # a queue could be used here to speed up the retrieval
                    results = res.results
                    results_list += [ samples_dm.SampleSet(**MessageToDict(res, preserving_proto_field_name=True)) for res in results ]
            except Exception as e:
                logging.error(f"Error retrieving sampleset_name: {e}")

        return results_list

    #@id_cache
    async def retrieve_id(self, 
                          sampleset_name: str = None,
                          sampleset_name_full: str = None,
                          iri: str = None,) -> str:
        if sampleset_name is not None:
            filter_dict = {"name": sampleset_name}
        elif sampleset_name_full is not None:
            filter_dict = {"name_full": sampleset_name_full}
        elif iri is not None:
            filter_dict = {"iri": iri}
            metadata = (("filters", (json.dumps(filter_dict))),)
            try:
                res = await self.sampleset_client.List(
                    lara_django_samples_pb2.SampleSetListRequest(), metadata=metadata
                )
                results = res.results
            except Exception as e:
                logging.error(f"Error retrieving sampleset_name: {e}")
            if len(results) > 0 and len(results) < 2:
                return results[0].sampleset_id
            else:
                raise ValueError(f"Error retrieving sampleset_id - no or too many results found.")
            
        
    async def list_items(self, filter_dict: dict = None, ids_only: bool = False) -> list[str]:
        if filter_dict is None:
            res = await self.sampleset_client.List(lara_django_samples_pb2.SampleSetListRequest())
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            res = await self.sampleset_client.List(
                lara_django_samples_pb2.SampleSetListRequest(), metadata=metadata
            )
        if res is not None and ids_only:
            return [item.sampleset_id for item in res.results]
        else:
            return res.results
            

    async def list_full(self, filter_dict: dict = None) :
        if self.sampleset_full_client is None:
            raise ValueError("No full client available.")
        if filter_dict is None:
            return await self.sampleset_full_client.List(
                lara_django_samples_pb2.SampleSetFullListRequest()
            )
        else:
            metadata = (("filters", (json.dumps(filter_dict))),)
            return await self.sampleset_full_client.List(
                lara_django_samples_pb2.SampleSetFullListRequest(), metadata=metadata
            )

    @import_file(relation_resolver=lambda x: x)
    # @update_image
    # @update_file
    async def update(self, sampleset : samples_dm.SampleSet = None) -> None:
        try:
            await self.sampleset_client.Update(
                lara_django_samples_pb2.SampleSetRequest(**sampleset.model_dump()
            ))
        except Exception as e:
            logging.error(f"Error updating sampleset_id: {e}")

    async def delete(self, id_list: list[str] = None, filter_dict: dict = None) -> None:
        if filter_dict is not None:
            id_list = await self.list_items(filter_dict=filter_dict, ids_only=True)
    
        try:
            delete_coroutines = ( self.sampleset_client.Destroy(lara_django_samples_pb2.SampleSetDestroyRequest(sampleset_id=id)) for id in id_list )
            await asyncio.gather(*delete_coroutines)
            
        except Exception as e:
            logging.error(f"Error deleting sampleset_id: {e}")

