class User(Resource):
    method_decorators = [authorize()]

    @swagger.operation(
        notes='Returns user information',
        parameters=[],
        responseClass=UserResponse.__name__,
        responseMessages=[UserResponse.description],
        tags=['Users'],
    )
    @marshal_with_parser(UserResponse)
    def get(self, uid, user_dict):
        found_user = UserTemplate.from_database(
            uid, user_manager.get_user(uid))
        return found_user, UserResponse.code

    @swagger.operation(
        notes='Returns user information',
        parameters=[],
        responseClass=UserResponse.__name__,
        responseMessages=[UserResponse.description],
        tags=['Users'],
    )
    @marshal_with_parser(UserResponse)
    def post(self, uid, user_dict):

        new_user = {
            'email': user_dict['email'],
            'name': user_dict.get('displayName', '')
        }
        new_user['id'] = uid
        new_user_template = UserTemplate.build(new_user)
        user_manager.create_or_update_user(uid, new_user_template)
        return new_user, UserResponse.code
class UserLock(Resource):
    method_decorators = [authorize()]

    @swagger.operation(
        notes='Returns a list of locks owned by a user',
        parameters=[],
        responseClass=UserLockResponse.__name__,
        responseMessages=[UserLockResponse.description],
        tags=['Locks'],
    )
    def get(self, uid, user):
        return user_lock_manager.get_user_locks(uid), UserLockResponse.code

    @swagger.operation(
        notes='Adds a valid lock id to a user\'s account',
        parameters=[PostUserLockArgs.schema],
        responseClass=UserLockResponse.__name__,
        responseMessages=[UserLockResponse.description],
        tags=['Locks'],
    )
    @use_kwargs(PostUserLockArgs.resource_fields, locations=("json", "form"))
    @marshal_with_parser(UserLockResponse)
    @record_history(
        state_changes={UserLockResponse.code: StateChange.USER_LOCK_DELETED})
    def post(self, uid, user, **args):
        user_locks = UserLocks.build(args)
        result = user_lock_manager.create_or_update_user_lock(
            uid, user_locks, should_overwrite=False)
        return result, UserLockResponse.code
class LockStatus(Resource):
    method_decorators = [authorize()]

    @swagger.operation(
        notes=LOCK_STATUS_PUT_NOTES,
        parameters=[PutLockStatusArgs.schema],
        responseClass=PutUserLockStatusResponse.__name__,
        responseMessages=[PutUserLockStatusResponse.description],
        tags=['Locks'],
    )
    @use_kwargs(PutLockStatusArgs.resource_fields, locations=("json", "form"))
    @marshal_with_parser(PutUserLockStatusResponse)
    @record_history(state_changes={
        PutUserLockStatusResponse.code:
        StateChange.LOCK_STATE_CHANGED
    })
    def put(self, uid, user, **args):
        lock_id = args['lockId']
        security_utils.verify_lock_ownership(uid, lock_id)

        # TODO: refactor this into something less hacky than enum matching
        if args.get('status') != LockStatusEnum.OPEN_REQUESTED:
            error_message = 'Users cannot set the lock to open or closed. ' + \
                'Open or close the vault to do so.'
            raise AuthorizationException(error_message)

        was_lock_removed = False
        found_password = security_utils.verify_password(
            lock_id, args.get('password'))
        if found_password.type == PasswordType.OTP:
            password_manager.remove_password(lock_id, found_password)
            was_lock_removed = True

        result = lock_manager.change_lock_status(
            lock_id, args.get('status'),
            was_lock_removed), PutUserLockStatusResponse.code

        return result

    @swagger.operation(
        notes='Gets the lock status of a user owned lock',
        responseClass=UserLockStatusResponse.__name__,
        responseMessages=[UserLockStatusResponse.description],
        tags=['Locks'],
    )
    @marshal_with_parser(UserLockStatusResponse)
    def get(self, uid, user, **kwargs):
        lock_id = kwargs['lockId']
        security_utils.verify_lock_ownership(uid, lock_id)
        return lock_manager.get_lock_status(
            lock_id), UserLockStatusResponse.code
class LockHistory(Resource):

    method_decorators = [authorize()]

    @swagger.operation(
        notes=HISTORY_INFO,
        tags=['History'],
        responseClass=LockHistoryResponse.__name__,
        responseMessages=[LockHistoryResponse.description],
    )
    @use_kwargs(GetLockHistoryArgs.resource_fields, locations=("json", "form"))
    @marshal_with_parser(LockHistoryResponse)
    def get(self, uid, user, **kwargs):
        lock_id = kwargs['lockId']
        security_utils.verify_lock_ownership(uid, lock_id)
        events = history_manager.get_events_from_lock_id(lock_id)
        return ({'events': events}, LockHistoryResponse.code)
class Lock(Resource):
    method_decorators = [authorize()]

    @swagger.operation(
        notes='Deletes a lock id associated with a user\'s account',
        # parameters=[DeleteUserLockArgs.schema],
        responseClass=UserLockResponse.__name__,
        responseMessages=[UserLockResponse.description],
        tags=['Locks'],
    )
    @use_kwargs(DeleteUserLockArgs.resource_fields, locations=("json", "form"))
    @marshal_with_parser(UserLockResponse)
    @record_history(
        state_changes={UserLockResponse.code: StateChange.USER_LOCK_ADDED})
    def delete(self, uid, user, **kwargs):
        lock_id = kwargs['lockId']
        security_utils.verify_lock_ownership(uid, lock_id)
        result = user_lock_manager.delete_user_lock(uid, lock_id)
        return result, UserLockResponse.code
