syntax = "proto3";

package nominal.iceberg.v1;

import "buf/validate/validate.proto";
import "google/api/annotations.proto";
import "google/protobuf/timestamp.proto";
import "nominal/protos/gen/v1/error.proto";
import "nominal/protos/iceberg/v1/table.proto";

option java_multiple_files = true;
option java_package = "io.nominal.iceberg.v1";

// A single maintenance action against one Iceberg table. A maintenance run
// executes an ordered list of these in one job; actions may target different
// tables, and the same action type may appear more than once with different
// parameters.
message MaintenanceAction {
  TableTarget target = 1 [(buf.validate.field).required = true];

  oneof action {
    option (buf.validate.oneof).required = true;

    RewriteDataFiles rewrite_data_files = 2;
    RewriteManifests rewrite_manifests = 3;
    ExpireSnapshots expire_snapshots = 4;
    RemoveOrphanFiles remove_orphan_files = 5;
  }
}

// Compact data files (with sort) via Iceberg's RewriteDataFiles action.
message RewriteDataFiles {
  // Override the configured default target file size for this action.
  optional uint64 target_file_size_bytes = 1 [(buf.validate.field).uint64.gt = 0];
}

// Coalesce metadata manifest files via Iceberg's RewriteManifests action.
message RewriteManifests {}

// Expire old snapshots (and their no-longer-reachable files) via Iceberg's
// ExpireSnapshots action.
message ExpireSnapshots {}

// Delete files in the table location unreferenced by any table metadata via
// Iceberg's DeleteOrphanFiles action.
message RemoveOrphanFiles {}

enum MaintenanceRunStatus {
  MAINTENANCE_RUN_STATUS_UNSPECIFIED = 0;
  MAINTENANCE_RUN_STATUS_PENDING = 1;
  MAINTENANCE_RUN_STATUS_RUNNING = 2;
  MAINTENANCE_RUN_STATUS_SUCCEEDED = 3;
  MAINTENANCE_RUN_STATUS_FAILED = 4;
}

// State of a maintenance run, returned by GetMaintenanceRun.
message GetMaintenanceRunResponse {
  // Opaque run identifier returned by RunMaintenance (the run's workflow ID).
  string run_id = 1;
  // Ordered actions the run executed, each with its target. Empty when the
  // actions could not be recovered from the workflow (e.g. history truncated).
  repeated MaintenanceAction actions = 2;
  MaintenanceRunStatus status = 3;
  // Set once the workflow has started executing.
  optional google.protobuf.Timestamp started_at = 4;
  // Set on terminal states (succeeded, failed).
  optional google.protobuf.Timestamp finished_at = 5;
}

message RunMaintenanceRequest {
  // Actions to execute in order within a single maintenance job. A repeated
  // message (not a set) so the same action type may appear multiple times with
  // distinct parameters; actions may target different tables.
  repeated MaintenanceAction actions = 1 [(buf.validate.field).repeated.min_items = 1];
  // Optional override of the maintenance job container image (full registry ref,
  // e.g. 123.dkr.ecr.us-east-1.amazonaws.com/iceberg-compaction:my-tag). Lets
  // operators test a new job build without redeploying combined-service.
  optional string image_override = 2 [(buf.validate.field).cel = {
    id: "text.non_whitespace"
    message: "string must contain at least one non-whitespace character"
    expression: "this.matches('[^\\\\s]')"
  }];
}

message RunMaintenanceResponse {
  // Identifier for the started run; pass to GetMaintenanceRun to look up status.
  string run_id = 1;
}

message GetMaintenanceRunRequest {
  string run_id = 1 [(buf.validate.field).string.min_len = 1];
}

enum TableMaintenanceErrors {
  TABLE_MAINTENANCE_ERRORS_UNSPECIFIED = 0;
  TABLE_MAINTENANCE_ERRORS_TABLE_NOT_FOUND = 1 [(nominal.gen.v1.error) = {
    code: "NOT_FOUND"
    message: "Table not found in Iceberg catalog"
  }];
}

// Service for programmatically starting and monitoring Iceberg table
// maintenance jobs. A run executes an ordered list of actions (possibly across
// multiple tables) in a single job and is identified by the returned run_id.
service IcebergTableMaintenanceService {
  // Run an ordered list of maintenance actions in a single job. Returns the
  // run_id to track it with GetMaintenanceRun.
  rpc RunMaintenance(RunMaintenanceRequest) returns (RunMaintenanceResponse) {
    option (google.api.http) = {
      post: "/iceberg/v1/maintenance/run"
      body: "*"
    };
  }

  // Look up the state of a maintenance run by its run_id.
  rpc GetMaintenanceRun(GetMaintenanceRunRequest) returns (GetMaintenanceRunResponse) {
    option (google.api.http) = {
      post: "/iceberg/v1/maintenance/runs/get"
      body: "*"
    };
  }
}
