syntax = "proto3";
package dkod.v1;

import "dkod/v1/types.proto";

service AgentService {
  rpc Connect(ConnectRequest) returns (ConnectResponse);
  rpc Context(ContextRequest) returns (ContextResponse);
  rpc Submit(SubmitRequest) returns (SubmitResponse);

  // Trigger verification pipeline on a changeset (server-streaming)
  rpc Verify(VerifyRequest) returns (stream VerifyStepResult);

  // Merge an approved changeset into a Git commit
  rpc Merge(MergeRequest) returns (MergeResponse);

  // Watch for repo events from other agents (server-streaming)
  rpc Watch(WatchRequest) returns (stream WatchEvent);

  // File-level operations through the session workspace overlay
  rpc FileRead(FileReadRequest) returns (FileReadResponse);
  rpc FileWrite(FileWriteRequest) returns (FileWriteResponse);
  rpc FileList(FileListRequest) returns (FileListResponse);

  // Dry-run conflict detection before SUBMIT
  rpc PreSubmitCheck(PreSubmitCheckRequest) returns (PreSubmitCheckResponse);

  // Get current session workspace state
  rpc GetSessionStatus(SessionStatusRequest) returns (SessionStatusResponse);

  // Push merged changes to GitHub (branch or PR)
  rpc Push(PushRequest) returns (PushResponse);

  // Approve a submitted changeset (platform-level state transition)
  rpc Approve(ApproveRequest) returns (ApproveResponse);

  // Resolve conflicts on a changeset
  rpc Resolve(ResolveRequest) returns (ResolveResponse);

  // Close a session and destroy its workspace
  rpc Close(CloseRequest) returns (CloseResponse);

  // Get code review results for a changeset
  rpc Review(ReviewRequest) returns (ReviewResponse);
}

// ── CONNECT ──

message ConnectRequest {
  string agent_id = 1;
  string auth_token = 2;
  string codebase = 3;
  string intent = 4;
  WorkspaceConfig workspace_config = 5;
  string agent_name = 6;  // Human-readable agent name (auto-assigned if empty)
}

message ConnectResponse {
  string session_id = 1;
  string codebase_version = 2;
  CodebaseSummary summary = 3;
  string changeset_id = 4;   // auto-created changeset
  string workspace_id = 5;
  WorkspaceConcurrencyInfo concurrency = 6;
}

message WorkspaceConfig {
  WorkspaceMode mode = 1;
  optional string base_commit = 2;
  optional string resume_session_id = 3;
  bool watch_other_sessions = 4;
}

enum WorkspaceMode {
  EPHEMERAL = 0;
  PERSISTENT = 1;
}

message WorkspaceConcurrencyInfo {
  uint32 active_sessions = 1;
  repeated ActiveSessionSummary other_sessions = 2;
}

message ActiveSessionSummary {
  string agent_id = 1;
  string intent = 2;
  repeated string active_files = 3;
}

message CodebaseSummary {
  repeated string languages = 1;
  uint64 total_symbols = 2;
  uint64 total_files = 3;
}

// ── CONTEXT ──

message ContextRequest {
  string session_id = 1;
  string query = 2;
  ContextDepth depth = 3;
  bool include_tests = 4;
  bool include_dependencies = 5;
  uint32 max_tokens = 6;
}

enum ContextDepth {
  SIGNATURES = 0;
  FULL = 1;
  CALL_GRAPH = 2;
}

message ContextResponse {
  repeated SymbolResult symbols = 1;
  repeated CallEdgeRef call_graph = 2;
  repeated DependencyRef dependencies = 3;
  uint32 estimated_tokens = 4;
}

message SymbolResult {
  SymbolRef symbol = 1;
  optional string source = 2;
  repeated string caller_ids = 3;
  repeated string callee_ids = 4;
  repeated string test_symbol_ids = 5;
}

// ── SUBMIT ──

message SubmitRequest {
  string session_id = 1;
  string intent = 2;
  repeated Change changes = 3;
  string changeset_id = 4;   // target changeset
}

message Change {
  ChangeType type = 1;
  string symbol_name = 2;
  string file_path = 3;
  optional string old_symbol_id = 4;
  string new_source = 5;
  string rationale = 6;
}

enum ChangeType {
  MODIFY_FUNCTION = 0;
  ADD_FUNCTION = 1;
  DELETE_FUNCTION = 2;
  MODIFY_TYPE = 3;
  ADD_TYPE = 4;
  ADD_DEPENDENCY = 5;
}

