// NoteFlow gRPC Service Definition
// Provides real-time ASR streaming and meeting management

syntax = "proto3";

package noteflow;

// =============================================================================
// Core Service
// =============================================================================

service NoteFlowService {
  // Bidirectional streaming: client sends audio chunks, server returns transcripts
  rpc StreamTranscription(stream AudioChunk) returns (stream TranscriptUpdate);

  // Meeting lifecycle management
  rpc CreateMeeting(CreateMeetingRequest) returns (Meeting);
  rpc StopMeeting(StopMeetingRequest) returns (Meeting);
  rpc ListMeetings(ListMeetingsRequest) returns (ListMeetingsResponse);
  rpc GetMeeting(GetMeetingRequest) returns (Meeting);
  rpc DeleteMeeting(DeleteMeetingRequest) returns (DeleteMeetingResponse);

  // Summary generation
  rpc GenerateSummary(GenerateSummaryRequest) returns (Summary);

  // Annotation management
  rpc AddAnnotation(AddAnnotationRequest) returns (Annotation);
  rpc GetAnnotation(GetAnnotationRequest) returns (Annotation);
  rpc ListAnnotations(ListAnnotationsRequest) returns (ListAnnotationsResponse);
  rpc UpdateAnnotation(UpdateAnnotationRequest) returns (Annotation);
  rpc DeleteAnnotation(DeleteAnnotationRequest) returns (DeleteAnnotationResponse);

  // Export functionality
  rpc ExportTranscript(ExportTranscriptRequest) returns (ExportTranscriptResponse);

  // Speaker diarization
  rpc RefineSpeakerDiarization(RefineSpeakerDiarizationRequest) returns (RefineSpeakerDiarizationResponse);
  rpc RenameSpeaker(RenameSpeakerRequest) returns (RenameSpeakerResponse);
  rpc GetDiarizationJobStatus(GetDiarizationJobStatusRequest) returns (DiarizationJobStatus);
  rpc CancelDiarizationJob(CancelDiarizationJobRequest) returns (CancelDiarizationJobResponse);
  rpc GetActiveDiarizationJobs(GetActiveDiarizationJobsRequest) returns (GetActiveDiarizationJobsResponse);

  // Server health and capabilities
  rpc GetServerInfo(ServerInfoRequest) returns (ServerInfo);

  // Named entity extraction (Sprint 4) + mutations (Sprint 8)
  rpc ExtractEntities(ExtractEntitiesRequest) returns (ExtractEntitiesResponse);
  rpc UpdateEntity(UpdateEntityRequest) returns (UpdateEntityResponse);
  rpc DeleteEntity(DeleteEntityRequest) returns (DeleteEntityResponse);

  // Calendar integration (Sprint 5)
  rpc ListCalendarEvents(ListCalendarEventsRequest) returns (ListCalendarEventsResponse);
  rpc GetCalendarProviders(GetCalendarProvidersRequest) returns (GetCalendarProvidersResponse);

  // OAuth integration (generic for calendar, email, PKM, etc.)
  rpc InitiateOAuth(InitiateOAuthRequest) returns (InitiateOAuthResponse);
  rpc CompleteOAuth(CompleteOAuthRequest) returns (CompleteOAuthResponse);
  rpc GetOAuthConnectionStatus(GetOAuthConnectionStatusRequest) returns (GetOAuthConnectionStatusResponse);
  rpc DisconnectOAuth(DisconnectOAuthRequest) returns (DisconnectOAuthResponse);

  // Webhook management (Sprint 6)
  rpc RegisterWebhook(RegisterWebhookRequest) returns (WebhookConfigProto);
  rpc ListWebhooks(ListWebhooksRequest) returns (ListWebhooksResponse);
  rpc UpdateWebhook(UpdateWebhookRequest) returns (WebhookConfigProto);
  rpc DeleteWebhook(DeleteWebhookRequest) returns (DeleteWebhookResponse);
  rpc GetWebhookDeliveries(GetWebhookDeliveriesRequest) returns (GetWebhookDeliveriesResponse);

  // Cloud consent management (Sprint 7)
  rpc GrantCloudConsent(GrantCloudConsentRequest) returns (GrantCloudConsentResponse);
  rpc RevokeCloudConsent(RevokeCloudConsentRequest) returns (RevokeCloudConsentResponse);
  rpc GetCloudConsentStatus(GetCloudConsentStatusRequest) returns (GetCloudConsentStatusResponse);

  // User preferences sync (Sprint 14)
  rpc GetPreferences(GetPreferencesRequest) returns (GetPreferencesResponse);
  rpc SetPreferences(SetPreferencesRequest) returns (SetPreferencesResponse);

  // Integration sync orchestration (Sprint 9)
  rpc StartIntegrationSync(StartIntegrationSyncRequest) returns (StartIntegrationSyncResponse);
  rpc GetSyncStatus(GetSyncStatusRequest) returns (GetSyncStatusResponse);
  rpc ListSyncHistory(ListSyncHistoryRequest) returns (ListSyncHistoryResponse);

  // Integration cache validation (Sprint 18.1)
  rpc GetUserIntegrations(GetUserIntegrationsRequest) returns (GetUserIntegrationsResponse);

  // Observability (Sprint 9)
  rpc GetRecentLogs(GetRecentLogsRequest) returns (GetRecentLogsResponse);
  rpc GetPerformanceMetrics(GetPerformanceMetricsRequest) returns (GetPerformanceMetricsResponse);

  // OIDC Provider Management (Sprint 17)
  rpc RegisterOidcProvider(RegisterOidcProviderRequest) returns (OidcProviderProto);
  rpc ListOidcProviders(ListOidcProvidersRequest) returns (ListOidcProvidersResponse);
  rpc GetOidcProvider(GetOidcProviderRequest) returns (OidcProviderProto);
  rpc UpdateOidcProvider(UpdateOidcProviderRequest) returns (OidcProviderProto);
  rpc DeleteOidcProvider(DeleteOidcProviderRequest) returns (DeleteOidcProviderResponse);
  rpc RefreshOidcDiscovery(RefreshOidcDiscoveryRequest) returns (RefreshOidcDiscoveryResponse);
  rpc ListOidcPresets(ListOidcPresetsRequest) returns (ListOidcPresetsResponse);

  // Project management (Sprint 18)
  rpc CreateProject(CreateProjectRequest) returns (ProjectProto);
  rpc GetProject(GetProjectRequest) returns (ProjectProto);
  rpc GetProjectBySlug(GetProjectBySlugRequest) returns (ProjectProto);
  rpc ListProjects(ListProjectsRequest) returns (ListProjectsResponse);
  rpc UpdateProject(UpdateProjectRequest) returns (ProjectProto);
  rpc ArchiveProject(ArchiveProjectRequest) returns (ProjectProto);
  rpc RestoreProject(RestoreProjectRequest) returns (ProjectProto);
  rpc DeleteProject(DeleteProjectRequest) returns (DeleteProjectResponse);

  // Active project (Sprint 18)
  rpc SetActiveProject(SetActiveProjectRequest) returns (SetActiveProjectResponse);
  rpc GetActiveProject(GetActiveProjectRequest) returns (GetActiveProjectResponse);

  // Project membership management (Sprint 18)
  rpc AddProjectMember(AddProjectMemberRequest) returns (ProjectMembershipProto);
  rpc UpdateProjectMemberRole(UpdateProjectMemberRoleRequest) returns (ProjectMembershipProto);
  rpc RemoveProjectMember(RemoveProjectMemberRequest) returns (RemoveProjectMemberResponse);
  rpc ListProjectMembers(ListProjectMembersRequest) returns (ListProjectMembersResponse);
}

