import inspect


class SELF:
    pass


class DbOperation:
    ADD = 'add'
    UPDATE = 'update'
    REMOVE = 'remove'


class ApiTypes:
    CREATE = 'create'
    UPDATE = 'update'
    GET_BASIC = 'get_basic'
    GET = 'get'
    SEARCH = 'search'
    GET_ALL = 'get_all'
    GET_BY_ID = 'get_by_id'


class SuperBase:
    ID = 'id'

    API_NAME = ''
    TABLE_NAME = ''
    SOURCE = ''
    C_SOURCE_ID = ''
    SEARCH_COLUMN = ''
    default_datatype = str

    fields = []  # fields which comes from UI and stores to table
    mandatory_fields = []
    dependent_fields = []
    fields_len_limit = {}
    no_duplicate_fields = []
    fields_datatype = {}
    foreign_fields = {}
    reverse_foreign_fields = {}
    auto_db_fields = {}     # fields not comes from UI but generated in code function and stores to table
    db_fields = []  # fields generated by db.
    condition_update_response = []  # update the response key based on condition is true
    key_params = {}
    non_ui_fields = []  # fields stored in db but will not be shown to UI. non_ui_fields subset-of auto_db_fields
    auto_ui_fields = {}     # fields generted for UI using some code function.

    ENTITIES = []

    @classmethod
    def init(cls):
        for entity in cls.ENTITIES:
            fields = []  # fields which comes from UI and stores to table
            mandatory_fields = []
            fields_len_limit = {}
            no_duplicate_fields = []
            fields_datatype = {}
            foreign_fields = {}
            reverse_foreign_fields = {}
            auto_db_fields = {}
            db_fields = [] # fields generated by db.
            condition_update_response = [] # update the response key based on condition is true
            key_params = {}
            non_ui_fields = []
            auto_ui_fields = {}

            for base_cls in inspect.getmro(entity)[::-1]:
                if base_cls == object:
                    continue
                fields += [f for f in base_cls.fields if f not in fields]
                mandatory_fields = [f for f in base_cls.mandatory_fields if f not in mandatory_fields] + mandatory_fields
                no_duplicate_fields = [f for f in base_cls.no_duplicate_fields if f not in no_duplicate_fields] + no_duplicate_fields
                db_fields += [f for f in base_cls.db_fields if f not in db_fields]
                condition_update_response = [f for f in base_cls.condition_update_response if f not in condition_update_response] + condition_update_response
                non_ui_fields = [f for f in base_cls.non_ui_fields if f not in non_ui_fields] + non_ui_fields
                fields_len_limit.update(base_cls.fields_len_limit)
                fields_datatype.update(base_cls.fields_datatype)
                foreign_fields.update(base_cls.foreign_fields)
                reverse_foreign_fields.update(base_cls.reverse_foreign_fields)
                auto_db_fields.update(base_cls.auto_db_fields)
                for key, val in base_cls.key_params.items():
                    if key in key_params and type(key_params[key]) == dict:
                        key_val = key_params[key].copy()
                        val = key_val.update(val)
                    key_params[key] = val

                auto_ui_fields.update(base_cls.auto_ui_fields)

            entity.fields = fields.copy()
            entity.mandatory_fields = mandatory_fields.copy()
            entity.no_duplicate_fields = no_duplicate_fields.copy()
            entity.db_fields = db_fields.copy()
            entity.condition_update_response = condition_update_response.copy()
            entity.non_ui_fields = non_ui_fields.copy()
            entity.fields_len_limit = fields_len_limit.copy()
            entity.fields_datatype = fields_datatype.copy()
            entity.foreign_fields = foreign_fields.copy()
            entity.reverse_foreign_fields = reverse_foreign_fields.copy()
            entity.auto_db_fields = auto_db_fields.copy()
            entity.key_params = key_params.copy()
            entity.auto_ui_fields = auto_ui_fields.copy()

    @classmethod
    def set_entities(cls, entities):
        cls.ENTITIES.clear()
        cls.ENTITIES.extend(entities)
        cls.init()

    @classmethod
    def get_fields(cls):
        return cls.fields

    @classmethod
    def get_no_duplicate_fields(cls):
        return cls.no_duplicate_fields

    @classmethod
    def get_mandatory_fields(cls):
        return cls.mandatory_fields

    @classmethod
    def get_dependent_fields(cls):
        return cls.dependent_fields

    @classmethod
    def get_all_db_fields(cls):
        return (cls.fields + list(cls.auto_db_fields.keys()) + cls.db_fields)

    @classmethod
    def get_non_ui_fields(cls):
        return cls.non_ui_fields

    @classmethod
    def get_reverse_foreign_fields(cls):
        return cls.reverse_foreign_fields

    @classmethod
    def get_foreign_fields(cls):
        return cls.foreign_fields

    @classmethod
    def get_auto_ui_fields(cls):
        return cls.auto_ui_fields

    @classmethod
    def get_auto_db_fields(cls):
        return cls.auto_db_fields

    @classmethod
    def get_fields_len_limit(cls):
        return cls.fields_len_limit

    @classmethod
    def get_condition_update_response(cls):
        return cls.condition_update_response

    @classmethod
    def get_search_field(cls):
        fields = cls.get_fields()
        search_field = fields[0] if fields else None
        return search_field

    @classmethod
    def get_datatype(cls, field):
        type = cls.fields_datatype.get(field)
        if type:
            return type
        else:
            return cls.default_datatype

    @classmethod
    def get_field_response_value(cls, field, field_var, field_response):
        return field_response

    @classmethod
    def get_field_request_value(cls, field, field_request):
        return field_request, True

    @classmethod
    def get_key_params(cls, field):
        params = cls.key_params.get(field)
        if params:
            return params
        else:
            return None

    @classmethod
    def get_log_fields(cls, field, value):
        """

        :param field:
        :param value:
        :return: log attributes_values dict
        """
        return {field: value}