class Locks(Resource):
    """
    An admin api token is required in order to use this route.
    This is used to register new lock devices to the database.
    """
    method_decorators = [authorize(admin=True)]

    @swagger.operation(notes='Creates a lock given an admin id token',
                       parameters=[PostLocksArgs.schema],
                       responseClass=AdminLocksResponse.__name__,
                       responseMessages=[AdminLocksResponse.description],
                       tags=['Admin'])
    @marshal_with_parser(AdminLocksResponse)
    @use_kwargs(PostLocksArgs.resource_fields, locations=("json", "form"))
    def post(self, uid, user, **args):
        args['secret'] = security_utils.hash_password(args['secret'])
        lock = Lock.build(args)
        lock_manager.add_lock(lock)
        return lock, AdminLocksResponse.code
class LockPasswords(Resource):

    method_decorators = [authorize()]

    @swagger.operation(
        notes='Gets metadata about a lock\'s passwords. ' + PASSWORD_INFO,
        tags=['Password Management'],
        responseClass=LockPasswordsResponse.__name__,
        responseMessages=[LockPasswordsResponse.description],
    )
    @marshal_with_parser(LockPasswordsResponse)
    def get(self, uid, user, **kwargs):
        lock_id = kwargs['lockId']
        security_utils.verify_lock_ownership(uid, lock_id)

        result = password_manager.get_passwords_metadata(lock_id), 200
        return result

    @swagger.operation(
        notes='Adds a password to a lock. ' + PASSWORD_INFO,
        tags=['Password Management'],
        parameters=[PostLockPasswordsArgs.schema],
        responseClass=LockPasswordResponse.__name__,
        responseMessages=[LockPasswordResponse.description],
    )
    @use_kwargs(PostLockPasswordsArgs.resource_fields,
                locations=("json", "form"))
    @record_history(state_changes={
        LockPasswordResponse.code: StateChange.PASSWORD_CREATED
    })
    @marshal_with_parser(LockPasswordResponse)
    def post(self, uid, user, **kwargs):
        lock_id = kwargs['lockId']
        security_utils.verify_lock_ownership(uid, lock_id)

        kwargs['password'] = security_utils.hash_password(kwargs['password'])

        password = Password.build(kwargs)
        return (password_manager.add_password(lock_id, password),
                LockPasswordResponse.code)
class LockPassword(Resource):

    method_decorators = [authorize()]

    @swagger.operation(
        notes='Gets metadata on a lock password. ' + PASSWORD_INFO,
        tags=['Password Management'],
        responseClass=LockPasswordResponse.__name__,
        responseMessages=[LockPasswordResponse.description],
    )
    @use_kwargs(GetLockPasswordMetadataArgs.resource_fields,
                locations=("json", "form"))
    @marshal_with_parser(LockPasswordResponse)
    def get(self, uid, user, **kwargs):
        lock_id = kwargs['lockId']
        password_id = kwargs['passwordId']
        security_utils.verify_lock_ownership(uid, lock_id)
        return (password_manager.get_password_metadata(lock_id, password_id),
                LockPasswordResponse.code)

    @swagger.operation(
        notes='Changes a password. ' + PASSWORD_INFO,
        tags=['Password Management'],
        parameters=[PutLockPasswordArgs.schema],
        responseClass=LockPasswordResponse.__name__,
        responseMessages=[LockPasswordResponse.description],
    )
    @use_kwargs(PutLockPasswordArgs.resource_fields,
                locations=("json", "form"))
    @marshal_with_parser(LockPasswordResponse)
    @record_history(state_changes={
        LockPasswordResponse.code:
        StateChange.PASSWORD_METADATA_CHANGED
    })
    def put(self, uid, user, **kwargs):
        lock_id = kwargs['lockId']
        password_id = kwargs['passwordId']

        # TODO(jeremy): roll this into an update request object
        update_args = self._build_update_args(kwargs)

        result = password_manager.update_password(
            lock_id,
            password_id,
            update_args,
        )
        return result, LockPasswordResponse.code

    @swagger.operation(
        notes='Gets metadata on a lock password. ' + PASSWORD_INFO,
        tags=['Password Management'],
    )
    @use_kwargs(DeleteLockPasswordArgs.resource_fields,
                locations=("json", "form"))
    @record_history(state_changes={
        LockPasswordResponse.code: StateChange.PASSWORD_DELETED
    })
    def delete(self, uid, user, **kwargs):
        lock_id = kwargs['lockId']
        password_id = kwargs['passwordId']
        security_utils.verify_lock_ownership(uid, lock_id)
        password_manager.remove_password_by_id(lock_id, password_id)
        return {}, 200

    def _build_update_args(self, kwargs):
        if 'password' in kwargs:
            kwargs['password'] = security_utils.hash_password(
                kwargs['password'])

        update_args = {}
        for arg in list(PutLockPasswordArgs.resource_fields.keys()):
            if arg in kwargs:
                update_args[arg] = kwargs[arg]
        return update_args