syntax = "proto3";

package devvit.dev_portal.app;

import "devvit/dev_portal/app/categories/categories.proto";
import "devvit/dev_portal/app/info/app_info.proto";
import "devvit/dev_portal/app/info/compute_cluster.proto";
import "devvit/dev_portal/app_version/info/app_version_info.proto";
import "devvit/dev_portal/fetch_domain/fetch_domain.proto";
import "devvit/dev_portal/fetch_domain_request/fetch_domain_request.proto";
import "devvit/dev_portal/reddit/redditor.proto";
import "google/protobuf/wrappers.proto";
import "reddit/devvit/ads/v1/ads.proto";

option go_package = "github.snooguts.net/reddit/reddit-devplatform-monorepo/go-common/generated/protos/types/devvit/devportal/app";

// region Enums

enum OrderBy {
  DATE_CREATED = 0;
  NAME = 1;
  POPULARITY = 2;
}

// endregion

// region Request parameters

// Used to search for app(s).
message AppSearchRequest {
  // What word(s) are you searching for? (NOTE: This feature is not yet
  // implemented.)
  google.protobuf.StringValue search_term = 1;
  // What categor(y|ies) does the app fit into?
  repeated categories.Categories categories = 2 [deprecated = true];
  // What page of results do you want? Defaults to the first page.
  google.protobuf.Int32Value page = 3;
  // What is the maximum number of items on a page? Defaults to 20.
  google.protobuf.Int32Value page_size = 4;
  // What field would you like to order the results by? Defaults to
  // DATE_CREATED.
  OrderBy order_by = 5;
  // Would you like your results sorted in ascending order? Defaults to false.
  google.protobuf.BoolValue is_ascending = 6;
  // Used with categoriesV2
  repeated int32 category_ids = 7;
  // Filter to apps owned by this user ID (t2_ format, eg 't2_abc123').
  optional string owner_id = 8;
}

// Used to get an app by its slug.
message GetAppBySlugRequest {
  // What is the slug of the app?
  string slug = 1;
  // How many versions of the app, at most, should we return?
  optional int32 limit = 2;
  // How many versions of the app should we skip? (Useful for pagination!)
  optional int32 offset = 3;
  // Should we hide prerelease versions?
  optional bool hide_prerelease_versions = 4;
}

message GetAllWithOwnerRequest {
  // Who's the owner in question? (Can be a t2_ ID or a username, with or
  // without `u/`)
  string owner = 1;
  // Maximum number of apps to return
  optional uint32 take = 2;
  // Number of apps to skip (for pagination)
  optional uint32 skip = 3;
}

// Used to create a new app.
message AppCreationRequest {
  // What is the new app's name?
  string name = 1;
  // What is the description of the app?
  string description = 2;
  // Is the app NSFW? NOTE: Once set, this cannot be reversed!
  bool is_nsfw = 3;
  // What categor(y|ies) are you looking in?
  repeated categories.Categories categories = 4 [deprecated = true];
  // Should we autogenerate a unique suffix for the app account?
  bool autogenerate_name = 5 [deprecated = true];
  // What's the captcha verification code?
  string captcha = 6;
  // Used with categoriesV2
  repeated int32 category_ids = 7;
}

// Used to get the X most popular apps, as determined by number of
// installations.
message GetPopularAppsRequest {
  // How many apps should we return?
  int32 limit = 1;
}

// Contains the filename, size, and hash info for a piece of media.
message MediaSignature {
  /*
     Path to the file. This isn't saved in the system; it's primary use is so
     that when we're telling the client later which pieces of media we do and do
     not have already, it knows which files we're talking about easily.
  */
  string file_path = 1;
  // Size of the file, in bytes
  int32 size = 2;
  // sha256 checksum of the file, as a hex string
  string hash = 3;
  // True if the asset is for webviews
  bool is_webview_asset = 4;
  // If this is a web view asset, and we're attempting to use direct-to-S3 uploading, the mime type of the asset
  optional string mime_type = 5;
}

// Does the app already have any media uploaded that matches these signatures?
message CheckIfMediaExistsRequest {
  // Either the app ID, or its slug, for the app we're talking about
  oneof identifier {
    string id = 1;
    string slug = 2;
  }
  // The signatures we want to look for
  repeated MediaSignature signatures = 3;
}