class AuditHistory(SuperBase):
    TABLE_NAME = 'audit_history'
    API_NAME = TABLE_NAME

    ID = 'id'
    C_ENTITY = 'entity'
    C_ENTITY_ID = 'entity_id'
    C_ATTRIBUTE = 'attribute'
    C_VALUE = 'value'
    C_SOURCE = 'data_source'
    C_SOURCE_ID = 'data_source_attribute_id'
    C_COMMENT = 'comment'
    C_DB_OPERATION = 'db_operation'
    C_USERNAME = 'username'

    fields = [C_ENTITY, C_ENTITY_ID, C_ATTRIBUTE, C_VALUE, C_SOURCE, C_SOURCE_ID, C_DB_OPERATION, C_COMMENT, C_USERNAME]

    mandatory_fields = [C_ENTITY, C_ENTITY_ID]

    fields_datatype = {
        C_ENTITY: str,
        C_ENTITY_ID: str,
        C_ATTRIBUTE: str,
        C_VALUE: str,
        C_SOURCE: str,
        C_SOURCE_ID: str,
        C_COMMENT: str,
        C_USERNAME: str,
        C_DB_OPERATION: str
    }

    fields_len_limit = {
        C_ENTITY: 50,
        C_ENTITY_ID: 200,
        C_ATTRIBUTE: 100,
        C_SOURCE: 100,
        C_USERNAME: 200
    }

    @classmethod
    def get_insert_values(cls, _entity: SuperBase, _id: int, values: dict, username: str, comment: str='',
                          db_operation=DbOperation.UPDATE) -> dict:
        source = values.pop(_entity.SOURCE, None)
        source = _entity.get_field_request_value(_entity.SOURCE, source)[0]

        source_id = values.pop(_entity.C_SOURCE_ID, None)
        source_id = _entity.get_field_request_value(_entity.C_SOURCE_ID, source_id)[0]

        common_value = {
            cls.C_ENTITY: _entity.TABLE_NAME,
            cls.C_ENTITY_ID: str(_id),
            cls.C_USERNAME: username,
            cls.C_SOURCE: source,
            cls.C_SOURCE_ID: source_id,
            cls.C_COMMENT: comment,
            cls.C_DB_OPERATION: db_operation
        }

        value_list = []
        for key, value in values.items():
            attr_vals = _entity.get_log_fields(key, value)
            if attr_vals:
                for attr, val in attr_vals.items():
                    history_value_status = common_value.copy()
                    history_value_status.update({
                        cls.C_ATTRIBUTE: attr,
                        cls.C_VALUE: str(value)
                    })
                    value_list.append(history_value_status)
        return value_list