// =============================================================================
// Audio Streaming Messages
// =============================================================================

message AudioChunk {
  // Meeting ID this audio belongs to
  string meeting_id = 1;

  // Raw audio data (float32, mono, 16kHz expected)
  bytes audio_data = 2;

  // Timestamp when audio was captured (monotonic, seconds)
  double timestamp = 3;

  // Sample rate in Hz (default 16000)
  int32 sample_rate = 4;

  // Number of channels (default 1 for mono)
  int32 channels = 5;

  // Sequence number for acknowledgment tracking (monotonically increasing per stream)
  int64 chunk_sequence = 6;
}

// Congestion information for backpressure signaling (Phase 3)
message CongestionInfo {
  // Time from chunk receipt to transcription processing (milliseconds)
  int32 processing_delay_ms = 1;

  // Number of chunks waiting to be processed
  int32 queue_depth = 2;

  // Signal that client should reduce sending rate
  bool throttle_recommended = 3;
}

message TranscriptUpdate {
  // Meeting ID this transcript belongs to
  string meeting_id = 1;

  // Type of update
  UpdateType update_type = 2;

  // For partial updates - tentative transcript text
  string partial_text = 3;

  // For final segments - confirmed transcript
  FinalSegment segment = 4;

  // Server-side processing timestamp
  double server_timestamp = 5;

  // Acknowledgment: highest contiguous chunk sequence received (optional)
  optional int64 ack_sequence = 6;

  // Congestion info for backpressure signaling (optional)
  optional CongestionInfo congestion = 10;
}

enum UpdateType {
  UPDATE_TYPE_UNSPECIFIED = 0;
  UPDATE_TYPE_PARTIAL = 1;      // Tentative, may change
  UPDATE_TYPE_FINAL = 2;        // Confirmed segment
  UPDATE_TYPE_VAD_START = 3;    // Voice activity started
  UPDATE_TYPE_VAD_END = 4;      // Voice activity ended
}

message FinalSegment {
  // Segment ID (sequential within meeting)
  int32 segment_id = 1;

  // Transcript text
  string text = 2;

  // Start time relative to meeting start (seconds)
  double start_time = 3;

  // End time relative to meeting start (seconds)
  double end_time = 4;

  // Word-level timestamps
  repeated WordTiming words = 5;

  // Detected language
  string language = 6;

  // Language detection confidence (0.0-1.0)
  float language_confidence = 7;

  // Average log probability (quality indicator)
  float avg_logprob = 8;

  // Probability that segment contains no speech
  float no_speech_prob = 9;

  // Speaker identification (from diarization)
  string speaker_id = 10;

  // Speaker assignment confidence (0.0-1.0)
  float speaker_confidence = 11;
}

message WordTiming {
  string word = 1;
  double start_time = 2;
  double end_time = 3;
  float probability = 4;
}

// =============================================================================
// Meeting Management Messages
// =============================================================================

message Meeting {
  // Unique meeting identifier
  string id = 1;

  // User-provided title
  string title = 2;

  // Meeting state
  MeetingState state = 3;

  // Creation timestamp (Unix epoch seconds)
  double created_at = 4;

  // Start timestamp (when recording began)
  double started_at = 5;

  // End timestamp (when recording stopped)
  double ended_at = 6;

  // Duration in seconds
  double duration_seconds = 7;

  // Full transcript segments
  repeated FinalSegment segments = 8;

  // Generated summary (if available)
  Summary summary = 9;

  // Metadata
  map<string, string> metadata = 10;

  // Optional project scope
  optional string project_id = 11;
}

enum MeetingState {
  MEETING_STATE_UNSPECIFIED = 0;
  MEETING_STATE_CREATED = 1;     // Created but not started
  MEETING_STATE_RECORDING = 2;   // Actively recording
  MEETING_STATE_STOPPED = 3;     // Recording stopped, processing may continue
  MEETING_STATE_COMPLETED = 4;   // All processing complete
  MEETING_STATE_ERROR = 5;       // Error occurred
}