// Please upload this piece of media for this app.
message UploadNewMediaRequest {
  // Either the app ID, or its slug, for the app we're talking about
  oneof identifier {
    string id = 1;
    string slug = 2;
  }
  // The size of the media asset, in bytes. This will be validated, and an error
  // thrown if incorrect.
  int32 size = 3;
  // The sha256 hash of the media asset, as a hex string. This will be
  // validated, and an error thrown if incorrect.
  string hash = 4;
  // The contents of the file.
  bytes contents = 5;
  // Make this asset available for webview consumption _instead_ of app UI
  // consumption.
  optional bool webview_asset = 6;
  // The path of the file in the project assets. Necessary for webview assets.
  optional string file_path = 7;
}

// Used to update an existing app's information.
// If a field is optional, omitting it will leave its value unchanged.
message AppUpdateRequest {
  // What is the ID of the app you're trying to update?
  string id = 1;
  // What is the app's new slug?
  google.protobuf.StringValue slug = 2;
  // What's the app's new name?
  google.protobuf.StringValue name = 3;
  // What's the app's new description?
  google.protobuf.StringValue description = 4;
  // Is the app NSFW? NOTE: Once set, this cannot be reversed!
  google.protobuf.BoolValue is_nsfw = 5;

  // Is the app delisted? (This is only editable by admins.)
  google.protobuf.BoolValue is_delisted = 6;

  // Created timestamp and owner are not editable

  // What categor(y|ies) does the app belong in?
  repeated categories.Categories categories = 7 [deprecated = true];

  // What are the terms and conditions of using this app?
  google.protobuf.StringValue terms_and_conditions = 8;

  // What is the privacy policy this app?
  google.protobuf.StringValue privacy_policy = 9;

  // What is the default compute pool that new versions should be assigned?
  optional app_version.info.ComputePool default_pool = 10;

  // Is the app allowed to use the webview block and manage webview assets?
  // Deprecated: Use capabilities instead.
  // TODO: remove this after 2 releases. sometime in (Q4 2024)
  optional bool is_webview_enabled = 11 [deprecated = true];

  // Is the app installation in small subreddits restricted (the actual limit is
  // specified in the code)
  optional bool min_subreddit_size_restriction_enabled = 13;

  reserved 12; // deprecated field:capabilities
  reserved "capabilities";

  // The promotion status of the app, for ads team use to mark
  // promoted apps and what level they are promoted.
  optional .reddit.devvit.ads.v1.AppPromoStatus promo_status = 14;

  // Used with categoriesV2
  repeated int32 category_ids = 15;

  // Is the app archived? (only app owners and admins can archive an app)
  // This field acts as a "soft delete" or deprecation status for an app:
  // - hides from search results
  // - prevents new installs (by anyone but the app owner)
  google.protobuf.BoolValue is_archived = 16;

  // What is the default compute cluster for new installations?
  optional info.ComputeCluster default_compute_cluster = 17;

  // Whether the app is developed by a trusted developer and should have more permissive auto review thresholds
  optional bool is_trusted = 18;
}

// endregion

// region Response types

// Contains the basic information about an app, as well as all versions of the
// app, and any statistics about the app that we want to include.
message FullAppInfo {
  info.AppInfo app = 1;
  repeated app_version.info.AppVersionInfo versions = 2;
  repeated fetch_domain_request.FetchDomainRequestInfo fetch_domain_requests = 3;
}

// Represents information about an app and its domain info, including domain requests and allowed domains.
message AppWithDomainInfo {
  // The ID of the app, eg '6d0a383a-eb60-44ed-92c5-62b6261177f8'
  string app_id = 1;
  // The slug of the app, eg 'wsb-app'
  string app_slug = 2;
  // The name of the app, eg 'WSB App'
  string app_name = 3;
  // The account of the app owner, eg 't2_3c4d'
  devvit.dev_portal.reddit.Redditor app_owner_account = 4;
  // The list of fetch domain requests for the app.
  repeated fetch_domain_request.FetchDomainRequestInfo domain_requests = 5;
  // The list of allowed domains for the app.
  repeated fetch_domain.FetchDomain allowed_domains = 6;
}

// Request used to get an app by its slug with its domain info.
message GetAppBySlugWithDomainInfoRequest {
  // The slug of app, eg 'pixelary-game'
  string app_slug = 1;
}

