'''A Dataset manages multiple cordraObjects and syncs with cordraClients'''
from .cordraClient import CordraClient
from .cordraObject import CordraObject
from .cordraId import CordraId
from uuid import uuid4
from copy import copy, deepcopy

class Dataset:
    '''Dataset is a specific implementation of object management. It generates classes for
    all schemas on a CordraClient. Adds objects in python and on Cordra simultaneously. 
    Pulls objects from Cordra and adds to dataset. Manages all datasets in a large dictionary
    where python objects can be accessed by id. Can sync all objects in dataset at once.'''
    def __init__(self, client: CordraClient, cordraObjects=dict()):
        self.client = client
        self.cordraObjects = copy(cordraObjects)

        self.classes = dict()
        for name, schema in client.schemas.items():
            self.classes[name] = type(name, (CordraObject,), dict())
            self.classes[name]._schema = schema
            self.classes[name].update()


    def add(self, name, properties=dict(), payloads=dict()):
        properties = deepcopy(properties)

        if name not in self.classes:
            raise KeyError('name not in client schemas')

        classRef = self.classes[name]

        if not classRef._idString:
            raise NotImplementedError(f"Schema of object type {name} doesn't have "+
            "an autogenerated handle and cannot therefore not be added to dataset!")

        if classRef._idString not in properties:
            new_id = CordraId(handle=self.client.handle, id=str(uuid4()))
            properties.update({classRef._idString: new_id})

        obj = classRef(properties=properties, payloads=payloads)

        if obj.get_id() in self.cordraObjects:
            raise KeyError("Object id already used for another object in dataset!")

        r = self.client.create(
            obj.properties, name, payloads=obj.payloads, 
            prefix=obj.get_id().handle, suffix=obj.get_id().id
        )

        self.cordraObjects[new_id] = obj
        return obj


    def pull(self, obj_id):
        if obj_id in self.cordraObjects:
            raise KeyError("Object id already used for another object in dataset!")

        r = self.client.read(str(obj_id), full=True, getAll=True)
        obj = self.classes[r["type"]](properties=r["content"], payloads=r.get("payloads", []))
        self.cordraObjects[obj_id] = obj
        return obj


    def rem(self, obj):
        if isinstance(obj, CordraObject):
            obj_id = obj.get_id()
        else:
            obj_id = obj
        del self.cordraObjects[obj_id]


    def deleteFromCordra(self, obj):
        if isinstance(obj, CordraObject):
            obj_id = str(obj.get_id())
        else:
            obj_id = obj

        r = self.client.delete(obj_id)
        del self.cordraObjects[obj_id]
        return r


    def sync(self):
        for obj in self.cordraObjects.values():
            self.client.update(obj.properties, str(obj.get_id()))