syntax = "proto3";

package gitmirror.v1;

import "google/protobuf/timestamp.proto";

option go_package = "tangled.org/core/gitmirror/proto/gen;gitmirrorv1";

service GitMirrorService {
  rpc Commits(CommitsRequest) returns (CommitsResponse);
  rpc Diff(DiffRequest) returns (stream FileDiff);
  rpc Interdiff(InterdiffRequest) returns (stream FileDiff);
  // GetBlob streams a blob's raw bytes by OID, in chunks.
  rpc GetBlob(GetBlobRequest) returns (stream BlobChunk);
  rpc CommitLog(CommitLogRequest) returns (stream CommitLogResponse);
}

message GetBlobRequest {
  // repo carries the DID as a string.
  string repo = 1;
  // oid is the blob object id (hex).
  string oid = 2;
}

message CommitLogRequest {
  // repo DID
  string repo = 1;
  // Ranges to include in the git log (revspec, "A..B", "A...B", etc.).
  // At least one range, or all_refs must be specified.
  repeated bytes ranges = 2;
  // If true, all refs are searched for commits.
  // Must not be true when ranges are given.
  bool all_refs = 3;
  // After is an optional parameter to specify the earliest commit to consider.
  google.protobuf.Timestamp after = 4;
  // Before is an optional parameter to specify the latest commit to consider.
  google.protobuf.Timestamp before = 5;
  // MaxCommits is an optional parameter to specify the maximum number of commits
  // to return. If max_commits is 0, all commits that match the criteria will be
  // returned.
  uint32 max_commits = 6;
  // Skip is an optional parameter to specify the number of commits to skip.
  // This can be used to implement a poor mans pagination.
  uint32 skip = 7;
}
message CommitLogResponse {
  repeated GitCommit commits = 1;
}

// One chunk of a blob's raw bytes; concatenate in arrival order.
message BlobChunk {
  bytes data = 1;
}

message CommitsRequest {
  // repo carries the DID as a string; parsing/validation is an impl detail.
  string repo = 1;
  CommitsOptions options = 2;
}

message CommitsOptions {
  // TODO: fields for the Go CommitsOptions (e.g. ref/branch, limit, cursor) —
  // unspecified for now.
}

message CommitsResponse {
  // maps the Go []string return.
  repeated string commits = 1;
}

message DiffRequest {
  // repo carries the DID as a string; parsing/validation is an impl detail.
  string repo = 1;
  // base/head are commit-ish ref strings.
  string base = 2;
  string head = 3;
}

message InterdiffRequest {
  // repo carries the DID as a string.
  string repo = 1;
  // Old patch range (from_base..from_head) and new patch range (to_base..to_head).
  string from_base = 2;
  string from_head = 3;
  string to_base = 4;
  string to_head = 5;
}

// Diff and Interdiff stream one FileDiff per changed file.

message FileContent {
  string path = 1;
  string oid = 2;
  uint64 size = 3;
  bool is_binary = 4;
  bool is_submodule = 5;
}

// One aligned line pair; a side is absent (None) when unset.
message LinePair {
  optional uint32 lhs = 1;
  optional uint32 rhs = 2;
}

message Hunk {
  // LineNumber (u32) sets containing novel content per side.
  repeated uint32 novel_lhs = 1;
  repeated uint32 novel_rhs = 2;
  repeated LinePair lines = 3;
}

// Number of bytes per side when the files differ (Rust Option<(usize, usize)>).
message ByteChanges {
  uint64 lhs = 1;
  uint64 rhs = 2;
}

message FileDiff {
  FileContent lhs_src = 1;
  FileContent rhs_src = 2;
  repeated Hunk hunks = 3;
  optional ByteChanges has_byte_changes = 4;
  bool has_syntactic_changes = 5;
  // TODO: lhs_positions / rhs_positions (MatchedPos) — AST, added later.
}

message GitCommit {
  string oid = 1;
  GitSignature author = 2;
  GitSignature committer = 3;
  bytes message = 4;
  repeated string parents = 5;
}

message GitSignature {
  bytes name = 1;
  bytes email = 2;
  google.protobuf.Timestamp date = 3;
}
