// Copyright 2025 Google LLC
//
// 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";

package google.maps.areainsights.v1;

import "google/api/annotations.proto";
import "google/api/client.proto";
import "google/api/field_behavior.proto";
import "google/api/resource.proto";
import "google/type/latlng.proto";

option csharp_namespace = "Google.Maps.AreaInsights.V1";
option go_package = "cloud.google.com/go/maps/areainsights/apiv1/areainsightspb;areainsightspb";
option java_multiple_files = true;
option java_outer_classname = "AreaInsightsServiceProto";
option java_package = "com.google.maps.areainsights.v1";
option objc_class_prefix = "MAI";
option php_namespace = "Google\\Maps\\AreaInsights\\V1";
option (google.api.resource_definition) = {
  type: "places.googleapis.com/Place"
  pattern: "places/{place_id}"
};

// Service definition for the Places Aggregate RPC.
service AreaInsights {
  option (google.api.default_host) = "areainsights.googleapis.com";
  option (google.api.oauth_scopes) =
      "https://www.googleapis.com/auth/cloud-platform";

  // This method lets you retrieve insights about areas using a variety of
  // filter such as: area, place type, operating status, price level
  // and ratings. Currently "count" and "places" insights are supported. With
  // "count" insights you can answer questions such as "How many restaurant are
  // located in California that are operational, are inexpensive and have an
  // average rating of at least 4 stars" (see `insight` enum for more details).
  // With "places" insights, you can determine which places match the
  // requested filter. Clients can then use those place resource names to fetch
  // more details about each individual place using the Places API.
  rpc ComputeInsights(ComputeInsightsRequest)
      returns (ComputeInsightsResponse) {
    option (google.api.http) = {
      post: "/v1:computeInsights"
      body: "*"
    };
  }
}

// Request for the ComputeInsights RPC.
message ComputeInsightsRequest {
  // Required. Insights to compute. Currently only INSIGHT_COUNT and
  // INSIGHT_PLACES are supported.
  repeated Insight insights = 4 [(google.api.field_behavior) = REQUIRED];

  // Required. Insight filter.
  Filter filter = 5 [(google.api.field_behavior) = REQUIRED];
}

// Response for the ComputeInsights RPC.
message ComputeInsightsResponse {
  // Result for Insights.INSIGHT_COUNT.
  optional int64 count = 1;

  // Result for Insights.INSIGHT_PLACES.
  repeated PlaceInsight place_insights = 5;
}

// Holds information about a place
message PlaceInsight {
  // The unique identifier of the place. This resource name can be used to
  // retrieve details about the place using the [Places
  // API](https://developers.google.com/maps/documentation/places/web-service/reference/rest/v1/places/get).
  string place = 1 [
    (google.api.resource_reference) = { type: "places.googleapis.com/Place" }
  ];
}

// Filters for the ComputeInsights RPC.
message Filter {
  // Required. Restricts results to places which are located in the area
  // specified by location filters.
  LocationFilter location_filter = 1 [(google.api.field_behavior) = REQUIRED];

  // Required. Place type filters.
  TypeFilter type_filter = 2 [(google.api.field_behavior) = REQUIRED];

  // Optional. Restricts results to places whose operating status is included on
  // this list. If operating_status is not set, OPERATING_STATUS_OPERATIONAL is
  // used as default.
  repeated OperatingStatus operating_status = 3
      [(google.api.field_behavior) = OPTIONAL];

  // Optional. Restricts results to places whose price level is included on this
  // list. If `price_levels` is not set, all price levels are included in the
  // results.
  repeated PriceLevel price_levels = 4 [(google.api.field_behavior) = OPTIONAL];

  // Optional. Restricts results to places whose average user ratings are in the
  // range specified by rating_filter. If rating_filter is not set, all ratings
  // are included in the result.
  RatingFilter rating_filter = 5 [(google.api.field_behavior) = OPTIONAL];
}

// Location filters.
//
// Specifies the area of interest for the insight.
message LocationFilter {
  // A circle is defined by a center point and radius in meters.
  message Circle {
    // The center of the circle.
    oneof center {
      // The latitude and longitude of the center of the circle.
      google.type.LatLng lat_lng = 1;

      // **Format:**  Must be in the format `places/PLACE_ID`, where `PLACE_ID`
      // is the unique identifier of a place. For example:
      // `places/ChIJgUbEo8cfqokR5lP9_Wh_DaM`.
      string place = 2 [(google.api.resource_reference) = {
        type: "places.googleapis.com/Place"
      }];
    }

    // Optional. The radius of the circle in meters
    int32 radius = 3 [(google.api.field_behavior) = OPTIONAL];
  }

  // A region is a geographic boundary such as: cities, postal codes, counties,
  // states, etc.
  message Region {
    // Defines a geographic region. Only one type of region (e.g. place) can
    // specified at a time.
    oneof region {
      // The unique identifier of a specific geographic region.
      string place = 1 [(google.api.resource_reference) = {
        type: "places.googleapis.com/Place"
      }];
    }
  }

  // Custom Area.
  message CustomArea {
    // A polygon is represented by a series of connected coordinates in an
    // counterclockwise ordered sequence. The coordinates form a closed loop and
    // define a filled region. The first and last coordinates are equivalent,
    // and they must contain identical values. The format is a simplified
    // version of GeoJSON polygons (we only support one counterclockwise
    // exterior ring).
    message Polygon {
      // Optional. The coordinates that define the polygon.
      repeated google.type.LatLng coordinates = 1
          [(google.api.field_behavior) = OPTIONAL];
    }

    // Required. The custom area represented as a polygon
    Polygon polygon = 1 [(google.api.field_behavior) = REQUIRED];
  }

  // One of the following must be specified.
  oneof area {
    // Area as a circle.
    Circle circle = 1;

    // Area as region.
    Region region = 2;

    // Custom area specified by a polygon.
    CustomArea custom_area = 3;
  }
}