message CreateMeetingRequest {
  // Optional title (generated if not provided)
  string title = 1;

  // Optional metadata
  map<string, string> metadata = 2;

  // Optional project scope (defaults to active project)
  optional string project_id = 3;
}

message StopMeetingRequest {
  string meeting_id = 1;
}

message ListMeetingsRequest {
  // Optional filter by state
  repeated MeetingState states = 1;

  // Pagination
  int32 limit = 2;
  int32 offset = 3;

  // Sort order
  SortOrder sort_order = 4;

  // Optional project filter (defaults to active project if omitted)
  optional string project_id = 5;
}

enum SortOrder {
  SORT_ORDER_UNSPECIFIED = 0;
  SORT_ORDER_CREATED_DESC = 1;   // Newest first (default)
  SORT_ORDER_CREATED_ASC = 2;    // Oldest first
}

message ListMeetingsResponse {
  repeated Meeting meetings = 1;
  int32 total_count = 2;
}

message GetMeetingRequest {
  string meeting_id = 1;

  // Whether to include full transcript segments
  bool include_segments = 2;

  // Whether to include summary
  bool include_summary = 3;
}

message DeleteMeetingRequest {
  string meeting_id = 1;
}

message DeleteMeetingResponse {
  bool success = 1;
}

// =============================================================================
// Summary Messages
// =============================================================================

message Summary {
  // Meeting this summary belongs to
  string meeting_id = 1;

  // Executive summary (2-3 sentences)
  string executive_summary = 2;

  // Key points / highlights
  repeated KeyPoint key_points = 3;

  // Action items extracted
  repeated ActionItem action_items = 4;

  // Generated timestamp
  double generated_at = 5;

  // Model/version used for generation
  string model_version = 6;
}

message KeyPoint {
  // The key point text
  string text = 1;

  // Segment IDs that support this point (evidence linking)
  repeated int32 segment_ids = 2;

  // Timestamp range this point covers
  double start_time = 3;
  double end_time = 4;
}

message ActionItem {
  // Action item text
  string text = 1;

  // Assigned to (if mentioned)
  string assignee = 2;

  // Due date (if mentioned, Unix epoch)
  double due_date = 3;

  // Priority level
  Priority priority = 4;

  // Segment IDs that mention this action
  repeated int32 segment_ids = 5;
}

enum Priority {
  PRIORITY_UNSPECIFIED = 0;
  PRIORITY_LOW = 1;
  PRIORITY_MEDIUM = 2;
  PRIORITY_HIGH = 3;
}

// Summarization style options (Sprint 1)
message SummarizationOptions {
  // Tone: professional, casual, technical, friendly
  string tone = 1;

  // Format: bullet_points, narrative, structured, concise
  string format = 2;

  // Verbosity: minimal, balanced, detailed, comprehensive
  string verbosity = 3;
}

message GenerateSummaryRequest {
  string meeting_id = 1;

  // Force regeneration even if summary exists
  bool force_regenerate = 2;

  // Advanced summarization options (Sprint 1)
  SummarizationOptions options = 3;
}

// =============================================================================
// Server Info Messages
// =============================================================================

message ServerInfoRequest {}

message ServerInfo {
  // Server version
  string version = 1;

  // ASR model loaded
  string asr_model = 2;

  // Whether ASR is ready
  bool asr_ready = 3;

  // Supported sample rates
  repeated int32 supported_sample_rates = 4;

  // Maximum audio chunk size in bytes
  int32 max_chunk_size = 5;

  // Server uptime in seconds
  double uptime_seconds = 6;

  // Number of active meetings
  int32 active_meetings = 7;

  // Whether diarization is enabled
  bool diarization_enabled = 8;

  // Whether diarization models are ready
  bool diarization_ready = 9;

  // Server state version for cache invalidation (Sprint GAP-002)
  // Increment when breaking state changes require client cache invalidation
  int64 state_version = 10;
}

// =============================================================================
// Annotation Messages
// =============================================================================

enum AnnotationType {
  ANNOTATION_TYPE_UNSPECIFIED = 0;
  ANNOTATION_TYPE_ACTION_ITEM = 1;
  ANNOTATION_TYPE_DECISION = 2;
  ANNOTATION_TYPE_NOTE = 3;
  ANNOTATION_TYPE_RISK = 4;
}

message Annotation {
  // Unique annotation identifier
  string id = 1;

  // Meeting this annotation belongs to
  string meeting_id = 2;

  // Type of annotation
  AnnotationType annotation_type = 3;

  // Annotation text
  string text = 4;

  // Start time relative to meeting start (seconds)
  double start_time = 5;

  // End time relative to meeting start (seconds)
  double end_time = 6;

  // Linked segment IDs (evidence linking)
  repeated int32 segment_ids = 7;

  // Creation timestamp (Unix epoch seconds)
  double created_at = 8;
}

message AddAnnotationRequest {
  // Meeting ID to add annotation to
  string meeting_id = 1;

  // Type of annotation
  AnnotationType annotation_type = 2;

  // Annotation text
  string text = 3;

  // Start time relative to meeting start (seconds)
  double start_time = 4;

  // End time relative to meeting start (seconds)
  double end_time = 5;

  // Optional linked segment IDs
  repeated int32 segment_ids = 6;
}

message GetAnnotationRequest {
  string annotation_id = 1;
}

message ListAnnotationsRequest {
  // Meeting ID to list annotations for
  string meeting_id = 1;

  // Optional time range filter
  double start_time = 2;
  double end_time = 3;
}

message ListAnnotationsResponse {
  repeated Annotation annotations = 1;
}

message UpdateAnnotationRequest {
  // Annotation ID to update
  string annotation_id = 1;

  // Updated type (optional, keeps existing if not set)
  AnnotationType annotation_type = 2;

  // Updated text (optional, keeps existing if empty)
  string text = 3;

  // Updated start time (optional, keeps existing if 0)
  double start_time = 4;

  // Updated end time (optional, keeps existing if 0)
  double end_time = 5;

  // Updated segment IDs (replaces existing)
  repeated int32 segment_ids = 6;
}

