// Copyright 2026 MOIA GmbH

syntax = "proto3";

package moia.trip.state.v1;

import "google/protobuf/duration.proto";
import "google/protobuf/timestamp.proto";
import "moia/type/v1/latlon.proto";
import "moia/type/v1/latlon_with_heading.proto";
import "moia/type/v1/money.proto";
import "moia/type/v1/time_interval.proto";

option go_package = "./tripv1";
option java_multiple_files = false;
option java_outer_classname = "TripStateServiceV1";
option java_package = "io.moia.protos.trip.state.v1";

// The TripStateService provides end-to-end trip lifecycle management, including request, retrieval, ordering and cancellation.
service TripStateService {
  // Requests offers for a trip.
  //
  // This is a customer-scoped endpoint.
  // The ID of the customer on behalf of whom the operation is requested needs to be provided in the request header (`"Customer-Id: <your-customer-id>"`).
  //
  // Fails with `PERMISSION_DENIED` with the following reasons:
  // * `CUSTOMER_ID_MISSING` if the customer ID is not present in the request header.
  // * `CUSTOMER_UNAUTHENTICATED` if the provided customer ID does not exist.
  // * `SERVICE_CLASSES_NOT_ALLOWED` if the customer is not allowed to use any of the configured service classes. This error reason usually indicates that the customer is missing a required customer flag.
  //
  // Fails with `FAILED_PRECONDITION` with the following reasons:
  // * `FLEET_SATURATED` if the fleet is saturated at the requested time and no offer can be proposed.
  // * `NO_STOPS_NEAR_DESTINATION` if there are no active stops close to the selected destination.
  // * `NO_STOPS_NEAR_ORIGIN` if there are no active stops close to the selected origin.
  // * `NO_STOPS_NEAR_ORIGIN_AND_DESTINATION` if there are no active stops close to the selected origin and destination.
  // * `ONGOING_TRIP` if the customer has a scheduled or ongoing trip that is conflicting with the selected time.
  //
  // Fails with `INVALID_ARGUMENT` with the following reasons:
  // * `INVALID_LOAD` if there are no vehicles configured with the necessary capacity to fulfill the requested load.
  // * `OUT_OF_SERVICE_AREA` if the requested origin and/or destination for the trip are outside of all configured service areas. You may list available service areas by calling the `ServiceAreaService`.
  // * `OUT_OF_SERVICE_HOURS` if the requested times for the trip would at any point coincide with the service being out of the scheduled service hours. You may list the service hours by calling the `ServiceAreaService`.
  // * `TIME_TOO_FAR_FROM_NOW` if the requested times for the trip are too far into the past or too far into the future.
  // * `TRIP_TOO_SHORT` if the requested trip is too short for a ride.
  rpc RequestTripOffers(RequestTripOffersRequest) returns (RequestTripOffersResponse);
  // Orders a trip.
  //
  // This is a customer-scoped endpoint.
  // The ID of the customer on behalf of whom the operation is requested needs to be provided in the request header (`"Customer-Id: <your-customer-id>"`).
  //
  // This operation is idempotent.
  //
  // Only order a single offer from a given `RequestTripOffersResponse`.
  // If you later call `OrderTrip` with a different `offer_token` from that same service area and `RequestTripOffersResponse`, the call will succeed but will return the `trip_id` of the first created trip and will not modify that trip.
  //
  // Fails with `PERMISSION_DENIED` with the following reasons:
  // * `CUSTOMER_ID_MISSING` if the customer ID is not present in the request header.
  // * `CUSTOMER_UNAUTHORIZED` if the offer was not requested by the provided customer ID (or the customer does not exist).
  //
  // Fails with `INVALID_ARGUMENT` with the reason `INVALID_OFFER` if the requested offer can not be processed.
  //
  // Fails with `FAILED_PRECONDITION` with the following reasons:
  // * `OFFER_EXPIRED` if the trip is ordered too late after the offer was requested. Re-request offers to order a trip.
  // * `ONGOING_TRIP` if there is an ongoing trip for the customer on behalf of whom the offer was requested.
  // * `FLEET_SATURATED` if the fleet is saturated at the requested time and no order can be processed.
  rpc OrderTrip(OrderTripRequest) returns (OrderTripResponse);
  // Cancels a trip because the customer decided to step back from taking the trip.
  // To further specify the customer's intent for cancellation,
  // the customer may provide feedback using the FeedbackService.
  //
  // This is a customer-scoped endpoint.
  // The ID of the customer on behalf of whom the operation is requested needs to be provided in the request header (`"Customer-Id: <your-customer-id>"`).
  //
  // This operation is idempotent.
  //
  // Fails with `PERMISSION_DENIED` with the following reasons:
  // * `CUSTOMER_ID_MISSING` if the customer ID is not present in the request header.
  // * `CUSTOMER_UNAUTHENTICATED` if the provided customer ID does not exist.
  // * `CUSTOMER_UNAUTHORIZED` if the trip does not belong to the provided customer ID.
  //
  // Fails with `NOT_FOUND` with the reason `TRIP_NOT_FOUND` if there is no trip for the requested trip ID.
  //
  // Fails with `FAILED_PRECONDITION` with the reason `TRIP_NOT_CANCELLABLE` if the trip is not in a state where a cancellation is possible.
  rpc CancelTripAsCustomer(CancelTripAsCustomerRequest) returns (CancelTripAsCustomerResponse);
  // Cancels a trip because of a technical reason (e.g. payment failure).
  // This action is initiated by the integrator and not the customer.
  // In case of the customer canceling the trip, please use the RPC `CancelTripAsCustomer`.
  //
  // This operation is idempotent.
  //
  // Fails with `NOT_FOUND` with the reason `TRIP_NOT_FOUND` if there is no trip for the requested trip ID.
  //
  // Fails with `FAILED_PRECONDITION` with the reason `TRIP_NOT_CANCELLABLE` if the trip is not in a state where a cancellation is possible.
  rpc CancelTripAsIntegrator(CancelTripAsIntegratorRequest) returns (CancelTripAsIntegratorResponse);
  // Gets the details of a trip.
  //
  // Fails with `NOT_FOUND` with the reason `TRIP_NOT_FOUND` if there is no trip for the requested trip ID.
  rpc GetTrip(GetTripRequest) returns (GetTripResponse);
}