message ReviewSummary {
  string tier = 1;
  optional int32 score = 2;
  int32 findings_count = 3;
  string top_severity = 4;
}

message SubmitResponse {
  SubmitStatus status = 1;
  string changeset_id = 2;
  optional string new_version = 3;
  repeated SubmitError errors = 4;
  optional SubmitConflictBlock conflict_block = 5;
  optional ReviewSummary review_summary = 6;
}

message SubmitSymbolVersion {
  string description = 1;
  string signature = 2;
  string body = 3;
  string change_type = 4;
}

message SubmitSymbolConflictDetail {
  string file_path = 1;
  string qualified_name = 2;
  string kind = 3;
  string conflicting_agent = 4;
  SubmitSymbolVersion their_change = 5;
  SubmitSymbolVersion your_change = 6;
  SubmitSymbolVersion base_version = 7;
}

message SubmitConflictBlock {
  repeated SubmitSymbolConflictDetail conflicting_symbols = 1;
  string message = 2;
}

enum SubmitStatus {
  ACCEPTED = 0;
  REJECTED = 1;
  CONFLICT = 2;
}

message SubmitError {
  string message = 1;
  optional string symbol_id = 2;
  optional string file_path = 3;
}

// --- VERIFY ---

message VerifyRequest {
  string session_id = 1;
  string changeset_id = 2;
}

message VerifyStepResult {
  int32 step_order = 1;
  string step_name = 2;
  string status = 3;
  string output = 4;
  bool required = 5;
  repeated Finding findings = 6;
  repeated Suggestion suggestions = 7;
}

message Finding {
  string severity = 1;
  string check_name = 2;
  string message = 3;
  optional string file_path = 4;
  optional uint32 line = 5;
  optional string symbol = 6;
}

message Suggestion {
  uint32 finding_index = 1;
  string description = 2;
  string file_path = 3;
  optional string replacement = 4;
}

// --- MERGE ---

message MergeRequest {
  string session_id = 1;
  string changeset_id = 2;
  string commit_message = 3;
  bool force = 4;
  string author_name = 5;   // Git commit author name (falls back to agent name if empty)
  string author_email = 6;  // Git commit author email (falls back to {agent}@dkod.dev if empty)
}

message MergeResponse {
  oneof result {
    MergeSuccess success = 1;
    MergeConflict conflict = 2;
    RecentOverwriteWarning overwrite_warning = 3;
  }
}

message MergeSuccess {
  string commit_hash = 1;
  string merged_version = 2;
  bool auto_rebased = 3;
  repeated string auto_rebased_files = 4;
}

message MergeConflict {
  string changeset_id = 1;
  repeated ConflictDetail conflicts = 2;
  string suggested_action = 3;
  repeated string available_actions = 4;
}

message ConflictDetail {
  string file_path = 1;
  repeated string symbols = 2;
  string your_agent = 3;
  string their_agent = 4;
  string conflict_type = 5;
  string description = 6;
}

// --- OVERWRITE WARNING ---

message RecentOverwriteWarning {
  string changeset_id = 1;
  repeated SymbolOverwrite overwrites = 2;
  repeated string available_actions = 3;
}

message SymbolOverwrite {
  string file_path = 1;
  string symbol_name = 2;
  string other_agent = 3;
  string other_changeset_id = 4;
  string merged_at = 5;
}

// --- WATCH ---

message WatchRequest {
  string session_id = 1;
  string repo_id = 2;
  string filter = 3;
}

message FileChange {
  string path = 1;
  string operation = 2;  // "add", "modify", "delete"
}

message SymbolChangeDetail {
  string symbol_name = 1;
  string file_path = 2;
  string change_type = 3;  // "added", "modified", "deleted"
  string kind = 4;         // "function", "struct", "impl", etc.
}

message WatchEvent {
  string event_type = 1;
  string changeset_id = 2;
  string agent_id = 3;
  repeated string affected_symbols = 4;
  string details = 5;
  string session_id = 6;
  repeated FileChange affected_files = 7;
  repeated SymbolChangeDetail symbol_changes = 8;
  string repo_id = 9;
  string event_id = 10;  // UUID for deduplication
}

// ── Code Review ──

message ReviewRequest {
  string session_id = 1;
  string changeset_id = 2;
}

