from .. import db


class BaseModel(db.Model):
    """
    This is the base for all our database models. It has common fields and
    functionality.
    """
    __abstract__ = True

    id = db.Column(db.Integer, primary_key=True)
    created_at = db.Column(db.DateTime(timezone=True),
                           server_default=db.func.now(),
                           nullable=False)
    updated_at = db.Column(db.DateTime(timezone=True),
                           server_default=db.func.now(),
                           onupdate=db.func.now(),
                           nullable=False)

    # These are designed to be overridden, as a list of field names which can
    # be returned in a JSON API response. Public fields will always be returned
    # in a JSON response for a given object, whereas private fields will trigger
    # a call to has_access_to_field to see if the requesting user can see the
    # data in that field
    private_fields = None
    public_fields = None

    def __repr__(self):
        return '<Model %s>' % self.id

    # Designed to be overridden. Check whether a user can see the contents of
    # a field on this object
    def has_access_to_field(self, user, field):
        return False

    # Convert a dict suitable for JSONification as API response
    def to_obj(self, whos_asking=None):
        if self.private_fields is None or self.public_fields is None:
            raise Exception('Models must declare private_fields and public_fields')
        ret = {'id': self.id}  # should always return object ids
        for field in self.public_fields:
            ret[field] = getattr(self, field)
        for field in self.private_fields:
            if whos_asking is None or self.has_access_to_field(whos_asking, field):
                ret[field] = getattr(self, field)
        # ensure we convert datetime objects proactively
        if 'created_at' in ret:
            ret['created_at'] = ret['created_at'].isoformat()
        if 'updated_at' in ret:
            ret['updated_at'] = ret['updated_at'].isoformat()
        return ret
