from unittest import TestCase
from podm import BaseJsonObject, JsonObject, Property, PropertyHandler
import podm.jsonobject
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.orm import relationship
from sqlalchemy.orm.attributes import InstrumentedAttribute

Base = declarative_base()


class CustomPropertyHandler(PropertyHandler):
    def __init__(self, obj_class, name, definition):
        super().__init__(obj_class, name, definition)

    def set(self, target, value):
        try:
            target.__dict__[self._name] = value
        except KeyError:
            pass

    def get(self, target):
        try:
            return target.__dict__[self._name]
        except KeyError:
            return None

    def json(self):
        return self._name

    def field_name(self):
        self._name


class SomeObject(JsonObject):
    value = Property()


class CustomIntrospector(podm.jsonobject.DefaultIntrospector):
    def get_properties(self, obj_class):

        properties = super().get_properties(obj_class)
        for key, val in obj_class.__dict__.items():
            if isinstance(val, InstrumentedAttribute):
                if key in obj_class.__table__.c:
                    print(key, val, obj_class.__table__.c[key].type)
                    properties[key] = CustomPropertyHandler(obj_class, key, None)

        return properties

    def property_handler_class(self):
        return CustomPropertyHandler


class BaseObject(Base, BaseJsonObject):

    _introspector = CustomIntrospector()

    __abstract__ = True

    id = Column(Integer, primary_key=True)


class House(BaseObject):

    __abstract__ = False
    __tablename__ = "house"

    name = Column(String(50))


class Person(BaseObject):

    """
    Base class for all entities that are stored in SQL Database by using
    SQLAlchemy ORM.

    Attributes:
        id: Entity id, numeric, unique for a given entity type, and autogenerated.
            Note that IDs are populated once session.flush() is performed.
    """

    __abstract__ = False
    __tablename__ = "person"

    name = Column(String(50))
    house_id = Column(Integer, ForeignKey("house.id"))
    house = relationship("House", lazy="joined")

    some_object = Property()

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self._some_object = None


class TestSQLAlchemy(TestCase):
    def test_serialize(self):

        entity = Person(id=1)
        entity.name = "Carlos"
        entity.some_object = {"1": [SomeObject()]}

        data_dict = entity.to_dict()
        print(data_dict)

        new_instance = Person.from_dict(data_dict)
        print(new_instance.id, new_instance.name)
