// Copyright © 2019 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/TheThingsIndustries/protoc-gen-go-flags/annotations.proto";
import "github.com/envoyproxy/protoc-gen-validate/validate/validate.proto";
import "github.com/gogo/protobuf/gogoproto/gogo.proto";
import "google/api/annotations.proto";
import "google/protobuf/empty.proto";
import "google/protobuf/field_mask.proto";
import "google/protobuf/timestamp.proto";
import "lorawan-stack/api/error.proto";
import "lorawan-stack/api/identifiers.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 ApplicationWebhookIdentifiers {
  option (thethings.flags.message) = { select: true, set: true };
  ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true];
  string webhook_id = 2 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}];
}

message ApplicationWebhookTemplateIdentifiers {
  option (thethings.flags.message) = { select: true, set: true };
  string template_id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}];
}

// ApplicationWebhookTemplateField represents a custom field that needs to be filled by the user in order to use the template.
// A field can be an API key, an username or password, or any custom platform specific field (such as region).
// The fields are meant to be replaced inside the URLs and headers when the webhook is created.
message ApplicationWebhookTemplateField {
  string id = 1 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 36}];
  string name = 2 [(validate.rules).string.max_len = 20];
  string description = 3 [(validate.rules).string.max_len = 100];
  // Secret decides if the field should be shown in plain-text or should stay hidden.
  bool secret = 4;
  string default_value = 5 [(validate.rules).string.max_len = 100];
  bool optional = 6;
}

message ApplicationWebhookTemplate {
  ApplicationWebhookTemplateIdentifiers ids = 1 [(validate.rules).message.required = true];

  string name = 2 [(validate.rules).string.max_len = 20];
  string description = 3 [(validate.rules).string.max_len = 100];
  string logo_url = 4 [(validate.rules).string.uri = true];
  string info_url = 5 [(validate.rules).string.uri = true];
  string documentation_url = 6 [(validate.rules).string.uri = true];

  // The base URL of the template. Can contain template fields, in RFC 6570 format.
  string base_url = 7 [(validate.rules).string.uri = true];

  // The HTTP headers used by the template. Both the key and the value can contain template fields.
  map<string,string> headers = 8 [
    (validate.rules).map = {
      max_pairs: 50,
      keys: { string: { max_len: 64 } },
      values: { string: { max_len: 256 } }
    }
  ];

  string format = 9 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 20}];

  repeated ApplicationWebhookTemplateField fields = 10;

  // Control the creation of the downlink queue operations API key.
  bool create_downlink_api_key = 19;

  message Message {
    // Path to append to the base URL. Can contain template fields, in RFC 6570 format.
    string path = 1 [(validate.rules).string.max_len = 64];
  }
  Message uplink_message = 11;
  Message uplink_normalized = 23;
  Message join_accept = 12;
  Message downlink_ack = 13;
  Message downlink_nack = 14;
  Message downlink_sent = 15;
  Message downlink_failed = 16;
  Message downlink_queued = 17;
  Message downlink_queue_invalidated = 21;
  Message location_solved = 18;
  Message service_data = 20;

  google.protobuf.FieldMask field_mask = 22;

  // next: 24
}

message ApplicationWebhookTemplates {
  repeated ApplicationWebhookTemplate templates = 1;
}

message ApplicationWebhookHealth {
  option (thethings.flags.message) = { select: true, set: false };
  message WebhookHealthStatusHealthy {
    option (thethings.flags.message) = { select: true, set: false };
   }
  message WebhookHealthStatusUnhealthy {
    option (thethings.flags.message) = { select: true, set: false };
    uint64 failed_attempts = 1;
    google.protobuf.Timestamp last_failed_attempt_at = 2 [(validate.rules).timestamp.required = true];
    ErrorDetails last_failed_attempt_details = 3;
  }
  oneof status {
    WebhookHealthStatusHealthy healthy = 1;
    WebhookHealthStatusUnhealthy unhealthy = 2;
  }
}

