// Copyright © 2022 The Things Network Foundation, The Things Industries B.V.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

syntax = "proto3";

import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "github.com/TheThingsIndustries/protoc-gen-go-flags/annotations.proto";
import "github.com/TheThingsIndustries/protoc-gen-go-json/annotations.proto";
import "google/api/annotations.proto";
import "google/protobuf/any.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
import "lorawan-stack/api/identifiers.proto";
import "lorawan-stack/api/enums.proto";

package ttn.lorawan.v3;

option go_package = "go.thethings.network/lorawan-stack/v3/pkg/ttnpb";

// TODO: Migrate away from GoGo Protobuf (https://github.com/TheThingsNetwork/lorawan-stack/issues/2798).
option (gogoproto.goproto_registration) = true;

message Notification {
  // The immutable ID of the notification. Generated by the server.
  string id = 1;

  // The time when the notification was triggered.
  google.protobuf.Timestamp created_at = 2;

  // The entity this notification is about.
  EntityIdentifiers entity_ids = 3;

  // The type of this notification.
  string notification_type = 4;

  // The data related to the notification.
  google.protobuf.Any data = 5;

  // If the notification was triggered by a user action, this contains the identifiers of the user that triggered the notification.
  UserIdentifiers sender_ids = 6;

  // If the notification was triggered by an event pipeline, this contains the identifiers of that pipeline.
  // EventPipelineIdentifiers event_pipeline_ids = 7;
  reserved "event_pipeline_ids"; reserved 7;

  // Relation of the notification receiver to the entity.
  repeated NotificationReceiver receivers = 8;

  // Whether an email was sent for the notification.
  bool email = 9;

  // The status of the notification.
  NotificationStatus status = 10;

  // The time when the notification status was updated.
  google.protobuf.Timestamp status_updated_at = 11;
}

enum NotificationReceiver {
  option (thethings.json.enum) = { marshal_as_string: true, prefix: "NOTIFICATION_RECEIVER" };

  NOTIFICATION_RECEIVER_UNKNOWN = 0;

  // Notification is received by collaborators of the entity.
  // If the collaborator is an organization, the notification is received by organization members.
  NOTIFICATION_RECEIVER_COLLABORATOR = 1;

  // Notification is received by administrative contact of the entity.
  // If this is an organization, the notification is received by organization members.
  NOTIFICATION_RECEIVER_ADMINISTRATIVE_CONTACT = 3;

  // Notification is received by technical contact of the entity.
  // If this is an organization, the notification is received by organization members.
  NOTIFICATION_RECEIVER_TECHNICAL_CONTACT = 4;
}

enum NotificationStatus {
  option (thethings.json.enum) = { marshal_as_string: true, prefix: "NOTIFICATION_STATUS" };

  NOTIFICATION_STATUS_UNSEEN = 0;
  NOTIFICATION_STATUS_SEEN = 1;
  NOTIFICATION_STATUS_ARCHIVED = 2;
}

message CreateNotificationRequest {
  // The entity this notification is about.
  EntityIdentifiers entity_ids = 1 [(validate.rules).message.required = true];

  // The type of this notification.
  string notification_type = 2 [
    (validate.rules).string = {
      min_len: 1,
      max_len: 100,
    }
  ];

  // The data related to the notification.
  google.protobuf.Any data = 3;

  // If the notification was triggered by a user action, this contains the identifiers of the user that triggered the notification.
  UserIdentifiers sender_ids = 4;

  // Receivers of the notification.
  repeated NotificationReceiver receivers = 5 [
    (validate.rules).repeated = {
      min_items: 1,
      unique: true,
      items: { enum: { defined_only: true } }
    }
  ];

  // Whether an email should be sent for the notification.
  bool email = 6;
}

message CreateNotificationResponse {
  string id = 1;
}

message ListNotificationsRequest {
  option (thethings.flags.message) = { select: false, set: true };

  // The IDs of the receiving user.
  UserIdentifiers receiver_ids = 1 [(validate.rules).message.required = true];

  // Select notifications with these statuses.
  // An empty list is interpreted as "all".
  repeated NotificationStatus status = 2 [
    (validate.rules).repeated = {
      unique: true,
      items: { enum: { defined_only: true } }
    }
  ];

  // Limit the number of results per page.
  uint32 limit = 3 [(validate.rules).uint32.lte = 1000];
  // Page number for pagination. 0 is interpreted as 1.
  uint32 page = 4;
}

message ListNotificationsResponse {
  repeated Notification notifications = 1;
}

message UpdateNotificationStatusRequest {
  option (thethings.flags.message) = { select: false, set: true };

  // The IDs of the receiving user.
  UserIdentifiers receiver_ids = 1 [(validate.rules).message.required = true];

  // The IDs of the notifications to update the status of.
  repeated string ids = 2 [
    (validate.rules).repeated = {
      min_items: 1,
      max_items: 1000,
      unique: true,
      items: { string: { len: 36 } }
    }
  ];

  // The status to set on the notifications.
  NotificationStatus status = 3 [
    (validate.rules).enum = {
      defined_only: true
    }
  ];
}

service NotificationService {
  // Create a new notification. Can only be called by internal services using cluster auth.
  rpc Create(CreateNotificationRequest) returns (CreateNotificationResponse);

  // List the notifications for a user or an organization.
  // When called with user credentials and empty receiver_ids, this will list
  // notifications for the current user and its organizations.
  rpc List(ListNotificationsRequest) returns (ListNotificationsResponse) {
    option (google.api.http) = {
      additional_bindings {
        get: "/users/{receiver_ids.user_id}/notifications"
      }
    };
  }

  // Batch-update multiple notifications to the same status.
  rpc UpdateStatus(UpdateNotificationStatusRequest) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      patch: "/users/{receiver_ids.user_id}/notifications"
      body: "*"
    };
  }
}

message EntityStateChangedNotification {
  State state = 1 [(validate.rules).enum.defined_only = true];
  string state_description = 2 [(validate.rules).string.max_len = 128];
}