// Requests offers for a trip.
message RequestTripOffersRequest {
  // The location where the customer wants to be picked up.
  NamedLocation origin = 1;
  // The location where the customer wants to be dropped off.
  NamedLocation destination = 2;
  // The requested load for the vehicle.
  // Must not be empty.
  repeated Load load = 3;
  // The time when the customer wants to be picked up or dropped off.
  oneof time {
    // The earliest time when the customer wants to be picked up.
    google.protobuf.Timestamp departure_time = 4;
    // The latest time when the customer wants to arrive at the destination.
    google.protobuf.Timestamp arrival_time = 5;
  }
  // The approach speed of the riders.
  // Used to calculate the `Stop.Route.approach_duration` for pickup and dropoff stops.
  ApproachSpeed approach_speed = 6;
  // Whether the riders need support because of visual impairment.
  bool visual_impairment_support = 7;
}

// Returns a list of offers for a trip request.
// No vehicle is reserved yet.
// The longer a customer waits before ordering a trip, the lower the chances of finding a vehicle.
message RequestTripOffersResponse {
  // The list of offers in no specific order.
  // Must not be empty.
  // The number of offers is configured by the service configuration manager.
  repeated Offer offers = 1;
}

// Orders a trip.
message OrderTripRequest {
  // The token of the offer that the customer wants to order, which is part of the RequestTripOffersResponse.
  string offer_token = 1;
}

// Signals that the order was processed successfully.
// The trip may still be rejected after ordering.
message OrderTripResponse {
  // The ID of the ordered trip.
  // The trip ID is used to get the trip details and updates.
  string trip_id = 1;
}

// Cancels a trip on behalf of the customer.
// Moves the trip to the cancelled status.
message CancelTripAsCustomerRequest {
  // The ID of the trip to request the cancellation for.
  string trip_id = 1;
}