message ReviewFindingProto {
  string id = 1;
  string file_path = 2;
  optional int32 line_start = 3;
  optional int32 line_end = 4;
  string severity = 5;
  string category = 6;
  string message = 7;
  optional string suggestion = 8;
  float confidence = 9;
  bool dismissed = 10;
}

message ReviewResultProto {
  string id = 1;
  string tier = 2;
  optional int32 score = 3;
  optional string summary = 4;
  repeated ReviewFindingProto findings = 5;
  string created_at = 6;
}

message ReviewResponse {
  repeated ReviewResultProto reviews = 1;
}

// --- FILE OPERATIONS ---

message FileReadRequest {
  string session_id = 1;
  string path = 2;
}

message FileReadResponse {
  bytes content = 1;
  string hash = 2;
  bool modified_in_session = 3;
}

message FileWriteRequest {
  string session_id = 1;
  string path = 2;
  bytes content = 3;
}

message FileWriteResponse {
  string new_hash = 1;
  repeated SymbolChange detected_changes = 2;
  repeated ConflictWarning conflict_warnings = 3;
}

message ConflictWarning {
  string file_path = 1;
  string symbol_name = 2;
  string conflicting_agent = 3;
  string conflicting_session_id = 4;
  string message = 5;
}

message FileListRequest {
  string session_id = 1;
  optional string prefix = 2;
  bool only_modified = 3;
}

message FileListResponse {
  repeated FileEntry files = 1;
}

message FileEntry {
  string path = 1;
  bool modified_in_session = 2;
  // If another session has modified this file, describes who and what.
  // Format: "fn create_task modified by agent-2" or "modified by agent-2"
  // Empty string if no other session has touched this file.
  string modified_by_other = 3;
}

// --- PRE-SUBMIT CHECK ---

message PreSubmitCheckRequest {
  string session_id = 1;
}

message PreSubmitCheckResponse {
  bool has_conflicts = 1;
  repeated SemanticConflict potential_conflicts = 2;
  uint32 files_modified = 3;
  uint32 symbols_changed = 4;
}

message SemanticConflict {
  string file_path = 1;
  string symbol_name = 2;
  string our_change = 3;
  string their_change = 4;
}

message SymbolChange {
  string symbol_name = 1;
  string change_type = 2;
}

// ── Session Status ──

message SessionStatusRequest {
  string session_id = 1;
}

message SessionStatusResponse {
  string session_id = 1;
  string base_commit = 2;
  repeated string files_modified = 3;
  repeated string symbols_modified = 4;
  uint64 overlay_size_bytes = 5;
  uint32 active_other_sessions = 6;
}

// --- PUSH ---

enum PushMode {
  PUSH_MODE_UNSPECIFIED = 0;
  PUSH_MODE_BRANCH = 1;
  PUSH_MODE_PR = 2;
}

message PushRequest {
  string session_id = 1;
  PushMode mode = 2;
  string branch_name = 3;
  string pr_title = 4;       // only used when mode = PUSH_MODE_PR
  string pr_body = 5;        // only used when mode = PUSH_MODE_PR
}

message PushResponse {
  string branch_name = 1;
  string pr_url = 2;         // empty when mode = PUSH_MODE_BRANCH
  string commit_hash = 3;
  repeated string changeset_ids = 4;  // changesets included in the push
}

// --- APPROVE ---

message ApproveRequest {
  string session_id = 1;
}

message ApproveResponse {
  bool success = 1;
  string changeset_id = 2;
  string new_state = 3;      // e.g. "approved"
  string message = 4;        // human-readable status message
}

// --- RESOLVE ---

enum ResolutionMode {
  RESOLUTION_MODE_UNSPECIFIED = 0;
  RESOLUTION_MODE_PROCEED     = 1;
  RESOLUTION_MODE_KEEP_YOURS  = 2;
  RESOLUTION_MODE_KEEP_THEIRS = 3;
  RESOLUTION_MODE_MANUAL      = 4;
}

message ResolveRequest {
  string session_id = 1;
  ResolutionMode resolution = 2;
  optional string conflict_id = 3;
  optional string manual_content = 4;
}

message ResolveResponse {
  bool success = 1;
  string changeset_id = 2;
  string new_state = 3;
  string message = 4;
  int32 conflicts_resolved = 5;
  int32 conflicts_remaining = 6;
}

// --- CLOSE ---

message CloseRequest {
  string session_id = 1;
}

message CloseResponse {
  bool success = 1;
  string message = 2;
  string session_id = 3;
}