message DeleteAnnotationRequest {
  string annotation_id = 1;
}

message DeleteAnnotationResponse {
  bool success = 1;
}

// =============================================================================
// Export Messages
// =============================================================================

enum ExportFormat {
  EXPORT_FORMAT_UNSPECIFIED = 0;
  EXPORT_FORMAT_MARKDOWN = 1;
  EXPORT_FORMAT_HTML = 2;
  EXPORT_FORMAT_PDF = 3;  // PDF export (Sprint 3)
}

enum JobStatus {
  JOB_STATUS_UNSPECIFIED = 0;
  JOB_STATUS_QUEUED = 1;
  JOB_STATUS_RUNNING = 2;
  JOB_STATUS_COMPLETED = 3;
  JOB_STATUS_FAILED = 4;
  JOB_STATUS_CANCELLED = 5;
}

message ExportTranscriptRequest {
  // Meeting ID to export
  string meeting_id = 1;

  // Export format
  ExportFormat format = 2;
}

message ExportTranscriptResponse {
  // Exported content
  string content = 1;

  // Format name
  string format_name = 2;

  // Suggested file extension
  string file_extension = 3;
}

// =============================================================================
// Speaker Diarization Messages
// =============================================================================

message RefineSpeakerDiarizationRequest {
  // Meeting ID to run diarization on
  string meeting_id = 1;

  // Optional known number of speakers (auto-detect if not set or 0)
  int32 num_speakers = 2;
}

message RefineSpeakerDiarizationResponse {
  // Number of segments updated with speaker labels
  int32 segments_updated = 1;

  // Distinct speaker IDs found
  repeated string speaker_ids = 2;

  // Error message if diarization failed
  string error_message = 3;

  // Background job identifier (empty if request failed)
  string job_id = 4;

  // Current job status
  JobStatus status = 5;
}

message RenameSpeakerRequest {
  // Meeting ID
  string meeting_id = 1;

  // Original speaker ID (e.g., "SPEAKER_00")
  string old_speaker_id = 2;

  // New speaker name (e.g., "Alice")
  string new_speaker_name = 3;
}

message RenameSpeakerResponse {
  // Number of segments updated
  int32 segments_updated = 1;

  // Success flag
  bool success = 2;
}

message GetDiarizationJobStatusRequest {
  // Job ID returned by RefineSpeakerDiarization
  string job_id = 1;
}

message DiarizationJobStatus {
  // Job ID
  string job_id = 1;

  // Current status
  JobStatus status = 2;

  // Number of segments updated (when completed)
  int32 segments_updated = 3;

  // Distinct speaker IDs found (when completed)
  repeated string speaker_ids = 4;

  // Error message if failed
  string error_message = 5;

  // Progress percentage (0.0-100.0)
  float progress_percent = 6;
}

message CancelDiarizationJobRequest {
  // Job ID to cancel
  string job_id = 1;
}

message CancelDiarizationJobResponse {
  // Whether cancellation succeeded
  bool success = 1;

  // Error message if failed
  string error_message = 2;

  // Final job status
  JobStatus status = 3;
}

message GetActiveDiarizationJobsRequest {
  // Empty - returns all active jobs for the current user/session
}

message GetActiveDiarizationJobsResponse {
  // List of active (QUEUED or RUNNING) diarization jobs
  repeated DiarizationJobStatus jobs = 1;
}

// =============================================================================
// Named Entity Extraction Messages (Sprint 4)
// =============================================================================

message ExtractEntitiesRequest {
  // Meeting ID to extract entities from
  string meeting_id = 1;

  // Force re-extraction even if entities exist
  bool force_refresh = 2;
}

message ExtractedEntity {
  // Unique entity identifier
  string id = 1;

  // Entity text as it appears in transcript
  string text = 2;

  // Category: person, company, product, technical, acronym, location, date, other
  string category = 3;

  // Segment IDs where this entity appears
  repeated int32 segment_ids = 4;

  // Extraction confidence (0.0-1.0)
  float confidence = 5;

  // User-confirmed (pinned) entity
  bool is_pinned = 6;
}

message ExtractEntitiesResponse {
  // Extracted entities
  repeated ExtractedEntity entities = 1;

  // Total entity count
  int32 total_count = 2;

  // True if returning cached results
  bool cached = 3;
}

// Entity mutation messages (Sprint 8)
message UpdateEntityRequest {
  // Meeting ID containing the entity
  string meeting_id = 1;

  // Entity ID to update
  string entity_id = 2;

  // New text value (optional, empty = no change)
  string text = 3;

  // New category value (optional, empty = no change)
  string category = 4;
}

message UpdateEntityResponse {
  // Updated entity
  ExtractedEntity entity = 1;
}

message DeleteEntityRequest {
  // Meeting ID containing the entity
  string meeting_id = 1;

  // Entity ID to delete
  string entity_id = 2;
}

message DeleteEntityResponse {
  // True if entity was deleted
  bool success = 1;
}

// =============================================================================
// Calendar Integration Messages (Sprint 5)
// =============================================================================

message CalendarEvent {
  // Calendar event identifier
  string id = 1;

  // Event title
  string title = 2;

  // Start time (Unix timestamp seconds)
  int64 start_time = 3;

  // End time (Unix timestamp seconds)
  int64 end_time = 4;

  // Attendee email addresses
  repeated string attendees = 5;

  // Event location
  string location = 6;

  // Event description
  string description = 7;

  // Meeting URL (Zoom, Meet, Teams, etc.)
  string meeting_url = 8;

  // Whether event is recurring
  bool is_recurring = 9;

  // Calendar provider: google, outlook
  string provider = 10;
}