// Response to a successfully received CancelTripAsCustomerRequest.
message CancelTripAsCustomerResponse {}

// Cancels a trip due to technical reasons.
// Moves the trip to the cancelled status.
message CancelTripAsIntegratorRequest {
  // The ID of the trip to request the cancellation for.
  string trip_id = 1;
}

// Response to a successfully received CancelTripAsIntegratorRequest.
message CancelTripAsIntegratorResponse {}

// Gets the details of a trip.
message GetTripRequest {
  // The ID of the trip to get the details of.
  string trip_id = 1;
}

// Returns the details of a trip.
message GetTripResponse {
  // The details of the trip.
  Trip trip = 1;
}

// An offer for a trip.
message Offer {
  // A token that identifies the offer.
  string token = 1;
  // The service area the offer was created for and in which the trip would be executed if ordered.
  string service_area_id = 2;
  // The ID of the service class of the offer. The service class is used to distinguish different types of offers.
  // Service classes are configured by the service configuration manager.
  string service_class_id = 3;
  // The possible stops for the pickup.
  // If only one stop is provided, the stop is the pickup location.
  // If multiple stops are provided, the pickup location will be chosen after ordering the trip.
  // Must not be empty.
  repeated Stop pickup_stops = 4;
  // The possible stops for the dropoff.
  // If only one stop is provided, the stop is the dropoff location.
  // If multiple stops are provided, the dropoff location will be chosen later.
  // Must not be empty.
  repeated Stop dropoff_stops = 5;
  // The time interval in which the customer will be picked up.
  moia.type.v1.TimeInterval pickup_interval = 6;
  // The time interval in which the customer will be dropped off.
  moia.type.v1.TimeInterval dropoff_interval = 7;
  // The price of the offer.
  Price price = 8;
  // The tags include more information about the offer.
  // Tag strings are expected to be stable but may change.
  // May be empty.
  repeated string tags = 9;
}

// A stop is a location where a vehicle can pick up or dropoff a customer.
message Stop {
  // Location of the stop accessible by a human.
  moia.type.v1.LatLon location = 1;
  // Human-readable name of the stop in all supported languages.
  // The key is an IETF BCP-47 language tag: https://en.wikipedia.org/wiki/IETF_language_tag
  // For example 'en' or 'de'.
  // The value is the localized string.
  map<string, string> name = 2;
  // The route from the selected origin to this stop or from this stop to the selected destination.
  Route route = 3;

  // The route.
  message Route {
    // The path of the route.
    // Must not be empty.
    repeated moia.type.v1.LatLon path = 1;
    // Estimated duration for the customer to approach to or from this stop.
    google.protobuf.Duration approach_duration = 2;
    // Distance to or from this stop in meters.
    int32 approach_meters = 3;
  }
}

// The price.
// Must use the same currency for gross amount, net amount and all price parts.
message Price {
  // The gross amount of the trip.
  // Equal to the sum of all price parts.
  moia.type.v1.Money gross_amount = 1;
  // The net amount of the trip.
  moia.type.v1.Money net_amount = 2;
  // The tax rate (e.g. 0.19 for 19%).
  float tax_rate = 3;
  // The price parts.
  // The sum of all price parts equals the gross amount.
  // Must not be empty.
  repeated PricePart price_parts = 4;
}

// A price part.
message PricePart {
  // The name of the price part.
  // Price part names are configured by the service configuration manager.
  string name = 1;
  // The gross amount of the price part.
  // A negative amount may be used to describe a price discount.
  moia.type.v1.Money gross_amount = 2;
}

// The requested load for the vehicle.
message Load {
  // The type of the load.
  oneof load_type {
    // An adult.
    Adult adult = 1;
    // A child.
    Child child = 2;
  }
}

// An adult.
message Adult {
  // The potential wheelchair of the adult.
  Wheelchair wheelchair = 1;
}

// A child.
message Child {
  // The potential wheelchair of the child.
  Wheelchair wheelchair = 1;
  // The potentially required child seat for the child.
  ChildSeat child_seat = 2;
}

