import 'dart:mirrors';
import 'package:angel_framework/angel_framework.dart';
import 'errors.dart';
import 'is_server_side.dart';

/// Restricts users to accessing only their own resources.
HookedServiceEventListener restrictToOwner(
    {String idField,
    String ownerField,
    userKey,
    String errorMessage,
    getId(user),
    getOwner(obj)}) {
  return (HookedServiceEvent e) async {
    if (!isServerSide(e)) {
      var user = e.request?.grab(userKey ?? 'user');

      if (user == null)
        throw new AngelHttpException.notAuthenticated(
            message:
                'The current user is missing. You must not be authenticated.');

      _getId(user) {
        if (getId != null)
          return getId(user);
        else if (user is Map)
          return user[idField ?? 'id'];
        else if (idField == null || idField == 'id')
          return user.id;
        else
          return reflect(user).getField(new Symbol(idField ?? 'id')).reflectee;
      }

      var id = await _getId(user);

      if (id == null) throw new Exception('The current user has no ID.');

      var resource = await e.service.read(
          e.id,
          {}
            ..addAll(e.params ?? {})
            ..remove('provider'));

      if (resource != null) {
        _getOwner(obj) {
          if (getOwner != null)
            return getOwner(obj);
          else if (obj is Map)
            return obj[ownerField ?? 'userId'];
          else if (ownerField == null || ownerField == 'userId')
            return obj.userId;
          else
            return reflect(obj)
                .getField(new Symbol(ownerField ?? 'userId'))
                .reflectee;
        }

        var ownerId = await _getOwner(resource);

        if ((ownerId is Iterable && !ownerId.contains(id)) || ownerId != id)
          throw new AngelHttpException.forbidden(
              message: errorMessage ?? Errors.INSUFFICIENT_PERMISSIONS);
      }
    }
  };
}