message ListCalendarEventsRequest {
  // How far ahead to look in hours (default: 24)
  int32 hours_ahead = 1;

  // Maximum events to return (default: 10)
  int32 limit = 2;

  // Optional: specific provider name
  string provider = 3;
}

message ListCalendarEventsResponse {
  // Upcoming calendar events
  repeated CalendarEvent events = 1;

  // Total event count
  int32 total_count = 2;
}

message GetCalendarProvidersRequest {}

message CalendarProvider {
  // Provider name: google, outlook
  string name = 1;

  // Whether provider is authenticated
  bool is_authenticated = 2;

  // Display name: "Google Calendar", "Microsoft Outlook"
  string display_name = 3;
}

message GetCalendarProvidersResponse {
  // Available calendar providers
  repeated CalendarProvider providers = 1;
}

// =============================================================================
// OAuth Integration Messages (generic for calendar, email, PKM, etc.)
// =============================================================================

message InitiateOAuthRequest {
  // Provider to authenticate: google, outlook, notion, etc.
  string provider = 1;

  // Redirect URI for OAuth callback
  string redirect_uri = 2;

  // Integration type: calendar, email, pkm, custom
  string integration_type = 3;
}

message InitiateOAuthResponse {
  // Authorization URL to redirect user to
  string auth_url = 1;

  // CSRF state token for verification
  string state = 2;
}

message CompleteOAuthRequest {
  // Provider being authenticated
  string provider = 1;

  // Authorization code from OAuth callback
  string code = 2;

  // CSRF state token for verification
  string state = 3;
}

message CompleteOAuthResponse {
  // Whether authentication succeeded
  bool success = 1;

  // Error message if failed
  string error_message = 2;

  // Email of authenticated account
  string provider_email = 3;
}

message OAuthConnection {
  // Provider name: google, outlook, notion
  string provider = 1;

  // Connection status: disconnected, connected, error
  string status = 2;

  // Email of authenticated account
  string email = 3;

  // Token expiration timestamp (Unix epoch seconds)
  int64 expires_at = 4;

  // Error message if status is error
  string error_message = 5;

  // Integration type: calendar, email, pkm, custom
  string integration_type = 6;
}

message GetOAuthConnectionStatusRequest {
  // Provider to check: google, outlook, notion
  string provider = 1;

  // Optional integration type filter
  string integration_type = 2;
}

message GetOAuthConnectionStatusResponse {
  // Connection details
  OAuthConnection connection = 1;
}

message DisconnectOAuthRequest {
  // Provider to disconnect
  string provider = 1;

  // Optional integration type
  string integration_type = 2;
}

message DisconnectOAuthResponse {
  // Whether disconnection succeeded
  bool success = 1;

  // Error message if failed
  string error_message = 2;
}

// =============================================================================
// Webhook Management Messages (Sprint 6)
// =============================================================================

message RegisterWebhookRequest {
  // Workspace this webhook belongs to
  string workspace_id = 1;

  // Target URL for webhook delivery
  string url = 2;

  // Events to subscribe to: meeting.completed, summary.generated, recording.started, recording.stopped
  repeated string events = 3;

  // Human-readable webhook name
  string name = 4;

  // Optional HMAC signing secret
  string secret = 5;

  // Request timeout in milliseconds (default: 10000)
  int32 timeout_ms = 6;

  // Maximum retry attempts (default: 3)
  int32 max_retries = 7;
}

message WebhookConfigProto {
  // Unique webhook identifier
  string id = 1;

  // Workspace this webhook belongs to
  string workspace_id = 2;

  // Human-readable webhook name
  string name = 3;

  // Target URL for webhook delivery
  string url = 4;

  // Subscribed event types
  repeated string events = 5;

  // Whether webhook is enabled
  bool enabled = 6;

  // Request timeout in milliseconds
  int32 timeout_ms = 7;

  // Maximum retry attempts
  int32 max_retries = 8;

  // Creation timestamp (Unix epoch seconds)
  int64 created_at = 9;

  // Last update timestamp (Unix epoch seconds)
  int64 updated_at = 10;
}

message ListWebhooksRequest {
  // Filter to only enabled webhooks
  bool enabled_only = 1;
}

message ListWebhooksResponse {
  // Registered webhooks
  repeated WebhookConfigProto webhooks = 1;

  // Total webhook count
  int32 total_count = 2;
}

message UpdateWebhookRequest {
  // Webhook ID to update
  string webhook_id = 1;

  // Updated URL (optional)
  optional string url = 2;

  // Updated events (replaces existing)
  repeated string events = 3;

  // Updated name (optional)
  optional string name = 4;

  // Updated secret (optional)
  optional string secret = 5;

  // Updated enabled status (optional)
  optional bool enabled = 6;

  // Updated timeout in milliseconds (optional)
  optional int32 timeout_ms = 7;

  // Updated max retries (optional)
  optional int32 max_retries = 8;
}

message DeleteWebhookRequest {
  // Webhook ID to delete
  string webhook_id = 1;
}

message DeleteWebhookResponse {
  // Whether deletion succeeded
  bool success = 1;
}

message WebhookDeliveryProto {
  // Unique delivery identifier
  string id = 1;

  // Webhook ID this delivery belongs to
  string webhook_id = 2;

  // Event type that triggered this delivery
  string event_type = 3;

  // HTTP status code (0 if no response)
  int32 status_code = 4;

  // Error message if delivery failed
  string error_message = 5;

  // Number of delivery attempts
  int32 attempt_count = 6;

  // Request duration in milliseconds
  int32 duration_ms = 7;

  // Delivery timestamp (Unix epoch seconds)
  int64 delivered_at = 8;

  // Whether delivery succeeded
  bool succeeded = 9;
}

message GetWebhookDeliveriesRequest {
  // Webhook ID to get deliveries for
  string webhook_id = 1;

  // Maximum deliveries to return (default: 50, max: 500)
  int32 limit = 2;
}