// Place type filters.
//
// Only Place types from
// [Table
// a](https://developers.google.com/maps/documentation/places/web-service/place-types#table-a)
// are supported.
//
// A place can only have a single primary type associated with it. For example,
// the primary type might be "mexican_restaurant" or "steak_house". Use
// included_primary_types and excluded_primary_types to filter the results on a
// place's primary type.
//
// A place can also have multiple type values associated with it. For example a
// restaurant might have the following types: "seafood_restaurant",
// "restaurant", "food", "point_of_interest", "establishment". Use
// included_types and excluded_types to filter the results on the list of types
// associated with a place.
//
// If a search is specified with multiple type restrictions, only places that
// satisfy all of the restrictions are returned. For example, if you specify
// {"included_types": ["restaurant"], "excluded_primary_types":
// ["steak_house"]}, the returned places provide "restaurant" related services
// but do not operate primarily as a "steak_house".
//
// If there are any conflicting types, i.e. a type appears in both
// included_types and excluded_types types or included_primary_types and
// excluded_primary_types, an INVALID_ARGUMENT error is returned.
//
// One of included_types or included_primary_types must be set.
message TypeFilter {
  // Optional. Included Place types.
  repeated string included_types = 1 [(google.api.field_behavior) = OPTIONAL];

  // Optional. Excluded Place types.
  repeated string excluded_types = 2 [(google.api.field_behavior) = OPTIONAL];

  // Optional. Included primary Place types.
  repeated string included_primary_types = 3
      [(google.api.field_behavior) = OPTIONAL];

  // Optional. Excluded primary Place types.
  repeated string excluded_primary_types = 4
      [(google.api.field_behavior) = OPTIONAL];
}

// Average user rating filters.
message RatingFilter {
  // Optional. Restricts results to places whose average user rating is greater
  // than or equal to min_rating. Values must be between 1.0 and 5.0.
  optional float min_rating = 5 [(google.api.field_behavior) = OPTIONAL];

  // Optional. Restricts results to places whose average user rating is strictly
  // less than or equal to max_rating. Values must be between 1.0 and 5.0.
  optional float max_rating = 6 [(google.api.field_behavior) = OPTIONAL];
}

// Supported insights.
enum Insight {
  // Not Specified.
  INSIGHT_UNSPECIFIED = 0;

  // Count insight.
  //
  // When this insight is specified ComputeInsights returns the number of
  // places that match the specified filter criteria.
  //
  // Example request:
  // ```
  // {
  //   "insights": ["INSIGHT_COUNT"],
  //   "filter": {
  //     "locationFilter": {
  //       "region": {
  //         "place": "places/ChIJPV4oX_65j4ARVW8IJ6IJUYs"
  //       }
  //     },
  //     "typeFilter": {
  //       "includedTypes": ["restaurant"]
  //     },
  //     "operatingStatus": ["OPERATING_STATUS_OPERATIONAL"],
  //     "priceLevels": [
  //       "PRICE_LEVEL_FREE",
  //       "PRICE_LEVEL_INEXPENSIVE"
  //     ],
  //     "ratingFilter": {
  //       "minRating": 4.0
  //     }
  //   }
  // }
  // ```
  //
  // Example response:
  // ```
  // {
  //   "count": 1234
  // }
  // ```
  INSIGHT_COUNT = 1;

  // Return Places
  //
  // When this insight is specified ComputeInsights returns places IDs
  // that match the specified filter criteria.
  //
  // Example request:
  // ```
  // {
  //   "insights": ["INSIGHT_PLACES"],
  //   "filter": {
  //     "locationFilter": {
  //       "region": {
  //         "place": "places/ChIJPV4oX_65j4ARVW8IJ6IJUYs"
  //       }
  //     },
  //     "typeFilter": {
  //       "includedTypes": ["restaurant"]
  //     },
  //     "operatingStatus": ["OPERATING_STATUS_OPERATIONAL"],
  //     "priceLevels": [
  //       "PRICE_LEVEL_FREE",
  //       "PRICE_LEVEL_INEXPENSIVE"
  //     ],
  //     "ratingFilter": {
  //       "minRating": 4.0
  //     }
  //   }
  // }
  // ```
  //
  // Example response:
  // ```
  // {
  //   "placeInsights": [
  //     {"place": "places/ABC"},
  //     {"place": "places/PQR"},
  //     {"place": "places/XYZ"}
  //   ]
  // }
  // ```
  INSIGHT_PLACES = 2;
}

// Operating status of the place.
enum OperatingStatus {
  // Not specified. This value should not be used.
  OPERATING_STATUS_UNSPECIFIED = 0;

  // The place is operational and its open during its defined hours.
  OPERATING_STATUS_OPERATIONAL = 1;

  // The Place is no longer in business.
  OPERATING_STATUS_PERMANENTLY_CLOSED = 3;

  // The place is temporarily closed and expected to reopen in the future.
  OPERATING_STATUS_TEMPORARILY_CLOSED = 4;
}

// Price level of the place.
enum PriceLevel {
  // Not specified. This value should not be used.
  PRICE_LEVEL_UNSPECIFIED = 0;

  // Place provides free services.
  PRICE_LEVEL_FREE = 1;

  // Place provides inexpensive services.
  PRICE_LEVEL_INEXPENSIVE = 2;

  // Place provides moderately priced services.
  PRICE_LEVEL_MODERATE = 3;

  // Place provides expensive services.
  PRICE_LEVEL_EXPENSIVE = 4;

  // Place provides very expensive services.
  PRICE_LEVEL_VERY_EXPENSIVE = 5;
}
