rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /messages/{messageId} {

      // anyone can read messages
      allow read: if true;

      // anyone can create a message
      // text must exist and be under 240 characters
      allow create: if
        request.resource.data.text is string &&
        request.resource.data.text.size() <= 240;

      // the only update allowed is an echo
      // echoCount and lastEchoAt fields can be changed
      // this prevents anyone from editing the text of someone else's message
      allow update: if
        request.resource.data.diff(resource.data)
          .affectedKeys().hasOnly(['echoCount', 'lastEchoAt']);

      // nobody can delete messages through the client
      allow delete: if false;
    }

    // --- "passport stamps" --------------------------------------------------
    // each anonymous-auth user has their own users/{uid}/stamps/{geohash4}
    // subcollection (see firebase/stamps.js) - request.auth.uid == userId
    // means a device can only ever read/write its OWN stamps, never anyone
    // else's. Stamps are permanent once earned: no update/delete from the
    // client, and create is validated the same way the messages create rule
    // above validates its fields.
    match /users/{userId}/stamps/{stampId} {
      allow read: if request.auth != null && request.auth.uid == userId;

      allow create: if
        request.auth != null && request.auth.uid == userId &&
        request.resource.data.keys().hasAll(['geohash4', 'city', 'country', 'iconId', 'color', 'earnedAt']) &&
        request.resource.data.geohash4 is string &&
        request.resource.data.city is string &&
        request.resource.data.country is string;

      allow update: if false;
      allow delete: if false;
    }

    // --- global "memory counter" (GlobalCountPill) -------------------------
    // a single shared doc, meta/stats, holding totalMessagesEverPosted.
    // anyone can read it (it's shown to every visitor); writes are limited to
    // just that one field, same "only these fields can change" pattern as the
    // echoCount update rule above, so this doc can't be hijacked to store
    // arbitrary data.
    match /meta/stats {
      allow read: if true;

      // first-ever write: only the counter field, and it must be a number
      allow create: if
        request.resource.data.keys().hasOnly(['totalMessagesEverPosted']) &&
        request.resource.data.totalMessagesEverPosted is number;

      // every later write (increment(1)) may only touch that same field
      allow update: if
        request.resource.data.diff(resource.data).affectedKeys().hasOnly(['totalMessagesEverPosted']);

      // nobody can delete the counter through the client
      allow delete: if false;
    }
  }
}