// Request used to get a list of apps with their domain info.
message GetAppsWithDomainInfoRequest {
  // How many apps to return in a single page.
  int32 page_size = 1;
  // The page number of apps to return. The page number should start at 1.
  int32 page_number = 2;
}

// Response containing a list of apps with their domain info.
message GetAppsWithDomainInfoResponse {
  // The list of apps with their domain info.
  repeated AppWithDomainInfo apps = 1;
  // Indicates whether or not there is a next page of apps.
  optional bool has_next_page = 2;
}

// Request used to add many domains to an app's 'allowedDomains' list.
message AddDomainsToAppRequest {
  // The ID of the app to add the domains to, eg '6d0a383a-eb60-44ed-92c5-62b6261177f8'
  string app_id = 1;
  // The domains to add to the app, eg 'hello-world.com', 'w3schools.org'
  repeated string domains = 2;
}

// Request used to remove a domain from an app's 'allowedDomains' list.
message RemoveDomainFromAppRequest {
  // The ID of the app to remove the domain from, eg '6d0a383a-eb60-44ed-92c5-62b6261177f8'
  string app_id = 1;
  // The domain to remove from the app, eg 'hello-world.com'
  string domain = 2;
}

// Contains the status of a piece of media - whether we've seen it before or
// not.
message MediaSignatureStatus {
  // Path to the file, as was given in the request object
  string file_path = 1;
  // Is this file new to the app?
  bool is_new = 2;
  // If the file isn't new, will contain the media ID of the existing asset.
  optional string existing_media_id = 3;
  // If the file is new, a webview asset, and we were given a mime type, this will contain the upload URL to use to upload it.
  optional string upload_url = 4;
  // If the file is new, a webview asset, and we were given a mime type, this will contain the headers to send with the upload.
  map<string, string> upload_headers = 5;
}

// A list of the status of each media signature we were asked about.
message CheckIfMediaExistsResponse {
  // The status of each requested media signature - whether we've seen it before
  // or not.
  repeated MediaSignatureStatus statuses = 1;
}

// The response given when a new piece of media is uploaded.
message UploadNewMediaResponse {
  // The ID of the asset as saved in the developer portal.
  string asset_id = 1;
  // The ID of the media as given by the media service.
  string media_id = 2;
}

// The results of searching for an app
message AppSearchResponse {
  // This page's apps; there may be more on other pages
  repeated info.AppInfo apps = 1;
  // How many total results exist for this search
  int32 total_results = 2;
  // What page number this is
  int32 page_number = 3;
  // The maximum number of items on each page
  int32 page_size = 4;
}

// endregion

message AppExistsRequest {
  // Does an app with this slug exist?
  google.protobuf.StringValue slug = 1;
}

message AppExistsResponse {
  bool exists = 1;
}

message AppAccountExistsRequest {
  // The name of the app account (without the leading u/)
  string account_name = 1;
  // By default, we'll give you suggestions to name your app if the given name is already taken.
  // Set this to true if you don't need those suggestions. (It'll make the request a little faster.)
  optional bool hide_suggestions = 2;
}

message AppAccountExistsResponse {
  bool exists = 1;
  repeated string suggestions = 2;
}

message CreateAppAccountRequest {
  string slug = 1;
  optional string account_name = 2;
  string captcha = 3;
}

message CreateAppAccountResponse {
  bool created = 1;
  optional string errors = 2;
}

// Used to recover partially created apps.
message RecoverAppAccountRequest {
  // What is the new app's name?
  string name = 1;
  // What is the slug/identifier of the app?
  string slug = 2;
  // What is the description of the app?
  string description = 3;
  // Is the app NSFW? NOTE: Once set, this cannot be reversed!
  bool is_nsfw = 4;
  // What categor(y|ies) does the app fit into?
  repeated categories.Categories categories = 5 [deprecated = true];
  // What is the ID of the owner of the app?
  string owner_id = 6;
  // What is the username of the app account to recover?
  string username = 7;
  // What is the temporary password of the app account to recover?
  string password = 8;
  // Used with categoriesV2
  repeated int32 category_ids = 9;
}

message DisableAppRequest {
  // App UUID
  string id = 1;
}

message EnableAppRequest {
  // App UUID
  string id = 1;
}
