rules_version = '2';

// To pick the right project: `firebase projects:list`, then `firebase use <project-name>`
// To deploy: `firebase deploy --only firestore:rules`
service cloud.firestore {
  match /databases/{database}/documents {

    function isAdmin() {
      return request.auth.token.email in [
        'akrolsmir@gmail.com',
        'jahooma@gmail.com',
        'taowell@gmail.com',
        'abc.sinclair@gmail.com',
        'manticmarkets@gmail.com',
        'iansphilips@gmail.com',
        'd4vidchee@gmail.com',
        'federicoruizcassarino@gmail.com',
        'ingawei@gmail.com'
      ]
    }

    match /stats/stats {
      allow read;
    }

    match /users/{userId} {
      allow read;
      allow update: if userId == request.auth.uid
                       && request.resource.data.diff(resource.data).affectedKeys()
                                                                    .hasOnly(['bio', 'bannerUrl', 'website', 'twitterHandle', 'discordHandle', 'followedCategories', 'lastPingTime','shouldShowWelcome', 'hasSeenContractFollowModal', 'homeSections']);
      // User referral rules
      allow update: if userId == request.auth.uid
                         && request.resource.data.diff(resource.data).affectedKeys()
                          .hasOnly(['referredByUserId', 'referredByContractId', 'referredByGroupId'])
                          // only one referral allowed per user
                          && !("referredByUserId" in resource.data)
                          // user can't refer themselves
                          && !(userId == request.resource.data.referredByUserId);
                          // quid pro quos enabled (only once though so nbd) - bc I can't make this work:
                          // && (get(/databases/$(database)/documents/users/$(request.resource.data.referredByUserId)).referredByUserId == resource.data.id);
    }

  	match /{somePath=**}/portfolioHistory/{portfolioHistoryId} {
      allow read;
    }

    match /{somePath=**}/challenges/{challengeId}{
      allow read;
    }

    match /contracts/{contractId}/follows/{userId} {
      allow read;
      allow create, delete: if userId == request.auth.uid;
    }

    match /contracts/{contractId}/challenges/{challengeId}{
      allow read;
      allow create: if request.auth.uid == request.resource.data.creatorId;
      // allow update if there have been no claims yet and if the challenge is still open
      allow update: if request.auth.uid == resource.data.creatorId;
    }

    match /users/{userId}/follows/{followUserId} {
      allow read;
      allow write: if request.auth.uid == userId;
    }

    match /users/{userId}/likes/{likeId} {
      allow read;
      allow write: if request.auth.uid == userId;
    }

    match /{somePath=**}/follows/{followUserId} {
      allow read;
    }

    match /private-users/{userId} {
      allow read: if userId == request.auth.uid || isAdmin();
      allow update: if (userId == request.auth.uid || isAdmin())
                       && request.resource.data.diff(resource.data).affectedKeys()
                         .hasOnly(['apiKey', 'notificationPreferences', 'twitchInfo']);
    }

    match /private-users/{userId}/views/{viewId} {
      allow create: if userId == request.auth.uid;
    }

    match /private-users/{userId}/events/{eventId} {
      allow create: if userId == request.auth.uid;
    }

    match /private-users/{userId}/latency/{loadTimeId} {
      allow create: if userId == request.auth.uid;
    }

    match /private-users/{userId}/cache/{docId} {
      allow read: if userId == request.auth.uid || isAdmin();
    }

    match /contracts/{contractId} {
      allow read;
      allow update: if request.resource.data.diff(resource.data).affectedKeys()
                                                                 .hasOnly(['tags', 'lowercaseTags', 'groupSlugs', 'groupLinks']);
      allow update: if request.resource.data.diff(resource.data).affectedKeys()
                                                                 .hasOnly(['description', 'closeTime', 'question', 'visibility', 'unlistedById'])
                       && resource.data.creatorId == request.auth.uid;
      allow update: if isAdmin();
          match /comments/{commentId} {
            allow create: if request.auth != null && commentMatchesUser(request.auth.uid, request.resource.data);
          }
    }

    match /{somePath=**}/bets/{betId} {
      allow read;
    }

    match /{somePath=**}/liquidity/{liquidityId} {
      allow read;
    }

    function commentMatchesUser(userId, comment) {
      // it's a bad look if someone can impersonate other ids/names/avatars so check everything
      let user = get(/databases/$(database)/documents/users/$(userId));
      return comment.userId == userId
        && comment.userName == user.data.name
        && comment.userUsername == user.data.username
        && comment.userAvatarUrl == user.data.avatarUrl;
    }

    match /{somePath=**}/comments/{commentId} {
      allow read;
    }

    match /{somePath=**}/answers/{answerId} {
      allow read;
    }


    match /{somePath=**}/followers/{userId} {
      allow read;
      allow create, update: if request.auth.uid == userId && request.resource.data.userId == userId;
      allow delete: if request.auth.uid == userId;
    }

    match /txns/{txnId} {
      allow read;
    }

    // Note: `resource` = existing doc, `request.resource` = incoming doc
    match /manalinks/{slug} {
      // Anyone can view any manalink
      allow get;
      // Only you can create a manalink with your fromId
      allow create: if request.auth.uid == request.resource.data.fromId;
      // Only you can list and change your own manalinks
      allow list, update: if request.auth.uid == resource.data.fromId;
    }

    match /users/{userId}/notifications/{notificationId} {
      allow read;
      allow update: if resource.data.userId == request.auth.uid
                       && request.resource.data.diff(resource.data).affectedKeys()
                                                                    .hasOnly(['isSeen', 'viewTime']);
    }

    match /{somePath=**}/groupMembers/{memberId} {
      allow read;
    }

    match /{somePath=**}/groupContracts/{contractId} {
      allow read;
    }

    match /groups/{groupId} {
      allow read;
      allow update: if (request.auth.uid == resource.data.creatorId || isAdmin())
                        && request.resource.data.diff(resource.data)
                        .affectedKeys()
                        .hasOnly(['name', 'about', 'anyoneCanJoin', 'aboutPostId', 'pinnedItems' ]);
      allow delete: if request.auth.uid == resource.data.creatorId;

      match /groupContracts/{contractId} {
        allow write: if isGroupMember() || request.auth.uid == get(/databases/$(database)/documents/groups/$(groupId)).data.creatorId
      }

      match /groupMembers/{memberId}{
        allow create: if request.auth.uid == get(/databases/$(database)/documents/groups/$(groupId)).data.creatorId || (request.auth.uid == request.resource.data.userId && get(/databases/$(database)/documents/groups/$(groupId)).data.anyoneCanJoin);
        allow delete: if request.auth.uid == resource.data.userId;
      }

      function isGroupMember() {
        return exists(/databases/$(database)/documents/groups/$(groupId)/groupMembers/$(request.auth.uid));
      }

      match /comments/{commentId} {
        allow read;
        allow create: if request.auth != null && commentMatchesUser(request.auth.uid, request.resource.data) && isGroupMember();
      }
    }

      match /posts/{postId} {
        allow read;
        allow update: if isAdmin() || request.auth.uid == resource.data.creatorId
                         && request.resource.data.diff(resource.data)
                         .affectedKeys()
                         .hasOnly(['name', 'content']);
        allow delete: if isAdmin() || request.auth.uid == resource.data.creatorId;
         match /comments/{commentId} {
          	allow read;
            allow create: if request.auth != null && commentMatchesUser(request.auth.uid, request.resource.data) ;
          }
      }
  }
}