message GetWebhookDeliveriesResponse {
  // Recent webhook deliveries
  repeated WebhookDeliveryProto deliveries = 1;

  // Total delivery count
  int32 total_count = 2;
}

// =============================================================================
// Cloud Consent Messages (Sprint 7)
// =============================================================================

message GrantCloudConsentRequest {}
message GrantCloudConsentResponse {}

message RevokeCloudConsentRequest {}
message RevokeCloudConsentResponse {}

message GetCloudConsentStatusRequest {}
message GetCloudConsentStatusResponse {
  // Whether cloud consent is currently granted
  bool consent_granted = 1;
}

// =============================================================================
// User Preferences Sync Messages (Sprint 14)
// =============================================================================

message GetPreferencesRequest {
  // Optional: filter to specific keys (empty = all)
  repeated string keys = 1;
}

message GetPreferencesResponse {
  // All preference key-value pairs as JSON strings
  // Key: preference key, Value: JSON-encoded value
  map<string, string> preferences = 1;

  // Server-side last update timestamp (Unix epoch seconds)
  double updated_at = 2;

  // ETag for optimistic concurrency control
  string etag = 3;
}

message SetPreferencesRequest {
  // Preferences to update as JSON strings
  // Key: preference key, Value: JSON-encoded value
  map<string, string> preferences = 1;

  // Optional ETag for conflict detection (if-match)
  string if_match = 2;

  // Client-side last update timestamp for conflict resolution
  double client_updated_at = 3;

  // Merge mode: if true, only updates provided keys; if false, replaces all
  bool merge = 4;
}

message SetPreferencesResponse {
  // Whether the update succeeded
  bool success = 1;

  // Whether a conflict was detected (client data was stale)
  bool conflict = 2;

  // Server preferences after update (or current state if conflict)
  map<string, string> server_preferences = 3;

  // Server-side timestamp after update
  double server_updated_at = 4;

  // New ETag after update
  string etag = 5;

  // Conflict details if conflict = true
  string conflict_message = 6;
}

// =============================================================================
// Integration Sync Orchestration Messages (Sprint 9)
// =============================================================================

message StartIntegrationSyncRequest {
  // Integration ID to sync
  string integration_id = 1;
}

message StartIntegrationSyncResponse {
  // Unique sync run identifier
  string sync_run_id = 1;

  // Initial status (always "running")
  string status = 2;
}

message GetSyncStatusRequest {
  // Sync run ID to check
  string sync_run_id = 1;
}

message GetSyncStatusResponse {
  // Current status: "running", "success", "error"
  string status = 1;

  // Number of items synced (so far or total)
  int32 items_synced = 2;

  // Total items to sync (if known)
  int32 items_total = 3;

  // Error message if status is "error"
  string error_message = 4;

  // Duration in milliseconds (when completed)
  int64 duration_ms = 5;

  // When this sync run expires from cache (ISO 8601 timestamp)
  // (Sprint GAP-002: State Synchronization)
  optional string expires_at = 10;

  // Reason for NOT_FOUND: "expired" or "never_existed"
  // (Sprint GAP-002: State Synchronization)
  optional string not_found_reason = 11;
}

message ListSyncHistoryRequest {
  // Integration ID to list history for
  string integration_id = 1;

  // Maximum runs to return (default: 20, max: 100)
  int32 limit = 2;

  // Pagination offset
  int32 offset = 3;
}

message ListSyncHistoryResponse {
  // Sync runs (newest first)
  repeated SyncRunProto runs = 1;

  // Total count of sync runs
  int32 total_count = 2;
}

message SyncRunProto {
  // Unique sync run identifier
  string id = 1;

  // Integration ID
  string integration_id = 2;

  // Status: "running", "success", "error"
  string status = 3;

  // Number of items synced
  int32 items_synced = 4;

  // Error message if failed
  string error_message = 5;

  // Duration in milliseconds
  int64 duration_ms = 6;

  // Start timestamp (ISO 8601)
  string started_at = 7;

  // Completion timestamp (ISO 8601, empty if running)
  string completed_at = 8;
}

// =============================================================================
// Integration Cache Validation Messages (Sprint 18.1)
// =============================================================================

message GetUserIntegrationsRequest {
  // Empty - uses identity context for user/workspace filtering
}

message IntegrationInfo {
  // Unique integration identifier
  string id = 1;

  // Display name (e.g., "Google Calendar")
  string name = 2;

  // Integration type: "calendar", "pkm", "custom"
  string type = 3;

  // Connection status: "connected", "disconnected", "error", "pending"
  string status = 4;

  // Workspace ID that owns this integration
  string workspace_id = 5;
}

message GetUserIntegrationsResponse {
  // List of integrations for the current user/workspace
  repeated IntegrationInfo integrations = 1;
}

// =============================================================================
// Observability Messages (Sprint 9)
// =============================================================================

message GetRecentLogsRequest {
  // Maximum logs to return (default: 100, max: 1000)
  int32 limit = 1;

  // Filter by log level: debug, info, warning, error
  string level = 2;

  // Filter by source: app, api, sync, auth, system
  string source = 3;
}

message GetRecentLogsResponse {
  // Recent log entries
  repeated LogEntryProto logs = 1;
}

message LogEntryProto {
  // Timestamp (ISO 8601)
  string timestamp = 1;

  // Log level: debug, info, warning, error
  string level = 2;

  // Source component: app, api, sync, auth, system
  string source = 3;

  // Log message
  string message = 4;

  // Additional details (key-value pairs)
  map<string, string> details = 5;
}

message GetPerformanceMetricsRequest {
  // Number of historical data points (default: 60)
  int32 history_limit = 1;
}

