# -*- coding: utf-8 -*-

from bravaorm.entity.datatype import *

__methods__ = ['toJSON', 'load', 'add']

class Entity():

    def __init__(self, aliases=None, context=None, _connection=None, **kw):
        self.__metadata__['data'] = {}
        self.__metadata__['aliases'] = {}
        self.__metadata__['relasionships'] = {}
        self.__metadata__['dict_cache'] = None
        self.__metadata__['connection'] = _connection
        self.__metadata__['status'] = 'created'
        self.__context__ = context
        self.load(**kw)
        if aliases:
            for c, k in aliases:
                if not k in self.__metadata__['aliases']:
                    self.__metadata__['aliases'][k] = kw[k]
                    self.__metadata__['dict_cache'] = None

    def load(self, **kw):
        if len(kw) > 0:
            for k in self.__dict__:
                if not k.startswith("__"):
                    if k in kw:
                        if self[k].__class__.__name__.startswith("Obj"):
                            self.add(k, kw[k])
                        else:
                            if self.__metadata__.get('connection') and self.__metadata__.get('status') == 'created':
                                # Bypass Type Checks for hydration performance by writing internal value directly to object value backing store
                                self[k]._value = kw[k]
                                self.__metadata__['data'][k] = kw[k]
                            else:
                                setattr(self, k, kw[k])
                    elif self[k].__class__.__name__.startswith("Obj"):
                        group_data = {key.replace(f"{k}.",''):value for key, value in kw.items() if key.startswith(f"{k}.")}
                        if len(group_data) > 0:
                            self.add(k, group_data)                   
            self.__metadata__['dict_cache'] = None

    def add(self, key=None, data=None):
        if key and data is not None:
            if "List" in self[key].__class__.__name__:
                if not key in self.__metadata__['relasionships']:
                    self.__metadata__['relasionships'][key]=[]
                if isinstance(data, list) or isinstance(data, set) or isinstance(data, map):
                    self.__metadata__['relasionships'][key].extend([item.toJSON() if hasattr(item, 'toJSON') else item for item in data])
                    self[key].value.extend([item if hasattr(item, '__context__') else self[key].type(context={'entity':self ,'key':key}, _connection=self.__metadata__.get('connection'), **item) for item in data])
                else:
                    self.__metadata__['relasionships'][key].append(data.toJSON() if hasattr(data, 'toJSON') else data)
                    self[key].value.append(data if hasattr(data, '__context__') else self[key].type(context={'entity':self ,'key':key}, _connection=self.__metadata__.get('connection') , **data))
            else:
                data = data[0] if isinstance(data, list) else data
                self.__metadata__['relasionships'][key] = data.toJSON() if hasattr(data, 'toJSON') else data
                self[key]._value = data if hasattr(data, '__context__') else self[key].type(context={'entity':self ,'key':key}, _connection=self.__metadata__.get('connection') , **data)
            self.__metadata__['dict_cache'] = None
        else:
            raise Exception("entity.add requires key and object data")

    def __getitem__(self, field):
        return super().__getattribute__(field) if hasattr(self, field) else self.__metadata__['aliases'][field]

    def __getattribute__(self, field):
        if field.startswith("__") or field in __methods__:
            return super().__getattribute__(field)
        else:
            return super().__getattribute__(field).value

    def __setattr__(self, item, value):
        try:
            if not item.startswith("__") and not "entity.datatype" in str(value.__class__):
                _is_managed = False
                try:
                    _current_attr = super().__getattribute__(item)
                    _is_managed = hasattr(_current_attr, 'value')
                except AttributeError:
                    _is_managed = False

                if _is_managed:
                    _attr_obj = super().__getattribute__(item)
                    if _attr_obj.value != value:
                        if hasattr(value, '__context__') and not value.__context__:
                            value.__context__ = {'entity': self, 'key': item}
                        _attr_obj.value = value
                        
                        if _attr_obj.__class__.__name__.startswith("Obj"):
                            self.__metadata__['relasionships'][item] = _attr_obj.value.toJSON() if hasattr(_attr_obj.value, 'toJSON') else _attr_obj.value
                        else:
                            self.__metadata__['data'][item] = _attr_obj.value.toJSON() if hasattr(_attr_obj.value, 'toJSON') else _attr_obj.value
                            
                        self.__metadata__['status'] = 'modified'
                        self.__metadata__['dict_cache'] = None
                        if self.__context__:
                            if isinstance(self.__context__, dict) and 'entity' in self.__context__:
                                _context = self.__context__['entity']
                                _context_key = self.__context__.get('key')
                            else:
                                _context = getattr(self.__context__, 'entity', self.__context__)
                                _context_key = getattr(self.__context__, 'key', None)
                                
                            if _context_key:
                                if isinstance(_context[_context_key].value, list):
                                    try:
                                        index = _context[_context_key].value.index(self)
                                        _context.__metadata__['relasionships'][_context_key][index] = self.__metadata__['data']
                                    except ValueError:
                                        pass
                                else:
                                    _context.__metadata__['relasionships'][_context_key] = self.__metadata__['data']
                            if hasattr(_context, '__metadata__'):
                                _context.__metadata__['dict_cache'] = None
                        if self.__metadata__.get('connection'):
                            self.__metadata__['connection'].add(self)

                else:
                    if getattr(self, item, None) != value:
                        super().__setattr__(item, value)
                        self.__metadata__['data'][item] = value.toJSON() if hasattr(value, 'toJSON') else value
                        self.__metadata__['status'] = 'modified'
                        self.__metadata__['dict_cache'] = None
            else:
                super().__setattr__(item, value)
        except Exception as e:
            try:
                obj_details = f"Object: {self.__class__.__name__}, Data: {self.toJSON()}"
            except Exception:
                obj_details = "Object details unavailable"
            raise Exception(f'Error setting {item} on {obj_details}. Original error: {e}')

    def toJSON(self):
        try:
            if not self.__metadata__['dict_cache']:
                self.__metadata__['dict_cache'] = {**self.__metadata__['data'], **self.__metadata__['relasionships'], **self.__metadata__['aliases']}
            return self.__metadata__['dict_cache']
        except Exception as e:
            raise e