// Defines the child seat requirement.
// Seat specifications according to UN ECE Regulation 44/04.
// Not all child seats are available in all vehicles.
enum ChildSeat {
  // The default child seat in case the child seat is not set. Should not be used.
  // Will be treated as `CHILD_SEAT_NOT_NEEDED` by the server.
  CHILD_SEAT_UNSPECIFIED = 0;
  // No child seat is needed.
  CHILD_SEAT_NOT_NEEDED = 1;
  // A baby seat is needed.
  CHILD_SEAT_BACKWARDS = 2;
  // A child seat is needed.
  CHILD_SEAT_FORWARDS = 3;
  // A booster seat is needed.
  CHILD_SEAT_BOOSTER = 4;
}

// Defines wheelchair usage and space requirement.
// Not all vehicles have wheelchair spaces.
enum Wheelchair {
  // The default in case the wheelchair usage is not set. Should not be used.
  // Will be treated as `WHEELCHAIR_NOT_NEEDED` by the server.
  WHEELCHAIR_UNSPECIFIED = 0;
  // A wheelchair space is not needed.
  WHEELCHAIR_NOT_NEEDED = 1;
  // A wheelchair space is needed.
  WHEELCHAIR_NEEDED = 2;
}

// A Location combined with its localized address.
message NamedLocation {
  // The location.
  moia.type.v1.LatLon location = 1;
  // Street name and number.
  optional string primary_address = 2;
  // Secondary address line, usually containing zip code and city, might also contain the country.
  optional string secondary_address = 3;
  // The name of a point of interest (e.g., "Hamburg Airport").
  optional string primary_poi_name = 4;
  // Additional information for a point of interest (e.g., "Hamburg, Germany").
  optional string secondary_poi_name = 5;
}