message GetPerformanceMetricsResponse {
  // Current metrics
  PerformanceMetricsPoint current = 1;

  // Historical metrics (oldest to newest)
  repeated PerformanceMetricsPoint history = 2;
}

message PerformanceMetricsPoint {
  // Unix timestamp
  double timestamp = 1;

  // CPU usage percentage (0-100)
  double cpu_percent = 2;

  // Memory usage percentage (0-100)
  double memory_percent = 3;

  // Memory used in megabytes
  double memory_mb = 4;

  // Disk usage percentage (0-100)
  double disk_percent = 5;

  // Network bytes sent since last measurement
  int64 network_bytes_sent = 6;

  // Network bytes received since last measurement
  int64 network_bytes_recv = 7;

  // NoteFlow process memory in megabytes
  double process_memory_mb = 8;

  // Active network connections
  int32 active_connections = 9;
}

// =============================================================================
// OIDC Provider Management Messages (Sprint 17)
// =============================================================================

message ClaimMappingProto {
  // OIDC claim names mapped to user attributes
  string subject_claim = 1;
  string email_claim = 2;
  string email_verified_claim = 3;
  string name_claim = 4;
  string preferred_username_claim = 5;
  string groups_claim = 6;
  string picture_claim = 7;
  optional string first_name_claim = 8;
  optional string last_name_claim = 9;
  optional string phone_claim = 10;
}

message OidcDiscoveryProto {
  // Discovery endpoint information
  string issuer = 1;
  string authorization_endpoint = 2;
  string token_endpoint = 3;
  optional string userinfo_endpoint = 4;
  optional string jwks_uri = 5;
  optional string end_session_endpoint = 6;
  optional string revocation_endpoint = 7;
  repeated string scopes_supported = 8;
  repeated string claims_supported = 9;
  bool supports_pkce = 10;
}

message OidcProviderProto {
  // Provider configuration
  string id = 1;
  string workspace_id = 2;
  string name = 3;
  string preset = 4;
  string issuer_url = 5;
  string client_id = 6;
  bool enabled = 7;

  // Discovery configuration (populated from .well-known)
  optional OidcDiscoveryProto discovery = 8;

  // Claim mapping configuration
  ClaimMappingProto claim_mapping = 9;

  // OAuth scopes to request
  repeated string scopes = 10;

  // Access control
  bool require_email_verified = 11;
  repeated string allowed_groups = 12;

  // Timestamps
  int64 created_at = 13;
  int64 updated_at = 14;
  optional int64 discovery_refreshed_at = 15;

  // Validation warnings (only in responses)
  repeated string warnings = 16;
}

message RegisterOidcProviderRequest {
  // Workspace to register provider in
  string workspace_id = 1;

  // Display name for the provider
  string name = 2;

  // OIDC issuer URL (base URL for discovery)
  string issuer_url = 3;

  // OAuth client ID
  string client_id = 4;

  // Optional client secret (for confidential clients)
  optional string client_secret = 5;

  // Provider preset: authentik, authelia, keycloak, auth0, okta, azure_ad, custom
  string preset = 6;

  // Optional custom scopes (defaults to preset)
  repeated string scopes = 7;

  // Optional custom claim mapping (defaults to preset)
  optional ClaimMappingProto claim_mapping = 8;

  // Optional group-based access control
  repeated string allowed_groups = 9;

  // Whether to require verified email (default: true)
  optional bool require_email_verified = 10;

  // Whether to auto-discover endpoints (default: true)
  bool auto_discover = 11;
}

message ListOidcProvidersRequest {
  // Optional workspace filter
  optional string workspace_id = 1;

  // Filter to only enabled providers
  bool enabled_only = 2;
}

message ListOidcProvidersResponse {
  // Registered OIDC providers
  repeated OidcProviderProto providers = 1;

  // Total count
  int32 total_count = 2;
}

message GetOidcProviderRequest {
  // Provider ID to retrieve
  string provider_id = 1;
}

message UpdateOidcProviderRequest {
  // Provider ID to update
  string provider_id = 1;

  // Updated name (optional)
  optional string name = 2;

  // Updated scopes (replaces existing)
  repeated string scopes = 3;

  // Updated claim mapping (optional)
  optional ClaimMappingProto claim_mapping = 4;

  // Updated allowed groups (replaces existing)
  repeated string allowed_groups = 5;

  // Updated require_email_verified (optional)
  optional bool require_email_verified = 6;

  // Updated enabled status (optional)
  optional bool enabled = 7;
}

message DeleteOidcProviderRequest {
  // Provider ID to delete
  string provider_id = 1;
}

message DeleteOidcProviderResponse {
  // Whether deletion succeeded
  bool success = 1;
}

message RefreshOidcDiscoveryRequest {
  // Optional provider ID (if not set, refreshes all)
  optional string provider_id = 1;

  // Optional workspace filter (for refresh all)
  optional string workspace_id = 2;
}

message RefreshOidcDiscoveryResponse {
  // Results per provider: provider_id -> error message (empty if success)
  map<string, string> results = 1;

  // Count of successful refreshes
  int32 success_count = 2;

  // Count of failed refreshes
  int32 failure_count = 3;
}

message ListOidcPresetsRequest {
  // No parameters needed
}

message OidcPresetProto {
  // Preset identifier
  string preset = 1;

  // Display name
  string display_name = 2;

  // Description
  string description = 3;

  // Default scopes
  repeated string default_scopes = 4;

  // Documentation URL
  optional string documentation_url = 5;

  // Configuration notes
  optional string notes = 6;
}

message ListOidcPresetsResponse {
  // Available presets
  repeated OidcPresetProto presets = 1;
}

// =============================================================================
// Project Management Messages (Sprint 18)
// =============================================================================

// Project role within a project (access control)
enum ProjectRoleProto {
  PROJECT_ROLE_UNSPECIFIED = 0;
  PROJECT_ROLE_VIEWER = 1;   // Read meetings, artifacts, run Q&A
  PROJECT_ROLE_EDITOR = 2;   // + Create/edit meetings, upload artifacts
  PROJECT_ROLE_ADMIN = 3;    // + Manage members, settings, rules
}