message ApplicationWebhook {
  option (thethings.flags.message) = { select: true, set: true };
  ApplicationWebhookIdentifiers ids = 1 [
    (validate.rules).message.required = true,
    (thethings.flags.field) = { select: false, hidden: true }
  ];
  google.protobuf.Timestamp created_at = 2 [
    (thethings.flags.field) = { select: false, set: false }
  ];
  google.protobuf.Timestamp updated_at = 3 [
    (thethings.flags.field) = { select: false, set: false }
  ];

  // Base URL to which the message's path is appended.
  string base_url = 4 [(validate.rules).string.uri = true];
  // HTTP headers to use.
  map<string,string> headers = 5 [
    (validate.rules).map = {
      max_pairs: 50,
      keys: { string: { max_len: 64 } },
      values: { string: { max_len: 4096 } }
    }
  ];
  // The format to use for the body.
  // Supported values depend on the Application Server configuration.
  string format = 6 [(validate.rules).string = {pattern: "^[a-z0-9](?:[-]?[a-z0-9]){2,}$" , max_len: 20}];

  // The ID of the template that was used to create the Webhook.
  ApplicationWebhookTemplateIdentifiers template_ids = 15;
  // The value of the fields used by the template. Maps field.id to the value.
  map<string,string> template_fields = 16;

  // The API key to be used for downlink queue operations.
  // The field is provided for convenience reasons, and can contain API keys with additional rights (albeit this is discouraged).
  string downlink_api_key = 17 [(validate.rules).string.max_len = 128];

  message Message {
    option (thethings.flags.message) = { select: true, set: true };
    // Path to append to the base URL.
    string path = 1 [(validate.rules).string.max_len = 64];
  }
  Message uplink_message = 7;
  Message uplink_normalized = 22;
  Message join_accept = 8;
  Message downlink_ack = 9;
  Message downlink_nack = 10;
  Message downlink_sent = 11;
  Message downlink_failed = 12;
  Message downlink_queued = 13;
  Message downlink_queue_invalidated = 19;
  Message location_solved = 14;
  Message service_data = 18;

  ApplicationWebhookHealth health_status = 20;

  google.protobuf.FieldMask field_mask = 21;

  // next: 23
}

message ApplicationWebhooks {
  repeated ApplicationWebhook webhooks = 1;
}

message ApplicationWebhookFormats {
  // Format and description.
  map<string, string> formats = 1;
}

message GetApplicationWebhookRequest {
  ApplicationWebhookIdentifiers ids = 1 [(validate.rules).message.required = true];
  google.protobuf.FieldMask field_mask = 2;
}

message ListApplicationWebhooksRequest {
  ApplicationIdentifiers application_ids = 1 [(validate.rules).message.required = true];
  google.protobuf.FieldMask field_mask = 2;
}

message SetApplicationWebhookRequest {
  ApplicationWebhook webhook = 1 [(validate.rules).message.required = true];
  google.protobuf.FieldMask field_mask = 2;
}

message GetApplicationWebhookTemplateRequest {
  ApplicationWebhookTemplateIdentifiers ids = 1 [(validate.rules).message.required = true];
  google.protobuf.FieldMask field_mask = 2;
}

message ListApplicationWebhookTemplatesRequest {
  google.protobuf.FieldMask field_mask = 1;
}

service ApplicationWebhookRegistry {
  rpc GetFormats(google.protobuf.Empty) returns (ApplicationWebhookFormats) {
    option (google.api.http) = {
      get: "/as/webhook-formats"
    };
  };

  rpc GetTemplate(GetApplicationWebhookTemplateRequest) returns (ApplicationWebhookTemplate) {
    option (google.api.http) = {
      get: "/as/webhook-templates/{ids.template_id}"
    };
  };

  rpc ListTemplates(ListApplicationWebhookTemplatesRequest) returns (ApplicationWebhookTemplates) {
    option (google.api.http) = {
      get: "/as/webhook-templates"
    };
  };

  rpc Get(GetApplicationWebhookRequest) returns (ApplicationWebhook) {
    option (google.api.http) = {
      get: "/as/webhooks/{ids.application_ids.application_id}/{ids.webhook_id}"
    };
  };

  rpc List(ListApplicationWebhooksRequest) returns (ApplicationWebhooks) {
    option (google.api.http) = {
      get: "/as/webhooks/{application_ids.application_id}"
    };
  };

  rpc Set(SetApplicationWebhookRequest) returns (ApplicationWebhook) {
    option (google.api.http) = {
      put: "/as/webhooks/{webhook.ids.application_ids.application_id}/{webhook.ids.webhook_id}"
      body: "*"
      additional_bindings {
        post: "/as/webhooks/{webhook.ids.application_ids.application_id}"
        body: "*"
      }
    };
  };

  rpc Delete(ApplicationWebhookIdentifiers) returns (google.protobuf.Empty) {
    option (google.api.http) = {
      delete: "/as/webhooks/{application_ids.application_id}/{webhook_id}",
    };
  };
}