// A trip.
message Trip {
  // The ID of the trip.
  // The value must not be shown to customers.
  string id = 1;
  // Human-readable resource name of the trip.
  // May be communicated to customers.
  // May be used by customers to contact customer support.
  string name = 2;
  // The ID of the customer who ordered the trip.
  string customer_id = 3;
  // The ID of the service area in which the trip is executed.
  string service_area_id = 4;
  // The ID of the service class of the trip.
  // Service classes are configured by the service configuration manager.
  string service_class_id = 5;
  // The location where the customer wants to be picked up.
  NamedLocation origin = 6;
  // The location where the customer wants to be dropped off.
  NamedLocation destination = 7;
  // The possible stops where the customer will be picked up.
  // If only one stop is provided, the stop is the pickup location.
  // If multiple stops are provided, the pickup location will be chosen later.
  // May be updated.
  // Must not be empty.
  repeated Stop pickup_stops = 8;
  // The possible stops where the customer will be dropped off.
  // If only one stop is provided, the stop is the dropoff location.
  // If multiple stops are provided, the dropoff location will be chosen later.
  // May be updated.
  // Must not be empty.
  repeated Stop dropoff_stops = 9;
  // The time interval in which the customer will be picked up.
  // May be updated.
  moia.type.v1.TimeInterval pickup_interval = 10;
  // The time interval in which the customer will be dropped off.
  // May be updated.
  moia.type.v1.TimeInterval dropoff_interval = 11;
  // The time when the trip was ordered.
  google.protobuf.Timestamp order_time = 12;
  // The pickup time of the trip.
  // Before pickup, this is the estimated pickup time.
  // After the pickup already happened, this is the actual time of the pickup.
  google.protobuf.Timestamp pickup_time = 13;
  // The dropoff time of the trip.
  // Before dropoff, this is the estimated dropoff time.
  // After the dropoff already happened, this is the actual time of the dropoff.
  google.protobuf.Timestamp dropoff_time = 14;
  // The price of the trip.
  Price price = 15;
  // The requested load of the vehicle.
  // Must not be empty.
  repeated Load load = 16;
  // The vehicle that is assigned to the trip.
  // The field is set once a vehicle is assigned to the trip.
  // May change if a different vehicle is assigned to the trip.
  optional Vehicle vehicle = 17;
  // The approach speed of the riders.
  // Used to calculate the `Stop.Route.approach_duration` for pickup and dropoff stops.
  ApproachSpeed approach_speed = 18;
  // Whether the riders need support because of visual impairment.
  bool visual_impairment_support = 19;
  // Fields 20-29 are reserved for future additional Trip fields.

  // The state of the trip.
  oneof state {
    // The trip is ordered.
    StateOrdered ordered = 30;
    // The trip is in fulfillment.
    StateInFulfillment in_fulfillment = 31;
    // The trip was fulfilled successfully.
    StateFulfilled fulfilled = 32;
    // The trip was not fulfilled successfully.
    StateNotFulfilled not_fulfilled = 33;
  }

  // The ordered state.
  message StateOrdered {}

  // The in fulfillment state.
  message StateInFulfillment {
    // The current phase of the trip in fulfillment.
    Phase phase = 1;

    // The possible phases of a trip in fulfillment.
    // The trip is not guaranteed to transition through all phases.
    //
    // The trips will transition through the phases in this order:
    // `PHASE_ACCEPTED` -> `PHASE_PICKED_UP`.
    //
    // The trips served by vehicles which require authentication via the BoardingAuthenticationService will transition through the phases in this order:
    // `PHASE_ACCEPTED` -> `PHASE_READY_FOR_BOARDING_AUTHENTICATION` -> `PHASE_BOARDING_AUTHENTICATION_COMPLETED` -> `PHASE_PICKED_UP`.
    enum Phase {
      // The default in case the detail is not set. Should not be used.
      PHASE_UNSPECIFIED = 0;
      // The trip was accepted by the system.
      PHASE_ACCEPTED = 1;
      // The vehicle has reached the pickup stop location and is ready for boarding authentication.
      // This phase is only relevant for trips served by vehicles which require authentication via the BoardingAuthenticationService.
      PHASE_READY_FOR_BOARDING_AUTHENTICATION = 2;
      // Authentication for boarding the vehicle has been completed.
      // This phase is only relevant for trips served by vehicles which require authentication via the BoardingAuthenticationService.
      PHASE_BOARDING_AUTHENTICATION_COMPLETED = 3;
      // The customer was picked up.
      PHASE_PICKED_UP = 4;
    }
  }

  // The state of a trip that was fulfilled successfully.
  // A trip has been fulfilled once the rider was dropped off by the vehicle at the dropoff stop location.
  message StateFulfilled {}

  // The state of a trip that was not fulfilled successfully.
  message StateNotFulfilled {
    // The reason why the trip was not fulfilled successfully.
    Reason reason = 1;

    // Defines the reason a trip can end with when not fulfilled successfully.
    enum Reason {
      // The default in case the status is not set. Should not be used.
      REASON_UNSPECIFIED = 0;
      // The trip was rejected by the system before being in fulfillment.
      REASON_REJECTED = 1;
      // The trip was terminated because of a problem with the service.
      // If the rider still desires to travel to the trip's destination, another trip has to be ordered.
      REASON_TERMINATED = 2;
      // The customer did not show up for pickup.
      REASON_NO_SHOW = 3;
      // The trip was cancelled by the customer.
      // This can also happen before the trip is in fulfillment.
      REASON_CANCELLED_AS_CUSTOMER = 4;
      // The trip was cancelled by the integrator.
      // This can also happen before the trip is in fulfillment.
      REASON_CANCELLED_AS_INTEGRATOR = 5;
    }
  }
}

// A vehicle.
message Vehicle {
  // The name of the vehicle.
  string name = 1;
  // The last known oriented geographic location of the vehicle.
  // Not available after the trip has entered a final state.
  optional moia.type.v1.LatLonWithHeading location = 2;
}

// Defines the approach speed of riders towards a destination.
enum ApproachSpeed {
  // The default in case the approach speed is not set. Should not be used.
  // Will be treated as `APPROACH_SPEED_MEDIUM` by the server.
  APPROACH_SPEED_UNSPECIFIED = 0;
  // The riders are approaching more slowly.
  // More time will be allocated for a given approach distance.
  APPROACH_SPEED_SLOW = 1;
  // The riders are approaching at a medium speed.
  APPROACH_SPEED_MEDIUM = 2;
}