// Export configuration for a project
message ExportRulesProto {
  // Default export format (markdown, html, pdf)
  optional ExportFormat default_format = 1;

  // Whether to include audio file in exports
  optional bool include_audio = 2;

  // Whether to include timestamps in transcript
  optional bool include_timestamps = 3;

  // ID of export template to use
  optional string template_id = 4;
}

// Trigger configuration for a project
message TriggerRulesProto {
  // Whether auto-start recording is enabled
  optional bool auto_start_enabled = 1;

  // Glob patterns for calendar event titles
  repeated string calendar_match_patterns = 2;

  // Glob patterns for application names
  repeated string app_match_patterns = 3;
}

// Project settings (inheritable from workspace)
message ProjectSettingsProto {
  // Export configuration
  optional ExportRulesProto export_rules = 1;

  // Trigger configuration
  optional TriggerRulesProto trigger_rules = 2;

  // Whether RAG Q&A is enabled for this project
  optional bool rag_enabled = 3;

  // Default summarization template ID
  optional string default_summarization_template = 4;
}

// Full project entity
message ProjectProto {
  // Unique project identifier
  string id = 1;

  // Parent workspace identifier
  string workspace_id = 2;

  // User-provided project name
  string name = 3;

  // URL-friendly identifier (unique per workspace)
  optional string slug = 4;

  // Optional project description
  optional string description = 5;

  // Whether this is the workspace's default project
  bool is_default = 6;

  // Whether the project is archived
  bool is_archived = 7;

  // Project-level settings
  optional ProjectSettingsProto settings = 8;

  // Creation timestamp (Unix epoch seconds)
  int64 created_at = 9;

  // Last modification timestamp (Unix epoch seconds)
  int64 updated_at = 10;

  // Archive timestamp (Unix epoch seconds, 0 if not archived)
  optional int64 archived_at = 11;
}

// Project membership entity
message ProjectMembershipProto {
  // Project identifier
  string project_id = 1;

  // User identifier
  string user_id = 2;

  // User's role in the project
  ProjectRoleProto role = 3;

  // When the user joined the project (Unix epoch seconds)
  int64 joined_at = 4;
}

// -----------------------------------------------------------------------------
// Project CRUD Request/Response Messages
// -----------------------------------------------------------------------------

message CreateProjectRequest {
  // Workspace to create project in
  string workspace_id = 1;

  // Project name
  string name = 2;

  // Optional URL-friendly slug (auto-generated from name if not provided)
  optional string slug = 3;

  // Optional project description
  optional string description = 4;

  // Optional project settings
  optional ProjectSettingsProto settings = 5;
}

message GetProjectRequest {
  // Project ID to retrieve
  string project_id = 1;
}

message GetProjectBySlugRequest {
  // Workspace ID
  string workspace_id = 1;

  // Project slug
  string slug = 2;
}

message ListProjectsRequest {
  // Workspace to list projects for
  string workspace_id = 1;

  // Whether to include archived projects (default: false)
  bool include_archived = 2;

  // Maximum projects to return (default: 50)
  int32 limit = 3;

  // Pagination offset
  int32 offset = 4;
}

message ListProjectsResponse {
  // Projects in the workspace
  repeated ProjectProto projects = 1;

  // Total count (for pagination)
  int32 total_count = 2;
}

message UpdateProjectRequest {
  // Project ID to update
  string project_id = 1;

  // Updated name (optional)
  optional string name = 2;

  // Updated slug (optional)
  optional string slug = 3;

  // Updated description (optional)
  optional string description = 4;

  // Updated settings (optional, replaces existing)
  optional ProjectSettingsProto settings = 5;
}

message ArchiveProjectRequest {
  // Project ID to archive
  string project_id = 1;
}

message RestoreProjectRequest {
  // Project ID to restore
  string project_id = 1;
}

message DeleteProjectRequest {
  // Project ID to delete
  string project_id = 1;
}

message DeleteProjectResponse {
  // Whether deletion succeeded
  bool success = 1;
}

// -----------------------------------------------------------------------------
// Active Project Request/Response Messages
// -----------------------------------------------------------------------------

message SetActiveProjectRequest {
  // Workspace scope
  string workspace_id = 1;

  // Project ID to set as active (empty to clear)
  string project_id = 2;
}

message SetActiveProjectResponse {}

message GetActiveProjectRequest {
  // Workspace scope
  string workspace_id = 1;
}

message GetActiveProjectResponse {
  // Active project ID (unset if workspace default is used)
  optional string project_id = 1;

  // Resolved project (default if none set)
  ProjectProto project = 2;
}

// -----------------------------------------------------------------------------
// Project Membership Request/Response Messages
// -----------------------------------------------------------------------------

message AddProjectMemberRequest {
  // Project ID
  string project_id = 1;

  // User ID to add
  string user_id = 2;

  // Role to assign
  ProjectRoleProto role = 3;
}

message UpdateProjectMemberRoleRequest {
  // Project ID
  string project_id = 1;

  // User ID
  string user_id = 2;

  // New role
  ProjectRoleProto role = 3;
}

message RemoveProjectMemberRequest {
  // Project ID
  string project_id = 1;

  // User ID to remove
  string user_id = 2;
}

message RemoveProjectMemberResponse {
  // Whether removal succeeded
  bool success = 1;
}

message ListProjectMembersRequest {
  // Project ID
  string project_id = 1;

  // Maximum members to return (default: 100)
  int32 limit = 2;

  // Pagination offset
  int32 offset = 3;
}

message ListProjectMembersResponse {
  // Project members
  repeated ProjectMembershipProto members = 1;

  // Total count
  int32 total_count = 2;
}
