# Standard Library packages
from uuid import uuid4
from typing import Dict
import json
from copy import deepcopy
from functools import partial
from warnings import warn

# Other Libraries
import fastjsonschema
import jsonschema


def identifiedObjectDefault(o, idString=""):
    if isinstance(o, CordraObject):
       return str(o.get_id())


def containsPair(d: dict, ds: dict) -> str:
    assert len(ds) == 1
    
    k, v = list(ds.items())[0]

    if k in d:
        if v == d[k]:
            return k
            
    return ""


def alwaysTrue(*args, **kwargs):
    return True


class CordraObject:
    _prefix: str #URL
    _schema: Dict=dict()
    _idString: str=""
    _jsonDefault = staticmethod(identifiedObjectDefault)
    _validate = staticmethod(alwaysTrue)


    @classmethod
    def update(cls):
        if "properties" in cls._schema:
            for k, v in cls._schema["properties"].items():
                if containsPair(v, {"cordra":{"type":{"autoGeneratedField":"handle"}}}):
                    cls._idString = k
                    
        cls._validate = staticmethod(partial(jsonschema.validate, schema=cls._schema))
                
        cls._jsonDefault = staticmethod(partial(identifiedObjectDefault, idString=cls._idString))

        return super().__new__(cls)

    
    def __init__(self, properties, payloads=None): 
        # Set defaults on properties
        self.properties = dict()

        if "properties" in self._schema:
            for k, v in self._schema["properties"].items():
                if "default" in v:
                    self.properties[k] = v["default"]

        self.properties.update(properties)

        self._validate(self.properties)

        self.payloads = payloads


    def json(self):
        return json.dumps(self.properties, default=self._jsonDefault)


    def get_id(self):
        if not self._idString:
            warn("No idString set on object. " +
                 "Make sure schema defines a Cordra autogenerated handle.")
            return ""

        return self.properties[self._idString]